首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

純 Java 代碼實現 X.509 數字證書生成、CA 根簽名與 PFX/JKS 密鑰庫導出

     編輯:Java 相關知識

技術棧與依賴

  • 純 JDK 內部 API:sun.security.x509.*、sun.security.util.*(非標準,版本間可能變動,生產建議用 Bouncy Castle)。
  • 無第三方依賴,JDK 8+ 可直接運行。
  • 核心類:GenX509Cert,包含 CA 生成、證書簽發、密鑰庫導出。

核心流程架構

  1. 生成 CA 根證書:CertAndKeyGen 自簽名,算法 RSA+MD5withRSA,有效期 10 年。
  2. 生成用戶密鑰對:KeyPairGenerator RSA 1024 位。
  3. 簽發用戶證書:構建 X509CertInfo 設置主體/頒發者/有效期/擴展/公鑰,用 CA 私鑰簽名。
  4. 導出密鑰庫:PKCS#12 (.pfx) 與 JKS 格式,含私鑰+證書鏈。

關鍵代碼片段解析

1. 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");
}

2. 用戶證書簽發

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());
}

3. 密鑰庫導出工具方法

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.crtX.509 PEM/Base64CA 根證書(公鑰)信任錨,導入信任存儲
RootCa.pfxPKCS#12CA 私鑰 + 根證書CA 簽發操作,極度機密
ScriptX.crtX.509 PEM/Base64用戶證書(公鑰)分發給信任方、SSL/TLS 服務端
ScriptX.pfxPKCS#12用戶私鑰 + 證書鏈客戶端認證、簽名、解密
ScriptX.pvkPKCS#8 DER用戶私鑰 (純私鑰)舊版工具兼容,不推薦單獨存儲

安全與合規提醒

  • 算法選擇:示例使用 MD5withRSA,**已被淘汰**(MD5 碰撞攻擊)。生產必須使用 SHA256withRSA 或 SHA384withECDSA。
  • 密鑰長度:RSA ≥2048 位(1024 已不安全),ECDSA P-256/P-384。
  • 內部 API 風險:sun.* 非標準,JDK 9+ 模組化後默認封裝,需 `--add-exports java.base/sun.security.x509=ALL-UNNAMED`,升級風險極大。**強烈建議遷移至 Bouncy Castle (BC)**。
  • 私鑰保護:PFX/JKS 密碼應高強度,生產環境使用 HSM/密碼管理器,嚴禁硬編碼。
  • 證書透明度:公網 TLS 證書需提交 CT 日誌,自簽 CA 不適用。

Bouncy Castle 遷移提示


// 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 等成熟庫、現代簽名算法、硬件保護私鑰、合規審計流程。**

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved