将证书链保存在 pkcs12 密钥库中

Posted

技术标签:

【中文标题】将证书链保存在 pkcs12 密钥库中【英文标题】:Saving certificate chain in a pkcs12 keystore 【发布时间】:2012-10-23 19:53:56 【问题描述】:

以下代码:

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

其中 certChain 持有最终证书和颁发者证书(即两个证书), 如果 keyStore 是 PKCS12 的实例,则不会将颁发者证书保存为已保存到文件系统密钥库文件中的链的一部分。

如果密钥库类型为PKCS12-3DES-3DES,它会保存两个证书。 为什么是这样? PKCS12 不认为两个证书都是链的一部分吗?

编辑:这是SSCCE。这对"JKS" 有效,在"PKCS12" 失败:只有链中的第一个证书可以通过getCertificateChain(String) 访问。可以使用openssl pkcs12 打开保存的文件,同时显示两个证书。

    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;

【问题讨论】:

OOI,来自 Oracle(SunJSSE 提供程序)的 PKCS12 密钥库类型是否表现出相同的症状?如果不是,那么这对于 BC 邮件列表来说可能是一个很好的问题。 @DuncanJones:我在 BC 邮件列表中问过这个问题,一个多星期没有得到回复 PKCS#12 没有任何假设,因为它是任意数量的证书及其私钥的容器。因此,将放入 PKCS#12 容器的内容是特定于实现的。 @EugeneMayevski'EldoSCorp:但我所做的只是将链作为关键条目的一部分。当然这不是出于某种原因不支持的东西 @EugeneMayevski'EldoSCorp: 好吧。换一种说法。PKCS12-3DES-3DES 不仅仅在加密方案上有所不同?那么为什么行为会有所不同呢? 【参考方案1】:

您的代码有 2 个错误:

第一:您没有为证书设置颁发者(客户端证书应该由CA颁发才能使链有效)。

第二个:你在创建证书链时使用了错误的顺序(应该是客户端证书,最后是CA)

这里是重新设计的 SSCCE,它可以正常工作。

@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;
    

【讨论】:

是的,你修好了。好工作!我确实意识到我没有在那里生成有效的链。在另一个测试中,我认为我是(同样的失败),所以我认为这并不重要,特别是因为它适用于JKS。原来是命令!所以顺序是:endpointtrust 锚最后。 @Cratylus,这能解决你的问题吗?我投票赞成接受这个答案。【参考方案2】:

根据您使用的 JDK,有不同的方式来打包您的应用程序。当一些人使用 Linux 和 OpenJDK 以及其他一些人在 Windows 上使用 SunJDK (Oracle) 进行开发时,我们就会遇到这种情况。

为了能够使用最强的算法,最新版本需要做一些额外的配置。如果您的问题与 JCE 政策有关,This article 可以为您提供帮助。

【讨论】:

我不认为这是一个政策问题。 OP 使用 Bouncy Castle,而不是 SunJSSE。 BouncyCastle 是 JCE 的实现,我们通过提供的链接解决了我们的问题,即使我们在项目中只使用 BouncyCastle 类。但你是对的,我的答案可能是也可能不是 Cratylus 的解决方案。 谢谢。无论如何都会检查这是否有帮助!

以上是关于将证书链保存在 pkcs12 密钥库中的主要内容,如果未能解决你的问题,请参考以下文章

如何使用keytool列出存储在PKCS12密钥库中的证书?

如何使用 keytool 在 PKCS12 密钥库中创建证书?

如何使用keytool将证书创建到PKCS12密钥库?

如何使用pkcs11接口读取证书的密钥进行RSA

来自 CA 的 PKCS12 Java 密钥库和 Java 中的用户证书

使用OpenSSL创建多级CA证书链签发证书并导出为pkcs12/p12/pfx文件