24

The following code:

//used Bouncy Castle provider for keyStore
keyStore.setKeyEntry(alias, (Key)keyPair.getPrivate(), pwd, certChain);  

where certChain holds the end certificate and the issuer certificate (i.e. two certificates),
doesn't save the issuer certificate as part of the chain in the saved to the file system keystore file if the keyStore is an instance of PKCS12.

It does save both certificates if the keystore type is PKCS12-3DES-3DES. Why is this? Doesn't a PKCS12 suppose to have both certificates are part of the chain?

EDIT: Here's an SSCCE. This works fine with "JKS", fails with "PKCS12": Only the first certificate in the chain is accessible via getCertificateChain(String). The saved file can be opened with openssl pkcs12 revealing both certificates.

    public void testKeyStore() {
    try {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();
        Certificate[] outChain = { createCertificate("CN=CA", publicKey, privateKey), createCertificate("CN=Client", publicKey, privateKey) };

        KeyStore outStore = KeyStore.getInstance("PKCS12");
        outStore.load(null, "secret".toCharArray());
        outStore.setKeyEntry("mykey", privateKey, "secret".toCharArray(), outChain);            
        OutputStream outputStream = new FileOutputStream("c:/outstore.pkcs12");
        outStore.store(outputStream, "secret".toCharArray());
        outputStream.flush();
        outputStream.close();

        KeyStore inStore = KeyStore.getInstance("PKCS12");      
        inStore.load(new FileInputStream("c:/outstore.pkcs12"), "secret".toCharArray());
        Key key = outStore.getKey("myKey", "secret".toCharArray());
        assertEquals(privateKey, key);

        Certificate[] inChain = outStore.getCertificateChain("mykey");
        assertNotNull(inChain);
        assertEquals(outChain.length, inChain.length);
    } catch (Exception e) {
        e.printStackTrace();
        fail(e.getMessage());
    }
}

private static X509Certificate createCertificate(String dn, PublicKey publicKey, PrivateKey privateKey) throws Exception {
    X509V3CertificateGenerator certGenerator = new X509V3CertificateGenerator();
    certGenerator.setSerialNumber(new BigInteger("1"));
    certGenerator.setIssuerDN(new X509Name(dn));
    certGenerator.setSubjectDN(new X509Name(dn));
    certGenerator.setNotBefore(Calendar.getInstance().getTime());
    certGenerator.setNotAfter(Calendar.getInstance().getTime());
    certGenerator.setPublicKey(publicKey);
    certGenerator.setSignatureAlgorithm("SHA1withRSA");
    X509Certificate certificate = (X509Certificate)certGenerator.generate(privateKey, "BC");
    return certificate;
}
martijno
  • 1,723
  • 1
  • 23
  • 53
Cratylus
  • 52,998
  • 69
  • 209
  • 339
  • OOI, does the PKCS12 keystore type from Oracle (SunJSSE provider) exhibit the same symptoms? If not, then this might be a good question for the BC mailing list. – Duncan Jones Nov 03 '12 at 09:09
  • @DuncanJones:I asked this in BC mailing list and did not get a reply for over a week – Cratylus Nov 03 '12 at 09:10
  • PKCS#12 doesn't suppose anything as it's a container for any number of certificates and their private keys. So it's implementation-specific what will be put to PKCS#12 container. – Eugene Mayevski 'Callback Nov 04 '12 at 15:29
  • 1
    @EugeneMayevski'EldoSCorp:But all I am trying to do is put the chain as part of the key entry.Surely this is not something that would not be supported for some reason – Cratylus Nov 04 '12 at 16:33
  • @Cratylus as a library developer (not related to BouncyCastle) I have a different point of view. In our product what you specify explicitly is what you get in PKCS#12. There are shortcuts to grabbing the complete chain, but shortcuts must be used explicitly as well. – Eugene Mayevski 'Callback Nov 04 '12 at 18:07
  • 1
    @EugeneMayevski'EldoSCorp:Ok.To put it differently then.`PKCS12-3DES-3DES` doesn't differ only in the encryption scheme?So why is the behavior different for that? – Cratylus Nov 04 '12 at 20:42
  • @Cratylus Windows (in earlier versions like original XP) didn't support strong algorithms like 3DES in PKCS#12. This can be related. But you should ask BouncyCrypto authors, not me. – Eugene Mayevski 'Callback Nov 05 '12 at 10:08
  • Which JDK are you using? OpenJDK or Sun JDK or another one? – рüффп Dec 16 '12 at 10:46

2 Answers2

17

Your code has 2 error:

first: You not set Issuer for certificate (client cert should be issued by CA to make valid chain).

second: You use wrong order when create certificate chain (should be client certs, CA last)

here is reworked SSCCE, and it works without errors.

@Test
public void testKeyStore() throws Exception{
        try {
        String storeName =  "/home/grigory/outstore.pkcs12";
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();
        Certificate trustCert =  createCertificate("CN=CA", "CN=CA", publicKey, privateKey);
        Certificate[] outChain = { createCertificate("CN=Client", "CN=CA", publicKey, privateKey), trustCert };

        KeyStore outStore = KeyStore.getInstance("PKCS12");
        outStore.load(null, "secret".toCharArray());
        outStore.setKeyEntry("mykey", privateKey, "secret".toCharArray(), outChain);
        OutputStream outputStream = new FileOutputStream(storeName);
        outStore.store(outputStream, "secret".toCharArray());
        outputStream.flush();
        outputStream.close();

        KeyStore inStore = KeyStore.getInstance("PKCS12");
        inStore.load(new FileInputStream(storeName), "secret".toCharArray());
        Key key = outStore.getKey("myKey", "secret".toCharArray());
        Assert.assertEquals(privateKey, key);

        Certificate[] inChain = outStore.getCertificateChain("mykey");
        Assert.assertNotNull(inChain);
        Assert.assertEquals(outChain.length, inChain.length);
    } catch (Exception e) {
        e.printStackTrace();
        throw new AssertionError(e.getMessage());
    }
   }
    private static X509Certificate createCertificate(String dn, String issuer, PublicKey publicKey, PrivateKey privateKey) throws Exception {
        X509V3CertificateGenerator certGenerator = new X509V3CertificateGenerator();
        certGenerator.setSerialNumber(BigInteger.valueOf(Math.abs(new Random().nextLong())));
        certGenerator.setSubjectDN(new X509Name(dn));
        certGenerator.setIssuerDN(new X509Name(issuer)); // Set issuer!
        certGenerator.setNotBefore(Calendar.getInstance().getTime());
        certGenerator.setNotAfter(Calendar.getInstance().getTime());
        certGenerator.setPublicKey(publicKey);
        certGenerator.setSignatureAlgorithm("SHA1withRSA");
        X509Certificate certificate = (X509Certificate)certGenerator.generate(privateKey, "BC");
        return certificate;
    }
Born2Smile
  • 2,918
  • 1
  • 18
  • 20
user1516873
  • 5,060
  • 2
  • 37
  • 56
  • Yeah, you fixed it. Good job! I do realize I wasn't generating a valid chain there. In another test I thought I was (with the same failure), so I thought that didn't matter, especially since it worked for `JKS`. Turns out it was the order! So the order is: endpointtrust anchor last. – martijno Dec 19 '12 at 13:40
  • @Cratylus, does this solve your problem? I vote for accepting this answer. – martijno Dec 19 '12 at 13:42
0

Depending which JDK you use, there are different way to package your application. It happens to us when some people where using Linux and OpenJDK and some other developping on Windows with SunJDK (Oracle).

The lastest have some extra configuration to do in order to be able to use the strongest algorithms. This article can help you if your problem is related to the JCE Policy.

рüффп
  • 5,172
  • 34
  • 67
  • 113
  • I don't think this is a policy problem. The OP uses Bouncy Castle, not SunJSSE. – martijno Dec 18 '12 at 11:31
  • 1
    BouncyCastle IS an implementation of JCE, we solved our issue by the provided link even we were using only BouncyCastle classes in our project. But you are right, my answer could or not be the solution to Cratylus. – рüффп Dec 18 '12 at 19:47