Search code examples
javaopensslbouncycastlex509

X509 certificate signed with bouncy castle is not valid


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/[email protected]
        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/[email protected]
        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/[email protected]
        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/[email protected]
        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/[email protected]")

                .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());
    }

Solution

  • I have a similar example working

    Try replacing X500Name issuer = with the X500 name got from CA to avoid problems with characters

    X500Name issuer = X500Name.getInstance(cacert.getSubjectX500Principal().getEncoded());
    

    Also, I think you have need this part before PEM encoding

    CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
    signer = new JcaContentSignerBuilder("SHA1withRSA").build(cakey);
    generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(signer, cacert));
    generator.addCertificate(new X509CertificateHolder(certencoded));
    generator.addCertificate(new X509CertificateHolder(cacert.getEncoded()));
    CMSTypedData content = new CMSProcessableByteArray(certencoded);
    CMSSignedData signeddata = generator.generate(content, true);
    byte signedCertificate[] = signeddata.getEncoded();