Signing CSR with Java does not validate using OpenSSL

1k views Asked by At

I'm signing a CSR to an intermediate certificate, both of which were generated using OpenSSL. The signing code uses Java Bouncy Castle APIs, and the code successfully generates a cert. When inspecting the cert, everything appears fine. The issuer and other data show up correctly in the dump.

However, when executing an openssl verify command, it fails with:
error 20 at 0 depth lookup:unable to get local issuer certificate

Signing an OpenSSL generated CSR to this same intermediate certificate verifies correctly.

The verify is successful when checked with the Java code:
cert.verify(cacert.getPublicKey())

-----BEGIN CERTIFICATE----- MIIDEjCCArmgAwIBAgIFAJl/JBYwCgYIKoZIzj0EAwIwdTETMBEGA1UEDRMKMTUw MzkxNzkxNDEVMBMGA1UEAwwMcGh5enpsaXRlLTAxMQ4wDAYDVQQLDAVQaHl6ejEd MBsGA1UECgwUTWltb3NhIE5ldHdvcmtzLCBJbmMxCzAJBgNVBAgMAkNBMQswCQYD VQQGEwJVUzAeFw0xNzA4MzAwMDA4MzhaFw0yNzAzMzEwMDA4MzhaMIG8Me0wGwYD VQQKExRNaW1vc2EgTmV0d39ya3MsIEluYzEjMCEGA2UEAxMaMDEyMzcyOEI4MDAw MTAwMTA3QkM0RkREQUMxEzARBgNVBAUTCjMwNzE1NDE4MjIxGjAYBgNVBCoTETIw OmI1OmM2OjBmOmQ7OjNiMRowGAYDVQQEExEyMDpiNTpjNjowZjpkNjozETEaMBgG A1UEKRMRMjA5YjU6YzY6MGY6ZDY6M2MxDTALBgNVBAwTBDMzMzkwWTATBgcqhkjO PQIBBggqhkjOPQMBBwNCAARUdhMYLbb94GlWSh8b01lVfKL7+6sCw7hZdiMy9JIF YBnTjLyGm1HjoRKl6ItuEzjdNFXMnFlMMuCbUTsij4L2o4HtMIHqMAwGA1UdEwQF MAMBAf8wHQYDVR0OBBYEFPjbF9wIOB3uq2C7Yf6l8iMSU7SDMIGlBgNVHSMEgZ0w gZqAFAshdhvN+xIrKpHeFG4o/TrJt6i/oXykejB4MQswCQYDVQQGEwJVUzELMAkG A1UECBMCQ0ExHTAbBgNVBAoTFE1pbW9zYSBOZXR3b3JrcywgSW5jMQ4wDAYDVQQL EwVQaHl6ejEYMBYGA1UEAxMPaW50ZXJjZWRpYRXlMTMzMRMwEQYDVQQNEwoxNDEy OTU0Nzk1ggQFoABWMBMGA1UdEQQMMAqICCsGAQQBgtJcMAoGCCqGSM49BAMCA0cA MEQCIFK2FycAFextGiAQPozuT2LFR/AtPDHpGyXn6z3ccUCVAiBFkwn/YBVz5yof r5YHxgoz0LIJ+XUqLACNTHJhHstDCA== -----END CERTIFICATE-----

public static X509Certificate certFromFile(String path) {
    X509Certificate cert;
    try {
        CertificateFactory fact = CertificateFactory.getInstance("X.509");
        FileInputStream is = new FileInputStream(path);
        cert = (X509Certificate) fact.generateCertificate(is);
    } catch (CertificateException | FileNotFoundException e) {
        String error = e.getMessage();
        System.err.println(error);
        return null;
    }
    return cert;
}

public static String DumpCert(X509Certificate cert) {

    ByteArrayOutputStream out = new ByteArrayOutputStream();
    try {
        out.write("-----BEGIN CERTIFICATE-----\n".getBytes());
        out.write(Base64.encode(cert.getEncoded()));
        out.write("\n-----END CERTIFICATE-----\n".getBytes());
        out.close();
    } catch (IOException | CertificateEncodingException e) {
        System.out.println(e.getMessage());
    }

    String certpem = null;
    try {
        certpem = new String(out.toByteArray(), "ISO-8859-1");
    } catch (UnsupportedEncodingException e) {
        System.out.println(e.getMessage());
    }
    return certpem;
}

public static ContentSigner createSigner(PrivateKey privateKey) {
    try {
        ContentSigner sig = new JcaContentSignerBuilder("SHA256withECDSA").build(privateKey);
        return sig;
    } catch (Exception e) {
        throw new RuntimeException("Could not create content signer.", e);
    }
}

public static String signCSR(PKCS10CertificationRequest csr, GeneralName san, int validity,
                             X509Certificate cacert, KeyStore keystore, String alias) throws Exception {

    Date from = new Date();
    Date to = new Date(System.currentTimeMillis() + (validity * 86400000L));

    PrivateKey cakey = null;
    try {
        cakey = (PrivateKey) keystore.getKey(alias, null);
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }

    GeneralNames subjectAltNames = new GeneralNames(san);

    org.bouncycastle.asn1.x500.X500Name csrSubject = csr.getSubject();
    X500Name issuer = new X500Name(cacert.getSubjectX500Principal().getName());
    X500Name caName = X500Name.getInstance(PrincipalUtil.getIssuerX509Principal(cacert).getEncoded());

    GeneralNames gn = new GeneralNames(new GeneralName(caName));

    BigInteger serial = new BigInteger(32, new SecureRandom());

    SubjectPublicKeyInfo keyinfo = csr.getSubjectPublicKeyInfo();

    DigestCalculator digCalc = new BcDigestCalculatorProvider().get(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1));
    X509ExtensionUtils x509ExtensionUtils = new X509ExtensionUtils(digCalc);

    X509v3CertificateBuilder certgen = new X509v3CertificateBuilder(issuer, serial, from, to, csrSubject, keyinfo);
    BigInteger serialcert = cacert.getSerialNumber();
    Boolean buildCACert = true;

    PublicKey caKey = cacert.getPublicKey();
    SubjectPublicKeyInfo keyinfoCA = SubjectPublicKeyInfo.getInstance(caKey.getEncoded());

    AuthorityKeyIdentifier akiMain = x509ExtensionUtils.createAuthorityKeyIdentifier(keyinfoCA);
    AuthorityKeyIdentifier aki = new AuthorityKeyIdentifier(akiMain.getKeyIdentifier(), gn, serialcert);

    certgen.addExtension(Extension.basicConstraints, false, new BasicConstraints(buildCACert));
    certgen.addExtension(Extension.subjectKeyIdentifier, false, x509ExtensionUtils.createSubjectKeyIdentifier(keyinfo));
    certgen.addExtension(Extension.authorityKeyIdentifier, false, aki);
    certgen.addExtension(Extension.subjectAlternativeName, false, subjectAltNames);

    ContentSigner signer = createSigner(cakey);
    X509CertificateHolder holder = certgen.build(signer);

    X509Certificate cert = null;
    try {
        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        InputStream in = new ByteArrayInputStream(holder.getEncoded());
        cert = (X509Certificate) certFactory.generateCertificate(in);
    } catch (CertificateException | IOException e) {
        System.out.println(e.getMessage());
    }

    return DumpCert(cert);
}

public static GeneralName getSubjectAlternativeName(PKCS10CertificationRequest csr) {
    org.bouncycastle.asn1.pkcs.Attribute[] certAttributes = csr.getAttributes();
    for (org.bouncycastle.asn1.pkcs.Attribute attribute : certAttributes) {
        if (attribute.getAttrType().equals(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest)) {
            Extensions extensions = Extensions.getInstance(attribute.getAttrValues().getObjectAt(0));
            GeneralNames gns = GeneralNames.fromExtensions(extensions, Extension.subjectAlternativeName);
            GeneralName name = gns.getNames()[0];
            return name;
        }
    }
    return null;
}

public static String PrepAndSignCSR(String raw_csr, String certPath, String keystore) {

    KeyStore ks = null;
    Object parsedObject = null;
    String alias = "alias";
    String s = null;
    X509Certificate caCert = null;

    StringReader sr = new StringReader(raw_csr);
    PEMParser pemParser = new PEMParser(sr);

    try {
        parsedObject = pemParser.readObject();
        System.out.println("PemParser returned : " + parsedObject);
    } catch (IOException e) {
        String error = e.getMessage();
        System.err.println(error);
    }

    PKCS10CertificationRequest CSR = (PKCS10CertificationRequest) parsedObject;
    caCert = certFromFile(certPath);

    try {
        ks = KeyStore.getInstance("ncipher.sworld", "nCipherKM");
        FileInputStream in = new FileInputStream(keystore);
        ks.load(in, null);
    } catch (KeyStoreException |
            NoSuchAlgorithmException |
            CertificateException |
            IOException |
            NoSuchProviderException e) {
        System.err.println(e.getMessage());
    }

    GeneralName san = getSubjectAlternativeName(CSR);
    try {
        String cert = signCSR(CSR, san, 3500, caCert, ks, alias);
        return cert;
    } catch (Exception e) {
        String error = e.getMessage();
        System.err.println(error);
    }
    return null;
}

public static void main(String[] args) {

    .
    .
    .
    String fini = PrepAndSignCSR(csr, caCertPath, keystore);
    System.out.println(fini);

}

}

2

There are 2 answers

6
user207421 On

You're comparing apples and oranges.

  • java.security.cert.Certificate.verify() does nothing more than check the digital signature against the supplied public key.

  • openssl x509 verify checks the entire certificate chain, looks for the trust anchor, and verifies the digital signature and expiry dates of every certificate in the chain.

You need to specify the -CAfile or -CApath options to openssl. See the man page.

0
Robert Daniels On

After some debug sessions within openssl, I found the issuer name was not matching. Replaced this line with getEncoded... X500Name issuer = newX500Name(cacert.getSubjectX500Principal().getName()); X500Name issuer = X500Name.getInstance(cacert.getSubjectX500Principal().getEncoded());

The signed certs are now verifying with openssl verify command.