所以我的问题如下,
基本上,我想使用充气城堡(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);
这将创建具有提供的属性的证书。
地图只是简单地添加了基本的约束以将证书设置为CA。我在这里使用地图,因为我希望能够在需要时添加额外的X509扩展。
//-----save ca certificate in PEM format
X509CertificateGenerator.savePemX509Certificate(caCert, caPair.getPrivate(), caWriter);
这将使用bouncy种姓pem写入器将证书和私钥存储在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);
过程基本相同,但我添加了额外的X509Ex的:
此外,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);
本质上它与创建中间证书相同。但是我现在使用以下X509Exency设置:
中间证书也用作颁发者,其私钥用于创建证书。
我也可以安装新证书,但是当我检查是否(再次IE)时,它显示证书无效,因为“CA无权颁发证书,或者证书不能用作最终实体。”
所以我不知何故需要通过添加一些我假设的KeyUsages/ExtendedKeyUsage来使中间证书也能够创建新证书。
有人知道我如何启用中间证书来做我需要它做的事情,或者如果我做了一些错误的事情?
编辑1:
所以好吧,我忘记提供创建证书的方法和以PEM格式保存证书的方法的代码(我将其重命名为savePemX509证书,因为旧的方法具有误导性)。
证书生成代码:
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为我提供了创建证书的正确方法之后,我最终使用此方法创建了一个普通或自签名(如果私钥与公钥来自同一个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;
}
创建PEM文件的方式看起来有问题。您使用的方法名为GenerateSelfSignedPemX509证书
,但您并不真正需要自签名证书,您需要由中间私钥签名的最终证书,以及由CA私钥签名的中间证书。
此外,您需要证书上的基本约束
和密钥使用
扩展。
为了创建由其他实体签名的证书(非自签名),我使用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或中间证书,您需要添加SubjectKeyIdfier
扩展。此外,BasicConstraint
应该是true
,KeyUsage应该是:
new X509KeyUsage(
X509KeyUsage.keyCertSign|
X509KeyUsage.cRLSign));