Anonymous
Как сгенерировать CSR вручную в Java
Сообщение
Anonymous » 15 дек 2024, 09:09
Я пытаюсь сгенерировать запрос на подпись сертификата (CSR) на Java без использования сторонних библиотек, таких как BouncyCastle. Ниже приведен код, который я использую для создания CSR:
Код: Выделить всё
public void createCSR(@NonNull KeyPair keyPair, @NonNull CSRCallback csrCallback) {
try {
byte[] subject = encodeSubject("CN=MyClient, O=MyOrganization, L=City, C=US");
byte[] publicKey = encodePublicKey(keyPair.getPublic());
byte[] algorithmIdentifier = encodeAlgorithmIdentifier();
byte[] dataToSign = concatenateData(subject, publicKey, algorithmIdentifier);
byte[] signature = signData(dataToSign, keyPair.getPrivate());
byte[] csrBytes = createCSR(subject, publicKey, algorithmIdentifier, signature);
String pemCSR = toPEM(csrBytes);
csrCallback.onCSRGenerated(pemCSR);
} catch (Exception e) {
csrCallback.onCSRGenerationFailed(e);
}
}
byte[] encodeSubject(String subjectDN) throws Exception {
// Example subject: "CN=MyClient, O=MyOrganization, L=City, C=US"
String[] rdnPairs = subjectDN.split(",\\s*");
ByteArrayOutputStream subjectStream = new ByteArrayOutputStream();
for (String rdn : rdnPairs) {
String[] keyValue = rdn.split("=");
String rdnType = keyValue[0];
String rdnValue = keyValue[1];
ByteArrayOutputStream rdnStream = new ByteArrayOutputStream();
rdnStream.write(0x0C); // UTF8String tag
rdnStream.write(rdnValue.length());
rdnStream.write(rdnValue.getBytes(StandardCharsets.UTF_8));
ByteArrayOutputStream rdnPairStream = new ByteArrayOutputStream();
rdnPairStream.write(0x31); // SET tag
rdnPairStream.write(rdnStream.size());
rdnPairStream.write(rdnStream.toByteArray());
subjectStream.write(0x30); // SEQUENCE tag
subjectStream.write(rdnPairStream.size());
subjectStream.write(rdnPairStream.toByteArray());
}
ByteArrayOutputStream finalSubjectStream = new ByteArrayOutputStream();
finalSubjectStream.write(0x30); // SEQUENCE tag
finalSubjectStream.write(subjectStream.size());
finalSubjectStream.write(subjectStream.toByteArray());
byte[] res = new byte[]{
(byte) 0x30, (byte) 0x56, (byte) 0x31, (byte) 0x0B, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
(byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31,
(byte) 0x0D, (byte) 0x30, (byte) 0x0B, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07,
(byte) 0x13, (byte) 0x04, (byte) 0x43, (byte) 0x69, (byte) 0x74, (byte) 0x79, (byte) 0x31, (byte) 0x18,
(byte) 0x30, (byte) 0x16, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0A, (byte) 0x13,
(byte) 0x0F, (byte) 0x4D, (byte) 0x79, (byte) 0x20, (byte) 0x4F, (byte) 0x72, (byte) 0x67, (byte) 0x61,
(byte) 0x6E, (byte) 0x69, (byte) 0x7A, (byte) 0x61, (byte) 0x74, (byte) 0x69, (byte) 0x6F, (byte) 0x6E,
(byte) 0x31, (byte) 0x1E, (byte) 0x30, (byte) 0x1C, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
(byte) 0x03, (byte) 0x13, (byte) 0x15, (byte) 0x4D, (byte) 0x79, (byte) 0x20, (byte) 0x43, (byte) 0x6C,
(byte) 0x69, (byte) 0x65, (byte) 0x6E, (byte) 0x74, (byte) 0x20, (byte) 0x43, (byte) 0x65, (byte) 0x72,
(byte) 0x74, (byte) 0x69, (byte) 0x66, (byte) 0x69, (byte) 0x63, (byte) 0x61, (byte) 0x74, (byte) 0x65
};
return res;
}
byte[] encodePublicKey(PublicKey publicKey) {
return publicKey.getEncoded(); // Returns the public key in X.509 DER format
}
byte[] encodeAlgorithmIdentifier() {
return new byte[]{
0x30, 0x0D, 0x06, 0x09,
0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01, 0x0B,
0x05, 0x00
};
}
byte[] concatenateData(byte[] subject, byte[] publicKey, byte[] algorithmIdentifier) throws Exception {
ByteArrayOutputStream data = new ByteArrayOutputStream();
data.write(subject);
data.write(publicKey);
data.write(algorithmIdentifier);
return data.toByteArray();
}
byte[] signData(byte[] data, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA/PSS");
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}
void addSequence(ByteArrayOutputStream stream, byte[] data) throws IOException {
stream.write(0x30);
encodeLength(stream, data.length);
stream.write(data);
};
void addByteStringASN(ByteArrayOutputStream stream, byte[] data) throws IOException {
stream.write(0x03);
encodeLength(stream, data.length);
stream.write(data);
};
void addIntASN(ByteArrayOutputStream stream, byte[] data) throws IOException {
stream.write(0x02);
encodeLength(stream, data.length);
stream.write(data);
};
public static byte[] mergeArrays(byte[]... arrays) {
return Stream.of(arrays)
.flatMapToInt(array -> java.util.stream.IntStream.range(0, array.length).map(i -> array[i]))
.collect(ByteArrayOutputStream::new,
ByteArrayOutputStream::write,
(a, b) -> {}).toByteArray();
}
byte[] createCSR(byte[] subject, byte[] publicKey, byte[] algorithmIdentifier, byte[] signature) throws Exception {
ByteArrayOutputStream certificateRequest = new ByteArrayOutputStream();
ByteArrayOutputStream certificateRequestInfo = new ByteArrayOutputStream();
byte[] version = new byte[] {0x02, 0x01, 0x00};
ByteArrayOutputStream pkInfo = new ByteArrayOutputStream();
ByteArrayOutputStream subjectPublicKey = new ByteArrayOutputStream();
addByteStringASN(subjectPublicKey, publicKey);
addSequence(pkInfo, mergeArrays(algorithmIdentifier, subjectPublicKey.toByteArray()));
byte[] attribute = new byte[] {(byte) 0xA0, 0x00};
addSequence(certificateRequestInfo, mergeArrays(version, subject, pkInfo.toByteArray(), attribute));
byte[] signatureAlgorithm = new byte[] {0x30, 0x0D, 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00};
ByteArrayOutputStream signatureBitString = new ByteArrayOutputStream();
addByteStringASN(signatureBitString, signature);
addSequence(certificateRequest, mergeArrays(certificateRequestInfo.toByteArray(), signatureAlgorithm, signatureBitString.toByteArray()));
return certificateRequest.toByteArray();
}
private void encodeLength(ByteArrayOutputStream stream, int length) {
if (length < 128) {
stream.write(length); // Short form
} else {
// Long form
int numBytes = 0;
int tempLength = length;
while (tempLength > 0) {
tempLength >>= 8;
numBytes++;
}
stream.write(0x80 | numBytes); // Long form indicator
for (int i = numBytes - 1; i >= 0; i--) {
stream.write((length >> (8 * i)) & 0xFF);
}
}
}
String toPEM(byte[] csrBytes) {
String base64CSR = Base64.getMimeEncoder(64, "\n".getBytes()).encodeToString(csrBytes);
return "-----BEGIN CERTIFICATE REQUEST-----\n" + base64CSR + "\n-----END CERTIFICATE REQUEST-----";
}
Сгенерированный сертификат
Код: Выделить всё
-----BEGIN CERTIFICATE REQUEST-----
MIICsTCCAZoCAQAwVjELMAkGA1UEBhMCVVMxDTALBgNVBAcTBENpdHkxGDAWBgNV
BAoTD015IE9yZ2FuaXphdGlvbjEeMBwGA1UEAxMVTXkgQ2xpZW50IENlcnRpZmlj
YXRlMIIBOTANBgkqhkiG9w0BAQsFAAOCASYwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQCl0DKXmjTTGCSQzUkHue+XJtBVza2+DQu82yAizQFMhDG1zdZw
crCOl46gvkcGYnOjBfaVq6VlLahTMu9FGLnEq+FOnL+n+EVKSIRCw/RCAB/R/eYw
SE4htgoqFK+zWFwjq3DvZkF7CmuZaGSilSh1ifcesANBg0qh6PwGLs2i4WuOy67P
52Oeg986gMsCB4UA7tUSHALbEPZs7UajsGB16yU0s6b+8shECXsbb/yrA4czfkgc
BFYeTW6h+nUIgGnXQ6zdeEHqIxdLGagT5iRndypnX0YVH3Wue5nbG7Mal4Yg8v3N
6wzMr54FhKcHwln5s033Ucbu1QnNJkjXjbolAgMBAAGgADANBgkqhkiG9w0BAQsF
AAOCAQBZ7x8XmDk19tjHAHA4edQYxhjtEGYDnnymEsh6zBVMP+Gkzd5HeEgb23Fl
oWeQXFm2pw9p6SXqQkYq8oB3LaaSPnUOKIR84ZQl3dTrn+vdEb2iJ2HkCmPA5BCp
V1+qULItQvLEYBpR8N9dmGW6/m/ErwO32y3WLxAbRKy7mDrUKAAl6gAicU6y5QQ8
isDRcFqCCuOPfKMNnuC1ViAmM4DSDj6UePH8R0uogPshIlpXaY2K+boaK6bgGNfa
+mReFP4LMIqiTLrABirCmr6lygUWinsBOzgtAD9OeWWzhG7/5l2mB0jlzc2y+e8V
iL0llf7wwXEHPMcOPid1lFvMha8G
-----END CERTIFICATE REQUEST-----
когда я запускаю: openssl req -in request.csr -noout -text
то получаю ошибку ниже:
Код: Выделить всё
C08F6E51F87F0000:error:068000A8:asn1 encoding routines:asn1_check_tlen:wrong tag:crypto/asn1/tasn_dec.c:1194:
C08F6E51F87F0000:error:0688010A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error:crypto/asn1/tasn_dec.c:349:Type=X509_REQ
error: unable to load X509 request from file 'request.csr'
Мой код генерации пары ключей —
Код: Выделить всё
public static KeyPair generateRSAKeyPairWithAuth(String alias) throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
keyGen.initialize(new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
.setAlgorithmParameterSpec(new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4))
.setUserAuthenticationRequired(true)
.setUserAuthenticationValidityDurationSeconds(120)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1, KeyProperties.SIGNATURE_PADDING_RSA_PSS)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA384, KeyProperties.DIGEST_SHA512).build());
return keyGen.generateKeyPair();
}
ПРИМЕЧАНИЕ. Я не хочу использовать для этого BouncyCastle или SpongyCastle.
Подробнее здесь:
https://stackoverflow.com/questions/792 ... ly-in-java
1734242985
Anonymous
Я пытаюсь сгенерировать запрос на подпись сертификата (CSR) на Java без использования сторонних библиотек, таких как BouncyCastle. Ниже приведен код, который я использую для создания CSR: [code]public void createCSR(@NonNull KeyPair keyPair, @NonNull CSRCallback csrCallback) { try { byte[] subject = encodeSubject("CN=MyClient, O=MyOrganization, L=City, C=US"); byte[] publicKey = encodePublicKey(keyPair.getPublic()); byte[] algorithmIdentifier = encodeAlgorithmIdentifier(); byte[] dataToSign = concatenateData(subject, publicKey, algorithmIdentifier); byte[] signature = signData(dataToSign, keyPair.getPrivate()); byte[] csrBytes = createCSR(subject, publicKey, algorithmIdentifier, signature); String pemCSR = toPEM(csrBytes); csrCallback.onCSRGenerated(pemCSR); } catch (Exception e) { csrCallback.onCSRGenerationFailed(e); } } byte[] encodeSubject(String subjectDN) throws Exception { // Example subject: "CN=MyClient, O=MyOrganization, L=City, C=US" String[] rdnPairs = subjectDN.split(",\\s*"); ByteArrayOutputStream subjectStream = new ByteArrayOutputStream(); for (String rdn : rdnPairs) { String[] keyValue = rdn.split("="); String rdnType = keyValue[0]; String rdnValue = keyValue[1]; ByteArrayOutputStream rdnStream = new ByteArrayOutputStream(); rdnStream.write(0x0C); // UTF8String tag rdnStream.write(rdnValue.length()); rdnStream.write(rdnValue.getBytes(StandardCharsets.UTF_8)); ByteArrayOutputStream rdnPairStream = new ByteArrayOutputStream(); rdnPairStream.write(0x31); // SET tag rdnPairStream.write(rdnStream.size()); rdnPairStream.write(rdnStream.toByteArray()); subjectStream.write(0x30); // SEQUENCE tag subjectStream.write(rdnPairStream.size()); subjectStream.write(rdnPairStream.toByteArray()); } ByteArrayOutputStream finalSubjectStream = new ByteArrayOutputStream(); finalSubjectStream.write(0x30); // SEQUENCE tag finalSubjectStream.write(subjectStream.size()); finalSubjectStream.write(subjectStream.toByteArray()); byte[] res = new byte[]{ (byte) 0x30, (byte) 0x56, (byte) 0x31, (byte) 0x0B, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x0D, (byte) 0x30, (byte) 0x0B, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x04, (byte) 0x43, (byte) 0x69, (byte) 0x74, (byte) 0x79, (byte) 0x31, (byte) 0x18, (byte) 0x30, (byte) 0x16, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0A, (byte) 0x13, (byte) 0x0F, (byte) 0x4D, (byte) 0x79, (byte) 0x20, (byte) 0x4F, (byte) 0x72, (byte) 0x67, (byte) 0x61, (byte) 0x6E, (byte) 0x69, (byte) 0x7A, (byte) 0x61, (byte) 0x74, (byte) 0x69, (byte) 0x6F, (byte) 0x6E, (byte) 0x31, (byte) 0x1E, (byte) 0x30, (byte) 0x1C, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x13, (byte) 0x15, (byte) 0x4D, (byte) 0x79, (byte) 0x20, (byte) 0x43, (byte) 0x6C, (byte) 0x69, (byte) 0x65, (byte) 0x6E, (byte) 0x74, (byte) 0x20, (byte) 0x43, (byte) 0x65, (byte) 0x72, (byte) 0x74, (byte) 0x69, (byte) 0x66, (byte) 0x69, (byte) 0x63, (byte) 0x61, (byte) 0x74, (byte) 0x65 }; return res; } byte[] encodePublicKey(PublicKey publicKey) { return publicKey.getEncoded(); // Returns the public key in X.509 DER format } byte[] encodeAlgorithmIdentifier() { return new byte[]{ 0x30, 0x0D, 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00 }; } byte[] concatenateData(byte[] subject, byte[] publicKey, byte[] algorithmIdentifier) throws Exception { ByteArrayOutputStream data = new ByteArrayOutputStream(); data.write(subject); data.write(publicKey); data.write(algorithmIdentifier); return data.toByteArray(); } byte[] signData(byte[] data, PrivateKey privateKey) throws Exception { Signature signature = Signature.getInstance("SHA256withRSA/PSS"); signature.initSign(privateKey); signature.update(data); return signature.sign(); } void addSequence(ByteArrayOutputStream stream, byte[] data) throws IOException { stream.write(0x30); encodeLength(stream, data.length); stream.write(data); }; void addByteStringASN(ByteArrayOutputStream stream, byte[] data) throws IOException { stream.write(0x03); encodeLength(stream, data.length); stream.write(data); }; void addIntASN(ByteArrayOutputStream stream, byte[] data) throws IOException { stream.write(0x02); encodeLength(stream, data.length); stream.write(data); }; public static byte[] mergeArrays(byte[]... arrays) { return Stream.of(arrays) .flatMapToInt(array -> java.util.stream.IntStream.range(0, array.length).map(i -> array[i])) .collect(ByteArrayOutputStream::new, ByteArrayOutputStream::write, (a, b) -> {}).toByteArray(); } byte[] createCSR(byte[] subject, byte[] publicKey, byte[] algorithmIdentifier, byte[] signature) throws Exception { ByteArrayOutputStream certificateRequest = new ByteArrayOutputStream(); ByteArrayOutputStream certificateRequestInfo = new ByteArrayOutputStream(); byte[] version = new byte[] {0x02, 0x01, 0x00}; ByteArrayOutputStream pkInfo = new ByteArrayOutputStream(); ByteArrayOutputStream subjectPublicKey = new ByteArrayOutputStream(); addByteStringASN(subjectPublicKey, publicKey); addSequence(pkInfo, mergeArrays(algorithmIdentifier, subjectPublicKey.toByteArray())); byte[] attribute = new byte[] {(byte) 0xA0, 0x00}; addSequence(certificateRequestInfo, mergeArrays(version, subject, pkInfo.toByteArray(), attribute)); byte[] signatureAlgorithm = new byte[] {0x30, 0x0D, 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00}; ByteArrayOutputStream signatureBitString = new ByteArrayOutputStream(); addByteStringASN(signatureBitString, signature); addSequence(certificateRequest, mergeArrays(certificateRequestInfo.toByteArray(), signatureAlgorithm, signatureBitString.toByteArray())); return certificateRequest.toByteArray(); } private void encodeLength(ByteArrayOutputStream stream, int length) { if (length < 128) { stream.write(length); // Short form } else { // Long form int numBytes = 0; int tempLength = length; while (tempLength > 0) { tempLength >>= 8; numBytes++; } stream.write(0x80 | numBytes); // Long form indicator for (int i = numBytes - 1; i >= 0; i--) { stream.write((length >> (8 * i)) & 0xFF); } } } String toPEM(byte[] csrBytes) { String base64CSR = Base64.getMimeEncoder(64, "\n".getBytes()).encodeToString(csrBytes); return "-----BEGIN CERTIFICATE REQUEST-----\n" + base64CSR + "\n-----END CERTIFICATE REQUEST-----"; } [/code] [b]Сгенерированный сертификат[/b] [code]-----BEGIN CERTIFICATE REQUEST----- MIICsTCCAZoCAQAwVjELMAkGA1UEBhMCVVMxDTALBgNVBAcTBENpdHkxGDAWBgNV BAoTD015IE9yZ2FuaXphdGlvbjEeMBwGA1UEAxMVTXkgQ2xpZW50IENlcnRpZmlj YXRlMIIBOTANBgkqhkiG9w0BAQsFAAOCASYwggEiMA0GCSqGSIb3DQEBAQUAA4IB DwAwggEKAoIBAQCl0DKXmjTTGCSQzUkHue+XJtBVza2+DQu82yAizQFMhDG1zdZw crCOl46gvkcGYnOjBfaVq6VlLahTMu9FGLnEq+FOnL+n+EVKSIRCw/RCAB/R/eYw SE4htgoqFK+zWFwjq3DvZkF7CmuZaGSilSh1ifcesANBg0qh6PwGLs2i4WuOy67P 52Oeg986gMsCB4UA7tUSHALbEPZs7UajsGB16yU0s6b+8shECXsbb/yrA4czfkgc BFYeTW6h+nUIgGnXQ6zdeEHqIxdLGagT5iRndypnX0YVH3Wue5nbG7Mal4Yg8v3N 6wzMr54FhKcHwln5s033Ucbu1QnNJkjXjbolAgMBAAGgADANBgkqhkiG9w0BAQsF AAOCAQBZ7x8XmDk19tjHAHA4edQYxhjtEGYDnnymEsh6zBVMP+Gkzd5HeEgb23Fl oWeQXFm2pw9p6SXqQkYq8oB3LaaSPnUOKIR84ZQl3dTrn+vdEb2iJ2HkCmPA5BCp V1+qULItQvLEYBpR8N9dmGW6/m/ErwO32y3WLxAbRKy7mDrUKAAl6gAicU6y5QQ8 isDRcFqCCuOPfKMNnuC1ViAmM4DSDj6UePH8R0uogPshIlpXaY2K+boaK6bgGNfa +mReFP4LMIqiTLrABirCmr6lygUWinsBOzgtAD9OeWWzhG7/5l2mB0jlzc2y+e8V iL0llf7wwXEHPMcOPid1lFvMha8G -----END CERTIFICATE REQUEST----- [/code] когда я запускаю: openssl req -in request.csr -noout -text то получаю ошибку ниже: [code]C08F6E51F87F0000:error:068000A8:asn1 encoding routines:asn1_check_tlen:wrong tag:crypto/asn1/tasn_dec.c:1194: C08F6E51F87F0000:error:0688010A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error:crypto/asn1/tasn_dec.c:349:Type=X509_REQ error: unable to load X509 request from file 'request.csr' [/code] Мой код генерации пары ключей — [code]public static KeyPair generateRSAKeyPairWithAuth(String alias) throws Exception { KeyPairGenerator keyGen = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore"); keyGen.initialize(new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) .setAlgorithmParameterSpec(new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4)) .setUserAuthenticationRequired(true) .setUserAuthenticationValidityDurationSeconds(120) .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1, KeyProperties.SIGNATURE_PADDING_RSA_PSS) .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA384, KeyProperties.DIGEST_SHA512).build()); return keyGen.generateKeyPair(); } [/code] [b]ПРИМЕЧАНИЕ. Я не хочу использовать для этого BouncyCastle или SpongyCastle.[/b] Подробнее здесь: [url]https://stackoverflow.com/questions/79258212/how-to-generate-csr-manually-in-java[/url]