6

所以我的问题如下,

基本上我想使用充气城堡(jdk16 版本 1.46)创建一个证书链。一般来说,我对充气城堡和 java.security 相当陌生,所以如果我的方法可能完全错误,但无论如何这就是我所做的:

到目前为止,我能够创建一个自签名证书,用作根证书。这是使用以下代码完成的:

//-----create CA certificate with key
KeyPair caPair = Signing.generateKeyPair("DSA", 1024, null, null);

这基本上创建了密钥对,如果需要,两个空选项用于提供者和安全随机选项。

Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> caMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>();
caMap.put(X509Extensions.BasicConstraints, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(true, new BasicConstraints(true)));

//------this creates the self signed certificate        
X509Certificate caCert = X509CertificateGenerator.generateX509Certificate(serial, "CN=CA", "CN=CA", start, end, "SHA1withDSA", caPair.getPrivate(), caPair.getPublic(), null, caMap);

这将创建具有提供的属性的证书。

  • serial = 只是当前时间(以毫秒为单位)
  • start = 基本上与串行相同(可能有 1 或 2 毫秒的差异)
  • 结束 = 开始 + 2 天

该映射只是添加了将证书设置为 CA 的基本约束。我在这里使用地图,因为我希望能够在需要时添加额外的 X509Extensions。

//-----save ca certificate in PEM format
X509CertificateGenerator.savePemX509Certificate(caCert, caPair.getPrivate(), caWriter);

这将使用 bouncy caste pem writer 将证书和私钥存储在 pem 文件中。

生成文件之后,我也可以安装该文件(我使用 IE,然后通过 Internet 选项将其安装为受信任的 CA。证书也显示为有效)。

之后,我使用以下代码创建中间证书(注意上面的代码在同一范围内,因此这些变量也可用)

KeyPair intermediatePair = Signing.generateKeyPair("DSA", 1024, null, null);    

Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> intermediateMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>();
intermediateMap.put(X509Extensions.AuthorityKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new AuthorityKeyIdentifierStructure(caCert)));
intermediateMap.put(X509Extensions.SubjectKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new SubjectKeyIdentifierStructure(intermediatePair.getPublic())));

X509Certificate intermediateCert = X509CertificateGenerator.generateX509Certificate(serial.add(BigInteger.valueOf(1l)), "CN=intermediate", caCert.getSubjectX500Principal().toString(), start, end, "SHA1withDSA", caPair.getPrivate(), intermediatePair.getPublic(), null, intermediateMap);   

//-----save intermediate certificate in PEM format
X509CertificateGenerator.savePemX509Certificate(intermediateCert, intermediatePair.getPrivate(), intermediateWriter);

该过程基本相同,但是我添加了额外的 X509Extensions:

  • X509Extensions.AuthorityKeyIdentifier = 将 CA 证书设置为中间父级
  • X509Extensions.SubjectKeyIdentifier = 使用生成的证书公钥

此外,CA 用作颁发者,CA 私钥用于创建中间证书。

这也有效,我可以安装中间证书(再次使用 IE),它还显示父证书是生成的 CA 证书并且证书是有效的。

现在到了我想我犯了一个错误的棘手部分。我现在使用以下代码使用中间证书创建一个新证书。

KeyPair endPair = Signing.generateKeyPair("DSA", 1024, null, null);

Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> endMap = new HashMap<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>>();
endMap.put(X509Extensions.AuthorityKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new AuthorityKeyIdentifierStructure(intermediateCert)));
endMap.put(X509Extensions.SubjectKeyIdentifier, new AbstractMap.SimpleEntry<Boolean, DEREncodable>(false, new SubjectKeyIdentifierStructure(endPair.getPublic())));

X509Certificate endCert = X509CertificateGenerator.generateX509Certificate(serial.add(BigInteger.valueOf(1l)), "CN=end", intermediateCert.getSubjectX500Principal().toString(), start, end, "SHA1withDSA", intermediatePair.getPrivate(), endPair.getPublic(), null, endMap);

X509CertificateGenerator.savePemX509Certificate(endCert, endPair.getPrivate(), endWriter);

本质上它与创建中间证书相同。但是我现在使用以下 X509Extension 设置:

  • X509Extensions.AuthorityKeyIdentifier = 将中间证书设置为证书父级
  • X509Extensions.SubjectKeyIdentifier = 使用生成的证书公钥

中间证书也用作颁发者,其私钥用于创建证书。

我也可以安装新证书,但是当我检查是否(再次 IE)时,它显示证书无效,因为“此 CA 要么无权颁发证书,要么证书不能用作最终实体。”

所以我需要通过添加一些我假设的 KeyUsages/ExtendedKeyUsage 来使中间证书也能够创建新证书。

有人知道我如何启用中间证书来做我需要它做的事情,或者我是否做错了什么?

编辑1:

所以好吧,我忘了提供创建证书的方法和以 PEM 格式保存证书的方法的代码(我将它重命名为savePemX509Certificate,因为旧的有误导性)。

证书生成代码:

public static X509Certificate generateX509Certificate(BigInteger serialnumber, String subject, String issuer, Date start , Date end, String signAlgorithm, PrivateKey privateKey, PublicKey publicKey, String provider, Map<ASN1ObjectIdentifier, Entry<Boolean, DEREncodable>> map) throws CertificateEncodingException, InvalidKeyException, IllegalStateException, NoSuchProviderException, NoSuchAlgorithmException, SignatureException
{
    if(serialnumber!=null && subject!=null && issuer!=null && start!=null && end!=null && signAlgorithm !=null && privateKey!=null && publicKey!=null)
    {
        //-----GENERATE THE X509 CERTIFICATE
        X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
        X509Principal dnSubject = new X509Principal(subject);
        X509Principal dnIssuer = new X509Principal(issuer);

        certGen.setSerialNumber(serialnumber);
        certGen.setSubjectDN(dnSubject);
        certGen.setIssuerDN(dnIssuer);
        certGen.setNotBefore(start);
        certGen.setNotAfter(end);
        certGen.setPublicKey(publicKey);
        certGen.setSignatureAlgorithm(signAlgorithm);

        //-----insert extension if needed
        if(map!=null)
            for(ASN1ObjectIdentifier extension : map.keySet())
                certGen.addExtension(extension, map.get(extension).getKey(), map.get(extension).getValue());

        return certGen.generate(privateKey, provider);  
    }
    return null;
}

证书和密钥的保存代码:

public static boolean savePemX509Certificate(X509Certificate cert, PrivateKey key, Writer writer) throws NoSuchAlgorithmException, NoSuchProviderException, CertificateEncodingException, SignatureException, InvalidKeyException, IOException 
{       
    if(cert!=null && key!=null && writer!=null)
    {               
        PEMWriter pemWriter = new PEMWriter(writer);
        pemWriter.writeObject(cert);
        pemWriter.flush();

        if(key!=null)
        {
            pemWriter.writeObject(key);
            pemWriter.flush();
        }
        pemWriter.close();
        return true;
        }
    return false;
}

如您所见,我基本上将证书和密钥放在文件中,仅此而已。结果如下,对我来说似乎很好。

-----BEGIN CERTIFICATE-----
MIICdjCCAjagAwIBAgIGAUDuXLRLMAkGByqGSM44BAMwDTELMAkGA1UEAwwCQ0Ew
HhcNMTMwOTA1MTM0MzA3WhcNMTMwOTA3MTM0MzA3WjANMQswCQYDVQQDDAJDQTCC
AbcwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADD
Hj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gE
exAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/Ii
Axmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4
V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozI
puE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4Vrl
nwaSi2ZegHtVJWQBTDv+z0kqA4GEAAKBgAeFoGATLbIr8+QNuxcbYJ7RhbefKWSC
Br67Pp4Ynikxx8FZN4kCjGX7pwT1KffN3gta7jxIXNM5G3IFbs4XnYljh5TbdnjP
9Ge3kxpwncsbMQfCqIwHh8T5gh55KaxH7yYV2mrtEEqj7NBL4thQhJe2WGwgkB9U
NxNmLoMq3m4poyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAJ
BgcqhkjOOAQDAy8AMCwCFFm5ybLY09y8y2uGsEnpceffy2KaAhQIyshgy3ohCLxQ
q3CmnvC+cfT2VQ==
-----END CERTIFICATE-----
-----BEGIN DSA PRIVATE KEY-----
MIIBuwIBAAKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR
+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb
+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdg
UI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlX
TAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCj
rh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQB
TDv+z0kqAoGAB4WgYBMtsivz5A27FxtgntGFt58pZIIGvrs+nhieKTHHwVk3iQKM
ZfunBPUp983eC1ruPEhc0zkbcgVuzhediWOHlNt2eM/0Z7eTGnCdyxsxB8KojAeH
xPmCHnkprEfvJhXaau0QSqPs0Evi2FCEl7ZYbCCQH1Q3E2YugyrebikCFDJCJHtt
NWB4LWYc4y4QvJ/l46ap
-----END DSA PRIVATE KEY-----

因此,在 gtrig 为我提供了创建证书的正确方法之后,我最终使用此方法创建了普通或自签名(如果私钥来自与公钥相同的密钥对)证书

public static X509Certificate createX509V3Certificate(X500Principal name, BigInteger serial, Date start, Date end, PublicKey pubKey, String algorithm, PrivateKey privateKey, Map<ASN1ObjectIdentifier, Entry<Boolean, ASN1Object>> map, X509Certificate parentCert) throws IOException, OperatorCreationException, CertificateException
{
    if(serial!=null && start!=null && end!=null && name!=null && pubKey!=null && algorithm!=null && privateKey!=null)
    {
        ContentSigner signer = new JcaContentSignerBuilder(algorithm).build(privateKey);
        X509v3CertificateBuilder certBldr = null;
        if(parentCert==null)
            certBldr = new JcaX509v3CertificateBuilder(name, serial, start, end, name, pubKey);
        else
            certBldr = new JcaX509v3CertificateBuilder(parentCert, serial, start, end, name, pubKey);

        if(map!=null)
            for(ASN1ObjectIdentifier extension : map.keySet())
                certBldr.addExtension(extension, map.get(extension).getKey(), map.get(extension).getValue());

        return new JcaX509CertificateConverter().setProvider("BC").getCertificate(certBldr.build(signer));  
    }
    return null;
}
4

1 回答 1

6

您创建 PEM 文件的方式看起来有些问题。您正在使用一种名为 的方法generateSelfSignedPemX509Certificate,但您并不真正想要一个自签名证书,您想要一个由中间私钥签名的最终证书,并且您想要一个由 CA 私钥签名的中间证书。

此外,您需要basic constraintskey usage扩展您的证书。

为了创建由其他实体签名的证书(非自签名),我使用 Bouncy Castle 中的这些方法来创建“结束”证书。

  ASN1Sequence seq= 
     (ASN1Sequence) new ASN1InputStream(parentPubKey.getEncoded()).readObject();

  SubjectPublicKeyInfo parentPubKeyInfo = new SubjectPublicKeyInfo(seq);

  ContentSigner signer = new JcaContentSignerBuilder(algorithm).build(parentPrivKey);

  X509v3CertificateBuilder certBldr = 
     new JcaX509v3CertificateBuilder(
        parentCert, 
        serialNum,
        startDate, 
        endDate, 
        distName, 
        pubKey)
     .addExtension(
           new ASN1ObjectIdentifier("2.5.29.35"),
           false,
           new AuthorityKeyIdentifier(parentPubKeyInfo))
     .addExtension(
        new ASN1ObjectIdentifier("2.5.29.19"), 
        false,
        new BasicConstraints(false)) // true if it is allowed to sign other certs
     .addExtension(
        new ASN1ObjectIdentifier("2.5.29.15"),
        true,
        new X509KeyUsage(
           X509KeyUsage.digitalSignature |
           X509KeyUsage.nonRepudiation   |
           X509KeyUsage.keyEncipherment  |
           X509KeyUsage.dataEncipherment));

  // Build/sign the certificate.
  X509CertificateHolder certHolder = certBldr.build(signer);

  X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC)
     .getCertificate(certHolder);

对于 CA 或中间证书,您需要添加SubjectKeyIdentifier扩展。此外,BasicConstraints应该是true,并且 KeyUsage 应该是:

        new X509KeyUsage(
           X509KeyUsage.keyCertSign|
           X509KeyUsage.cRLSign));
于 2013-09-06T01:04:07.180 回答