使用bouny castle正确创建一个带有中间证书的新证书

Dok*_*kki 6 java security bouncycastle pem x509certificate

所以我的问题如下,

基本上我想使用充气城堡(jdk16版本1.46)创建证书链.我对bouncy castle和java.security很新,所以如果我的方法可能完全错误,但无论如何这就是我所做的:

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

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

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

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);
Run Code Online (Sandbox Code Playgroud)

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

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

地图只是添加基本约束以将证书设置为CA. 我在这里使用地图,因为我希望能够在需要时添加额外的X509扩展.

//-----save ca certificate in PEM format
X509CertificateGenerator.savePemX509Certificate(caCert, caPair.getPrivate(), caWriter);
Run Code Online (Sandbox Code Playgroud)

这将使用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);
Run Code Online (Sandbox Code Playgroud)

该过程基本相同,但我添加了额外的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);
Run Code Online (Sandbox Code Playgroud)

基本上它与创建中间证书相同.但是我现在使用以下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;
}
Run Code Online (Sandbox Code Playgroud)

保存证书和密钥的代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我基本上将证书和密钥放在文件中,这就是全部.结果如下,对我来说似乎很好.

-----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-----
Run Code Online (Sandbox Code Playgroud)

所以在gtrig为我提供了创建证书的正确方法后,我最终使用这种方法创建了一个普通或自签名(如果私钥来自与公钥相同的keyPair)证书

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;
}
Run Code Online (Sandbox Code Playgroud)

gtr*_*rig 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);
Run Code Online (Sandbox Code Playgroud)

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

        new X509KeyUsage(
           X509KeyUsage.keyCertSign|
           X509KeyUsage.cRLSign));
Run Code Online (Sandbox Code Playgroud)