sun.security.x509.*、sun.security.util.*(非標準,版本間可能變動,生產建議用 Bouncy Castle)。GenX509Cert,包含 CA 生成、證書簽發、密鑰庫導出。CertAndKeyGen 自簽名,算法 RSA+MD5withRSA,有效期 10 年。KeyPairGenerator RSA 1024 位。X509CertInfo 設置主體/頒發者/有效期/擴展/公鑰,用 CA 私鑰簽名。public void createRootCA() throws Exception {
CertAndKeyGen cak = new CertAndKeyGen("RSA", "MD5WithRSA", null);
cak.setRandom(sr); // SecureRandom SHA1PRNG
cak.generate(1024); // 密鑰長度
X500Name subject = new X500Name("CN=RootCA,OU=hackwp,O=wp,L=BJ,S=BJ,C=CN");
X509Certificate caCert = cak.getSelfCertificate(
subject, new Date(), 3650L * 24 * 60 * 60 * 1000); // 10年毫秒數
// 導出 .crt (Base64 編碼)
BASE64Encoder encoder = new BASE64Encoder();
encoder.encodeBuffer(caCert.getEncoded(), new FileOutputStream("RootCa.crt"));
// 導出 .pfx (含私鑰)
savePfx("RootCA", cak.getPrivateKey(), "123456", new Certificate[]{caCert}, "RootCa.pfx");
}
public void createCert(X509Certificate caCert, PrivateKey caPrivKey, KeyPair userKp) throws Exception {
// 1. 解析 CA 證書獲取 X509CertInfo 模板
X509CertImpl caImpl = new X509CertImpl(caCert.getEncoded());
X509CertInfo info = (X509CertInfo) caImpl.get("x509.info");
// 2. 設置用戶公鑰
info.set("key", new CertificateX509Key(userKp.getPublic()));
// 3. 設置頒發者
X500Name issuer = new X500Name("CN=RootCA,OU=hackwp,O=wp,L=BJ,S=BJ,C=CN");
info.set("issuer.dname", issuer);
// 4. 設置主體
X500Name subject = new X500Name("CN=scriptx,OU=wps,O=wps,L=BJ,ST=BJ,C=CN");
info.set("subject.dname", subject);
// 5. 簽名算法標識符
Signature sig = Signature.getInstance("MD5WithRSA");
sig.initSign(userKp.getPrivate()); // 用用戶私鑰初始化(實際由 CA 簽名)
X500Signer signer = new X500Signer(sig, issuer);
info.set("algorithmID", new CertificateAlgorithmId(signer.getAlgorithmId()));
// 6. 有效期
Date notBefore = new Date();
Date notAfter = new Date(notBefore.getTime() + 3650L * 24 * 60 * 60 * 1000);
info.set("validity", new CertificateValidity(notBefore, notAfter));
// 7. 序列號(時間戳秒)
info.set("serialNumber", new CertificateSerialNumber((int)(System.currentTimeMillis()/1000)));
// 8. 版本 v3
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
// 9. 關鍵擴展
// 9.1 SubjectKeyIdentifier
CertificateExtensions exts = new CertificateExtensions();
exts.set("SubjectKeyIdentifier", new SubjectKeyIdentifierExtension(
new KeyIdentifier(userKp.getPublic()).getIdentifier()));
// 9.2 KeyUsage
KeyUsageExtension ku = new KeyUsageExtension();
ku.set(KeyUsageExtension.DIGITAL_SIGNATURE, true);
ku.set(KeyUsageExtension.NON_REPUDIATION, true);
ku.set(KeyUsageExtension.KEY_ENCIPHERMENT, true);
ku.set(KeyUsageExtension.DATA_ENCIPHERMENT, true);
exts.set("keyUsage", ku);
// 9.3 ExtendedKeyUsage (客戶端認證)
ObjectIdentifier ekuOid = new ObjectIdentifier(new int[]{1,3,6,1,5,5,7,3,3}); // id-kp-clientAuth
ExtendedKeyUsageExtension eku = new ExtendedKeyUsageExtension(
Collections.singletonList(ekuOid));
exts.set("extendedKeyUsage", eku);
info.set("extensions", exts);
// 10. 生成最終證書並由 CA 簽名
X509CertImpl userCert = new X509CertImpl(info);
userCert.sign(caPrivKey, "MD5WithRSA"); // 關鍵:用 CA 私鑰簽名
// 11. 驗證證書鏈
userCert.verify(caCert.getPublicKey(), null);
// 12. 導出 .crt .pfx .pvk
BASE64Encoder encoder = new BASE64Encoder();
encoder.encodeBuffer(userCert.getEncoded(), new FileOutputStream("ScriptX.crt"));
savePfx("scriptx", userKp.getPrivate(), "123456", new Certificate[]{userCert}, "ScriptX.pfx");
// 提取私鑰單獨導出 .pvk (PKCS#8)
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream("ScriptX.pfx"), "123456".toCharArray());
PrivateKey pvk = (PrivateKey) ks.getKey("scriptx", "123456".toCharArray());
Files.write(Paths.get("ScriptX.pvk"), pvk.getEncoded());
}
public void savePfx(String alias, PrivateKey privKey, String pwd,
Certificate[] chain, String path) throws Exception {
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(null, pwd.toCharArray()); // 初始化空庫
ks.setKeyEntry(alias, privKey, pwd.toCharArray(), chain);
ks.store(new FileOutputStream(path), pwd.toCharArray());
}
public void saveJks(String alias, PrivateKey privKey, String pwd,
Certificate[] chain, String path) throws Exception {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(null, pwd.toCharArray());
ks.setKeyEntry(alias, privKey, pwd.toCharArray(), chain);
ks.store(new FileOutputStream(path), pwd.toCharArray());
}
public static void main(String[] args) throws Exception {
GenX509Cert gen = new GenX509Cert();
gen.createRootCA(); // 產出 RootCa.crt, RootCa.pfx
gen.signCert(); // 內部調用 genKey() + createCert(),產出 ScriptX.crt, ScriptX.pfx, ScriptX.pvk
}
| 文件 | 格式 | 內容 | 用途 |
|---|---|---|---|
| RootCa.crt | X.509 PEM/Base64 | CA 根證書(公鑰) | 信任錨,導入信任存儲 |
| RootCa.pfx | PKCS#12 | CA 私鑰 + 根證書 | CA 簽發操作,極度機密 |
| ScriptX.crt | X.509 PEM/Base64 | 用戶證書(公鑰) | 分發給信任方、SSL/TLS 服務端 |
| ScriptX.pfx | PKCS#12 | 用戶私鑰 + 證書鏈 | 客戶端認證、簽名、解密 |
| ScriptX.pvk | PKCS#8 DER | 用戶私鑰 (純私鑰) | 舊版工具兼容,不推薦單獨存儲 |
sun.* 非標準,JDK 9+ 模組化後默認封裝,需 `--add-exports java.base/sun.security.x509=ALL-UNNAMED`,升級風險極大。**強烈建議遷移至 Bouncy Castle (BC)**。
// BC 生成自簽根證書
X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
caSubject, serial, notBefore, notAfter, caSubject, caKeyPair.getPublic());
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(caKeyPair.getPrivate());
X509CertificateHolder certHolder = builder.build(signer);
X509Certificate caCert = new JcaX509CertificateConverter().getCertificate(certHolder);
// BC 簽發用戶證書
builder = new JcaX509v3CertificateBuilder(caSubject, userSerial, notBefore, notAfter, userSubject, userKeyPair.getPublic())
.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment))
.addExtension(Extension.extendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeId.id_kp_clientAuth))
.addExtension(Extension.subjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(userKeyPair.getPublic()));
certHolder = builder.build(signer); // 同一簽名器(CA 私鑰)
本文展示了使用 JDK 內部 sun.security.x509 包從零構建 X.509 v3 證書體系:CA 自簽、用戶證書簽發(含 KeyUsage/EKU/SKI 擴展)、PKCS#12/JKS 密鑰庫導出。代碼完整可運行,但**僅供教學/測試用途**。**生產環境 PKI 系統必須使用 Bouncy Castle 等成熟庫、現代簽名算法、硬件保護私鑰、合規審計流程。**