在本文中,我将讨论使用 bouncy castle API 在 java 程序中创建 SSL 证书。 Bouncy castle 是一种轻量级加密 API。它是 Java Cryptography Extension(JCE)和 Java Cryptography Architecture(JCA)的实现。
我将创建非常基本的证书,其中只包含必要的属性。您可以浏览 API 以获取可应用于证书的更多操作和属性
请在代码中导入以下依赖项。
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.55</version>
</dependency>
<!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.4</version>
</dependency>
复制代码
我在代码中执行以下步骤: 步骤: 1)创建自签名根证书。 2)创建在步骤 1 中创建的根证书签名的中间证书. 3)创建在步骤 2 中创建的中间证书签名的最终用户证书。
代码:
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.joda.time.DateTime;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Random;
public class App {
public static void main(String[] args) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, OperatorCreationException, InvalidKeyException, NoSuchProviderException, SignatureException, UnrecoverableKeyException {
Security.addProvider(new BouncyCastleProvider());
// Create self signed Root CA certificate
KeyPair rootCAKeyPair = generateKeyPair();
X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
new X500Name("CN=rootCA"), // issuer authority
BigInteger.valueOf(new Random().nextInt()), //serial number of certificate
DateTime.now().toDate(), // start of validity
new DateTime(2025, 12, 31, 0, 0, 0, 0).toDate(), //end of certificate validity
new X500Name("CN=rootCA"), // subject name of certificate
rootCAKeyPair.getPublic()); // public key of certificate
// key usage restrictions
builder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign));
builder.addExtension(Extension.basicConstraints, false, new BasicConstraints(true));
X509Certificate rootCA = new JcaX509CertificateConverter().getCertificate(builder
.build(new JcaContentSignerBuilder("SHA256withRSA").setProvider("BC").
build(rootCAKeyPair.getPrivate()))); // private key of signing authority , here it is self signed
saveToFile(rootCA, "D:\\rootCA.cer");
//create Intermediate CA cert signed by Root CA
KeyPair intermedCAKeyPair = generateKeyPair();
builder = new JcaX509v3CertificateBuilder(
rootCA, // here rootCA is issuer authority
BigInteger.valueOf(new Random().nextInt()), DateTime.now().toDate(),
new DateTime(2025, 12, 31, 0, 0, 0, 0).toDate(),
new X500Name("CN=IntermedCA"), intermedCAKeyPair.getPublic());
builder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign));
builder.addExtension(Extension.basicConstraints, false, new BasicConstraints(true));
X509Certificate intermedCA = new JcaX509CertificateConverter().getCertificate(builder
.build(new JcaContentSignerBuilder("SHA256withRSA").setProvider("BC").
build(rootCAKeyPair.getPrivate())));// private key of signing authority , here it is signed by rootCA
saveToFile(intermedCA, "D:\\intermedCA.cer");
//create end user cert signed by Intermediate CA
KeyPair endUserCertKeyPair = generateKeyPair();
builder = new JcaX509v3CertificateBuilder(
intermedCA, //here intermedCA is issuer authority
BigInteger.valueOf(new Random().nextInt()), DateTime.now().toDate(),
new DateTime(2025, 12, 31, 0, 0, 0, 0).toDate(),
new X500Name("CN=endUserCert"), endUserCertKeyPair.getPublic());
builder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature));
builder.addExtension(Extension.basicConstraints, false, new BasicConstraints(false));
X509Certificate endUserCert = new JcaX509CertificateConverter().getCertificate(builder
.build(new JcaContentSignerBuilder("SHA256withRSA").setProvider("BC").
build(intermedCAKeyPair.getPrivate())));// private key of signing authority , here it is signed by intermedCA
saveToFile(endUserCert, "D:\\endUserCert.cer");
}
private static KeyPair generateKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException {
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
kpGen.initialize(2048, new SecureRandom());
return kpGen.generateKeyPair();
}
private static void saveToFile(X509Certificate certificate, String filePath) throws IOException, CertificateEncodingException {
FileOutputStream fileOutputStream = new FileOutputStream(filePath);
fileOutputStream.write(certificate.getEncoded());
fileOutputStream.flush();
fileOutputStream.close();
}
}
复制代码
在这里你可以看到,对于 rootCA,我使用自己的私钥来签署证书。对于 intermedCA,我使用 rootCA 私钥进行签名。对于最终用户证书,我使用 IntermedCA 私钥进行签名。 在现实生活中,您还可以看到与此类似的证书链。从浏览器打开任何 HTTPS 连接证书,并观察证书链和每个证书的属性。
“SHA256withRSA”是我用它来签名证书的签名算法。 在 keyUsage 中,“keyCertSign”仅限制证书使用以签署其他证书。虽然 SSL 客户端需要“digitalSignature”使用,但我们的 Web 浏览器使用该证书进行实体身份验证和数据源身份验证的完整性。
BasicConstraints 中的“true”标志将证书标记为可以签署其他证书的 CA 证书。“false”标志将证书标记为证书链的最终实体。
作为此程序的出现,您将在指定的文件路径中拥有 3 个证书。
现在首先打开 rootCA:
您可以看到证书不可信的消息。因此,我们将此证书添加到受信任的根证书存储区中
1)单击“安装证书”按钮。 2)选择“本地计算机”,单击下一步 3)选择“将所有证书放在以下存储中”的第二个选项 4)浏览并选择“受信任的根证书颁发机构” 5)单击“下一步”,然后单击“完成”。 6)导入成功。
对于“Intermed CA”证书执行相同的操作,仅在步骤 4 中进行更改:浏览并选择“中间证书颁发机构”。
现在关闭证书并重新打开它。 让我们现在检查每个证书:
- RootCA
- 中间 CA
- 最终用户证书
还有其他方法可以创建证书和证书链。例如:java“keytool”命令或使用“keystore explorer”之类的工具.... 但很多时候你必须从程序中进行证书操作。 所以这在当时会非常有用。
在下一篇文章中,我将更多地介绍证书的主题以及您可以使用 java 程序中的 bouncy castle 执行的操作。