I went quite a long way to make java sign a CSR and at last I was able to do this, but openssl tells that it's not valid. The same CSR signed with openssl passes verification step.
All is the same x509 version (1), no extensions, Subject, Issuer are the same.
I suspect the problem is with subject DN (email especially) or dates.
Verification:
openssl verify -verbose -CAfile src/test/resources/ca.cer.pem o.cer.pem
o.cer.pem: OK
openssl verify -verbose -CAfile src/test/resources/ca.cer.pem client.cer.pem
client.cer.pem: C = RU, ST = Moscow, L = Moscow, O = Hoofs, OU = IT, CN = Danee Yaitskov
error 20 at 0 depth lookup:unable to get local issuer certificate
File sizes are similar:
1229 Jul 28 12:45 client.cer.pem 1233 Jul 28 13:00 o.cer.pem
It complains like there is a missing certificate in the chain but, I don't see such information.
How to check what is the next parent certificate?
Info about good certificate:
openssl x509 -in o.cer.pem -text -noout
Certificate:
Data:
Version: 1 (0x0)
Serial Number: 1192228 (0x123124)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=RU, ST=Moscow, L=Moscow, O=Hoofs, OU=IT, CN=www.hoofs.com/emailAddress=admin@hoofs.com
Validity
Not Before: Jul 28 11:00:01 2016 GMT
Not After : Jul 28 11:00:01 2017 GMT
Subject: C=RU, ST=Moscow, L=Moscow, O=Hoofs, OU=IT, CN=Danee Yaitskov
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:c2:94:04:69:58:3c:90:a9:0e:7e:23:78:9a:7c:
30:09:f1:5b:cf:0f:3c:d9:63:48:fb:97:77:2a:67:
85:20:30:a0:d6:57:4d:0c:55:5b:53:97:7b:5c:2f:
f5:6d:49:84:7d:59:6b:eb:3d:9b:84:ac:2c:bc:56:
1f:24:d4:d3:6b:be:0c:53:c4:e6:57:85:1e:95:9e:
37:9d:58:e1:e3:d5:5f:17:99:6c:69:2a:7e:9a:a5:
f4:11:69:54:b5:eb:71:ea:5d:a5:9f:b2:38:b7:47:
33:42:87:b5:83:64:0b:8c:d1:3c:2b:a4:a8:fd:6a:
1e:5c:1e:eb:c3:c2:f7:c6:10:95:65:b9:f4:15:97:
2a:88:c6:22:53:f5:63:92:89:05:ce:91:af:ee:4f:
4e:bb:a8:03:3c:ed:5b:0f:35:45:45:c3:a1:6f:af:
aa:87:21:94:ba:4d:63:25:fa:eb:65:1e:e0:34:75:
90:04:d4:71:4f:54:ed:e9:52:a1:b8:52:45:3b:03:
9f:15:80:3f:e6:d8:0d:32:55:df:e0:ea:78:34:e0:
30:64:dd:7c:77:b4:03:ce:d1:0d:ac:24:a7:b4:08:
63:3d:1a:9e:54:b1:2e:b1:b0:1d:24:b2:a6:9b:8d:
dc:3f:bd:ae:59:72:01:07:f8:e9:e8:c8:73:78:5c:
0c:b1
Exponent: 65537 (0x10001)
Signature Algorithm: sha256WithRSAEncryption
a3:88:4c:84:5a:af:e3:35:6c:3d:a8:05:9b:7e:f5:a0:a3:b1:
79:de:31:db:1e:ca:ce:d9:69:aa:88:8f:fb:78:04:aa:3b:c4:
41:ed:13:77:3b:17:b5:62:9b:da:54:92:25:0e:46:71:a0:f1:
43:28:d4:81:3f:be:a6:ce:53:3f:03:70:13:55:44:5f:f2:a5:
ab:b0:d5:1f:84:70:84:f9:b5:74:cd:4a:f6:fc:bd:f8:71:bc:
42:66:e0:a4:ec:4a:b6:26:e9:f9:fa:5e:67:fe:73:07:10:7d:
e2:02:d7:a6:30:8e:20:fb:0c:f9:f6:3e:6e:80:87:6f:3b:30:
c3:07:3d:af:ee:f7:e2:cc:0f:7d:71:39:fc:30:1a:15:1c:1f:
7f:4a:7e:9d:80:a4:1a:8f:f5:d9:e9:0b:95:c9:3c:5c:88:6d:
a7:66:2b:dc:b0:03:6e:f2:c5:b2:7a:85:35:0b:d6:8f:53:79:
d7:13:28:3f:fb:2c:59:9c:69:df:8a:dd:96:f6:bd:b8:78:5e:
b7:84:c5:48:d2:cf:4f:e8:a4:a8:d7:f5:91:d2:8c:94:95:9f:
a5:b9:10:c2:87:4b:ee:fa:2d:1c:bb:8f:37:f6:56:20:1c:a5:
aa:e9:77:bf:c4:29:92:67:14:81:76:43:e9:47:dd:5b:7d:9e:
69:7a:73:ec
Info about bad certificate:
openssl x509 -in client.cer.pem -text -noout
Certificate:
Data:
Version: 1 (0x0)
Serial Number: 362342824 (0x1598e9a8)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=RU, ST=Moscow, L=Moscow, O=Hoofs, OU=IT, CN=www.hoofs.com/emailAddress=admin@hoofs.com
Validity
Not Before: Jul 28 10:45:12 2015 GMT
Not After : Jul 28 10:45:12 2026 GMT
Subject: C=RU, ST=Moscow, L=Moscow, O=Hoofs, OU=IT, CN=Danee Yaitskov
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:c2:94:04:69:58:3c:90:a9:0e:7e:23:78:9a:7c:
30:09:f1:5b:cf:0f:3c:d9:63:48:fb:97:77:2a:67:
85:20:30:a0:d6:57:4d:0c:55:5b:53:97:7b:5c:2f:
f5:6d:49:84:7d:59:6b:eb:3d:9b:84:ac:2c:bc:56:
1f:24:d4:d3:6b:be:0c:53:c4:e6:57:85:1e:95:9e:
37:9d:58:e1:e3:d5:5f:17:99:6c:69:2a:7e:9a:a5:
f4:11:69:54:b5:eb:71:ea:5d:a5:9f:b2:38:b7:47:
33:42:87:b5:83:64:0b:8c:d1:3c:2b:a4:a8:fd:6a:
1e:5c:1e:eb:c3:c2:f7:c6:10:95:65:b9:f4:15:97:
2a:88:c6:22:53:f5:63:92:89:05:ce:91:af:ee:4f:
4e:bb:a8:03:3c:ed:5b:0f:35:45:45:c3:a1:6f:af:
aa:87:21:94:ba:4d:63:25:fa:eb:65:1e:e0:34:75:
90:04:d4:71:4f:54:ed:e9:52:a1:b8:52:45:3b:03:
9f:15:80:3f:e6:d8:0d:32:55:df:e0:ea:78:34:e0:
30:64:dd:7c:77:b4:03:ce:d1:0d:ac:24:a7:b4:08:
63:3d:1a:9e:54:b1:2e:b1:b0:1d:24:b2:a6:9b:8d:
dc:3f:bd:ae:59:72:01:07:f8:e9:e8:c8:73:78:5c:
0c:b1
Exponent: 65537 (0x10001)
Signature Algorithm: sha256WithRSAEncryption
71:17:8f:bb:09:05:91:0e:47:ba:f8:53:28:e3:d3:e3:b2:94:
02:71:b1:d1:93:45:d7:a0:f2:be:1f:4d:a3:18:95:35:23:6a:
1c:1d:4b:5f:60:cf:1c:93:22:1a:1b:4d:6c:e3:14:bc:7f:25:
85:24:a5:00:fb:ed:36:23:ea:b2:51:6d:8a:f2:58:07:e9:5f:
89:7e:8c:59:d2:1d:7c:85:69:bf:97:3f:f4:8f:3d:b4:21:4e:
c3:ad:1a:bd:fa:22:03:85:a3:d2:9c:76:71:58:43:4e:3f:d8:
d2:ec:8e:17:d0:53:65:c1:b7:82:38:fc:73:53:a1:80:38:1d:
89:f6:e2:48:d8:ea:a6:f6:b4:46:95:2e:cb:36:b6:e5:c2:02:
3f:bc:b2:82:a8:2e:02:7b:56:8e:59:c4:ee:1e:a5:40:bf:38:
b9:28:e7:37:2c:95:ce:2d:0b:b1:45:43:9b:49:fe:ec:37:49:
bd:f6:1e:7a:d2:2e:5c:8d:bc:00:e6:aa:96:16:83:72:8d:71:
13:33:1c:8f:8c:c7:dd:e0:99:b3:98:ac:7d:52:83:00:34:0f:
35:7a:55:d0:05:57:6c:a4:e0:5e:6d:58:a9:eb:79:e2:ae:e0:
13:87:32:e4:78:eb:a7:31:64:bf:c4:13:6d:2d:85:a2:67:ec:
62:d8:98:cb
Info about CA certificate:
openssl x509 -in src/test/resources/ca.cer.pem -text -noout
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 15043747854009729194 (0xd0c620f7d0cb80aa)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=RU, ST=Moscow, L=Moscow, O=Hoofs, OU=IT, CN=www.hoofs.com/emailAddress=admin@hoofs.com
Validity
Not Before: Apr 7 08:18:18 2016 GMT
Not After : Apr 7 08:18:18 2017 GMT
Subject: C=RU, ST=Moscow, L=Moscow, O=Hoofs, OU=IT, CN=www.hoofs.com/emailAddress=admin@hoofs.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:d4:7a:59:42:12:40:fb:4f:02:09:af:cf:6e:a1:
56:1e:4a:1d:9b:8e:5c:4a:53:5b:63:34:f1:ac:5c:
4c:de:e8:2c:f0:6b:14:58:ec:64:a7:9f:1d:54:4a:
36:b7:11:4f:65:d6:bd:9b:9a:b5:b7:df:d7:41:e1:
f0:2e:8f:c8:88:d5:bc:56:ab:f5:cd:fc:f5:0c:0f:
25:a5:c9:78:cc:e3:74:86:3e:58:51:ce:18:d4:9c:
61:85:5f:de:08:2c:65:17:a2:ad:0e:05:63:92:58:
c4:76:ee:02:2c:68:41:4e:a9:8f:8f:2e:98:82:47:
39:eb:60:a2:5c:ee:0a:55:23:5e:d6:cd:d2:29:94:
0d:e0:cd:82:b0:af:83:61:93:22:99:b1:5c:f2:f8:
3b:71:30:5b:26:46:3e:15:d0:26:d7:70:ae:34:31:
35:a4:39:f7:dd:e4:99:4f:68:42:78:9a:90:70:4a:
8d:0f:08:2d:80:b2:2a:23:5e:55:b9:28:52:dd:ce:
15:bd:77:41:66:3f:1b:dc:9f:47:89:b3:e2:0d:f0:
25:5e:5e:47:d4:f9:e9:f6:fb:8e:08:7e:52:5f:bd:
bd:4d:2a:bf:ed:08:6a:7f:4c:32:21:c6:c0:6a:53:
84:f8:1d:37:47:0d:93:e7:90:90:2b:7c:03:db:7a:
40:fb
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
B7:2A:B2:C4:63:E8:E3:D5:7A:A7:30:4D:5B:E8:C3:2D:5A:72:BC:DE
X509v3 Authority Key Identifier:
keyid:B7:2A:B2:C4:63:E8:E3:D5:7A:A7:30:4D:5B:E8:C3:2D:5A:72:BC:DE
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
3a:74:2d:13:96:06:26:35:7d:cc:44:28:d2:9a:47:e4:08:9e:
c5:ef:91:b7:6f:66:e1:bd:96:92:28:b1:13:3b:f3:2d:57:4f:
85:c6:e8:7d:53:3f:ba:c3:78:80:da:4e:ba:a8:85:e2:22:b2:
19:5d:62:2a:7d:ed:48:ab:b4:22:7a:9a:f1:83:b8:04:0d:87:
dc:9e:61:fd:e7:e8:2e:c4:12:6e:b9:6b:b0:14:79:35:86:91:
e8:f0:de:00:b8:bd:7e:d0:d1:4c:33:db:c3:f0:05:b7:06:2a:
21:33:4b:82:e5:74:7d:65:d8:ce:81:7f:f3:6f:03:c8:5c:aa:
de:fd:24:46:aa:20:95:d6:bc:91:ee:f9:ec:d0:c8:e5:9e:8d:
1c:44:1b:6c:05:4a:a9:bd:19:86:61:f0:5f:75:12:46:28:80:
29:79:c2:1e:e8:1c:e7:48:38:7d:7a:40:c8:ca:c9:4d:b1:a9:
5c:53:90:33:4f:13:70:93:97:73:0a:84:ac:31:0e:8e:a6:cb:
c4:53:b7:c8:0c:9e:15:22:11:0a:b8:db:5a:95:6a:d2:26:49:
e4:4e:3a:c0:9f:47:95:29:db:84:bc:6a:da:25:ba:96:05:33:
d6:1c:23:5a:76:36:75:4f:ce:19:f8:ff:27:5e:e0:4f:c3:77:
2a:63:63:6e
Command for signing CSR with openssl
openssl x509 -req -days ${DAYS:-365} -in src/test/resources/client.csr -CA src/test/resources/ca.cer.pem -CAkey src/test/resources/ca.key.pem -out o.cer.pem -CAserial serial
Java code for signing CSR (bouncycastle 1.54 on java8):
@Test
@SneakyThrows
public void sign() {
Security.addProvider(new BouncyCastleProvider());
X509Certificate caCert = loadCert("/ca.cer.pem");
PrivateKey caKey = readPrivateKey("/ca.key.pem");
try (InputStream csr = getClass().getResourceAsStream("/client.csr")) {
String cert = signCSR(new InputStreamReader(csr), caKey, caCert);
assertNotNull(cert);
Files.write(Paths.get("client.cer.pem"), cert.getBytes());
}
}
@SneakyThrows
public byte[] readFile(String path) {
try (InputStream keyStream = getClass().getResourceAsStream(path)) {
return IOUtils.toByteArray(keyStream);
}
}
public static final char[] PASSWORD = "12312312".toCharArray();
@SneakyThrows
public PrivateKey readPrivateKey(String privateKeyPath) {
PEMParser keyReader = new PEMParser(new InputStreamReader(getClass()
.getResourceAsStream(privateKeyPath)));
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
converter.setProvider(new BouncyCastleProvider());
PKCS8EncryptedPrivateKeyInfo keyPair = (PKCS8EncryptedPrivateKeyInfo) keyReader.readObject();
final JceOpenSSLPKCS8DecryptorProviderBuilder jceOpenSSLPKCS8DecryptorProviderBuilder = new JceOpenSSLPKCS8DecryptorProviderBuilder();
jceOpenSSLPKCS8DecryptorProviderBuilder.setProvider("BC");
InputDecryptorProvider pkcs8Prov = jceOpenSSLPKCS8DecryptorProviderBuilder.build(PASSWORD);
PrivateKeyInfo pk = keyPair.decryptPrivateKeyInfo(pkcs8Prov);
return converter.getPrivateKey(pk);
}
@SneakyThrows
public X509Certificate loadCert(String path) {
try (InputStream caStream = getClass().getResourceAsStream(path)) {
X509CertificateHolder holder = (X509CertificateHolder) new PEMParser(
new InputStreamReader(caStream))
.readObject();
CertificateFactory cf = CertificateFactory.getInstance("X509",
new BouncyCastleProvider());
return (X509Certificate) cf.generateCertificate(
new ByteArrayInputStream(holder.getEncoded()));
}
}
public static String signCSR(Reader pemcsr, PrivateKey cakey, X509Certificate cacert) throws Exception {
PEMParser reader = new PEMParser(pemcsr);
PKCS10CertificationRequest csr = (PKCS10CertificationRequest) reader.readObject();
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256withRSA");
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
X500Name issuer = new X500NameBuilder( BCStrictStyle.INSTANCE )
.addRDN(BCStrictStyle.C, "RU")
.addRDN( BCStyle.ST, "Moscow")
.addRDN( BCStyle.L, "Moscow" )
.addRDN(BCStyle.O, "Hoofs")
.addRDN(BCStyle.OU, "IT")
.addRDN(BCStyle.CN, "www.hoofs.com/emailAddress=admin@hoofs.com")
.build();
BigInteger serial = new BigInteger(32, new SecureRandom());
Date from = new DateTime().minusYears(1).toDate();
Date to = new DateTime().plusYears(10).toDate();
X509v1CertificateBuilder certBuilder = new X509v1CertificateBuilder(
issuer, serial,
from, to, csr.getSubject(), csr.getSubjectPublicKeyInfo());
ContentSigner signer = new BcRSAContentSignerBuilder(sigAlgId, digAlgId)
.build(PrivateKeyFactory.createKey(cakey.getEncoded()));
X509CertificateHolder holder = certBuilder.build(signer);
byte[] certencoded = holder.toASN1Structure().getEncoded();
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write("-----BEGIN CERTIFICATE-----\n".getBytes());
out.write(java.util.Base64.getMimeEncoder(64, "\n".getBytes()).encode(certencoded));
out.write("\n-----END CERTIFICATE-----\n".getBytes());
out.close();
return new String(out.toByteArray());
}