PKCS12 是一种用于将密码对象存储为单个文件的活动文件格式。它可以用于存储密钥、私钥和证书。它是由RSA实验室发布的标准化格式,这意味着它不仅可以在Java中使用,也可以在C、C++或C#等其他库中使用。此文件格式经常用于从其他密钥库类型导入和导出条目。
接下来我们将解释可以在PKCS12密钥库上执行的操作。
创建PKCS12密钥库
在将条目存储到PKCS12密钥库之前,必须先加载密钥库。这意味着我们必须首先创建一个密钥库。创建PKCS12密钥库最简单的方法是:
try{
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null, null);
keyStore.store(new FileOutputStream("output.p12"), "password".toCharArray());
} catch (Exception ex){
ex.printStackTrace();
}
注意,当调用keyStore.load(null, null)时,输入密钥库流和密码传递两个null值。这是因为我们还没有密钥库可用。运行此程序后,当前工作目录中应该有一个名为output.p12的密钥库文件。
存储密钥
PKCS12允许在有限的基础上存储密钥。密钥经常用于加密/解密数据。为了方便地传输密钥,可以将它们存储在像PKCS12这样的密钥库中并进行传输。
try{
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(null, null);
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
Key key = keyGen.generateKey();
keyStore.setKeyEntry("secret", key, "password".toCharArray(), null);
keyStore.store(new FileOutputStream("output.p12"), "password".toCharArray());
} catch (Exception ex){
ex.printStackTrace();
}
一些使用AES算法存储在PKCS12密钥库中的密钥无法在Java中提取。由于PKCS12是一个可移植的标准,其他库可能支持提取密钥。
存储私钥
私钥及其关联的证书链可以存储在PKCS12密钥库中。包含私钥和证书的密钥库可用于Web上的SSL通信。
try{
KeyStore keyStore = KeyStore.getInstance("PKCS12");
// keyStore.load(new FileInputStream("output.p12"),"password".toCharArray());
keyStore.load(null, null);;
CertAndKeyGen gen = new CertAndKeyGen("RSA","SHA1WithRSA");
gen.generate(1024);
Key key=gen.getPrivateKey();
X509Certificate cert=gen.getSelfCertificate(new X500Name("CN=ROOT"), (long)365*24*3600);
X509Certificate[] chain = new X509Certificate[1];
chain[0]=cert;
keyStore.setKeyEntry("private", key, "password".toCharArray(), chain);
keyStore.store(new FileOutputStream("output.p12"), "password".toCharArray());
}catch(Exception ex){
ex.printStackTrace();
}
使用CertAndKeyGen生成RSA私钥,并生成关联的证书。然后通过调用keyStore.setEntry()将密钥条目存储在keyStore中。不要忘记通过调用keyStore.store()保存keyStore,否则程序退出时条目将丢失。
存储证书
PKCS12密钥库还允许仅存储证书本身,而不存储相应的私钥。要存储证书,可以调用KeyStore.setCertificateEntry()。
try{
KeyStore keyStore = KeyStore.getInstance("PKCS12");
// keyStore.load(new FileInputStream("output.p12"),"password".toCharArray());
keyStore.load(null, null);;
CertAndKeyGen gen = new CertAndKeyGen("RSA","SHA1WithRSA");
gen.generate(1024);
X509Certificate cert=gen.getSelfCertificate(new X500Name("CN=ROOT"), (long)365*24*3600);
keyStore.setCertificateEntry("cert", cert);
keyStore.store(new FileOutputStream("output.p12"), "password".toCharArray());
}catch(Exception ex){
ex.printStackTrace();
}
可以通过使用提供的别名调用KeyStore.getCertificate()来提取存储的证书。例如:
Certificate cert = keyStore.getCertificate("cert");
加载私钥
PKCS12密钥库与其他密钥库(例如JKS)之间的一个区别在于,PKCS12的私钥可以在没有NullPointerException的情况下提取。可以使用正确的密码正确提取私钥。
try{
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(new FileInputStream("output.p12"), "password".toCharArray());
Key pvtKey = keyStore.getKey("private", "password".toCharArray());
System.out.println(pvtKey.toString());
} catch (Exception ex){
ex.printStackTrace();
}
以上代码的输出是:
sun.security.rsa.RSAPrivateCrtKeyImpl@ffff2466
加载证书链
如果证书链存储在密钥库中,则可以通过调用KeyStore.getCertificateChain()来加载它。以下代码用于提取与关联私钥关联的关联证书链。
try{
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(new FileInputStream("output.p12"), "password".toCharArray());
Key pvtKey = keyStore.getKey("private", "password".toCharArray());
System.out.println(pvtKey.toString());
java.security.cert.Certificate[] chain = keyStore.getCertificateChain("private");
for(java.security.cert.Certificate cert:chain){
System.out.println(cert.toString());
}
} catch (Exception ex){
ex.printStackTrace();
}
输出为:
[
[
Version: V3
Subject: CN=ROOT
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
Key: Sun RSA public key, 1024 bits
modulus: 107262652552256813768678166856978781385254195794582600239703451044252881438814396239031781495369251659734172714120481593881055888193254336293673302267462500060447786562885955334870856482264000504019061160524587434562257067298291769329550807938162702640388267016365640782567817416484577163775446236245223552189
public exponent: 65537
Validity: [From: Mon Jan 05 13:03:29 SGT 2015,
To: Tue Jan 05 13:03:29 SGT 2016]
Issuer: CN=ROOT
SerialNumber: [ 5e5ca8a4]
]
Algorithm: [SHA1withRSA]
Signature:
0000: 22 21 BF 73 A6 6D 12 9B F7 49 6C 0E B3 50 6A 9D "!.s.m...Il..Pj.
0010: FA 30 43 22 32 FF 54 95 80 2E B3 8B 6F 59 D4 B5 .0C"2.T.....oY..
0020: 6C A6 AE 89 B7 18 9A A8 35 7D 65 37 BF ED A3 F4 l.......5.e7....
0030: E7 DB 5D 5F 9B DA 4B FA 39 04 9B 4D DB C2 3E FA ..]_..K.9..M..>.
0040: 3B C2 63 F8 1E BE 03 F3 BD 1C D4 8A 8E 3C 51 68 ;.c..........
要了解如何在Java中创建证书链,请参考在Java中生成证书——证书链。
加载证书
如果提供的别名映射到证书链,则通过调用KeyStore.getCertificate()加载证书也很简单,只会返回叶子证书。
try{
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(new FileInputStream("output.p12"), "password".toCharArray());
java.security.cert.Certificate cert = keyStore.getCertificate("private");
System.out.println(cert);
} catch (Exception ex){
ex.printStackTrace();
}
输出如下所示:
[
[
Version: V3
Subject: CN=ROOT
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
Key: Sun RSA public key, 1024 bits
modulus: 107262652552256813768678166856978781385254195794582600239703451044252881438814396239031781495369251659734172714120481593881055888193254336293673302267462500060447786562885955334870856482264000504019061160524587434562257067298291769329550807938162702640388267016365640782567817416484577163775446236245223552189
public exponent: 65537
Validity: [From: Mon Jan 05 13:03:29 SGT 2015,
To: Tue Jan 05 13:03:29 SGT 2016]
Issuer: CN=ROOT
SerialNumber: [ 5e5ca8a4]
]
Algorithm: [SHA1withRSA]
Signature:
0000: 22 21 BF 73 A6 6D 12 9B F7 49 6C 0E B3 50 6A 9D "!.s.m...Il..Pj.
0010: FA 30 43 22 32 FF 54 95 80 2E B3 8B 6F 59 D4 B5 .0C"2.T.....oY..
0020: 6C A6 AE 89 B7 18 9A A8 35 7D 65 37 BF ED A3 F4 l.......5.e7....
0030: E7 DB 5D 5F 9B DA 4B FA 39 04 9B 4D DB C2 3E FA ..]_..K.9..M..>.
0040: 3B C2 63 F8 1E BE 03 F3 BD 1C D4 8A 8E 3C 51 68 ;.c..........
导入和导出密钥和证书
PKCS12密钥库可用于导入和导出密钥和证书。由于可以从PKCS12密钥库中提取私钥,因此可以从PKCS12密钥库中导出条目,然后将其导入到其他密钥库类型(例如JKS)。
以下代码片段演示了从PKCS12导出私钥条目并将其导入到JSK密钥库:
try{
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(new FileInputStream("output.p12"), "password".toCharArray());
Key pvtKey = keyStore.getKey("private", "password".toCharArray());
java.security.cert.Certificate[] chain = keyStore.getCertificateChain("private");
KeyStore jksStore = KeyStore.getInstance("JKS");
jksStore.load(null, null);;
jksStore.setKeyEntry("jksPrivate", pvtKey, "newpassword".toCharArray(), chain);
jksStore.store(new FileOutputStream("output.jks"), "password".toCharArray());
} catch (Exception ex){
ex.printStackTrace();
}
如果您想要一个可移植的密钥库类型,并且将来可能希望将其与其他非Java库一起使用,则建议使用PKCS12密钥库类型。
有关其他密钥库的更多信息,请参考Java中不同类型的密钥库——概述
How can I choose the extension, p12 or pfx, for a PKCS12 keystore? Is there a mandatory rule?
Another question about the KeyStore.setXXX() method. Why does it use setXXX() method name instead of addXXX() to add new entry? Any special thought?