Java中不同类型的密钥库(Keystore) – 概述
密钥库是用于存储加密密钥和证书的存储工具 ,最常用于SSL通信,以证明服务器和客户端的身份。密钥库可以是文件或硬件设备。有三种类型的条目可以存储在密钥库中,取决于密钥库的类型,这三种类型的条目分别是:
PrivateKey:用于非对称加密的密钥,通常由于其敏感性而受密码保护。它还可用于签署数字签名;
Certificate证书:证书包含一个公钥,可以识别证书中声明的主题 (Subject)。它通常用于验证服务器的身份。有时,它还用于在请求时识别客户端的身份,如双向认证时;
SecretKey:在对称加密中使用的密钥条目;
根据密钥库可以存储的条目以及密钥库如何存储条目,Java中有几种不同类型的密钥库:JKS,JCEKS,PKCS12,PKCS11和DKS。最常见的是JKS,JCEKS,PKCS12类型,我们将主要介绍这三种。可以在Oracle的Java Cryptography Architecture描述中找到这些密钥库的介绍。
接下来,我们将简要介绍这些密钥库类型:
JKS,Java Key Store。可以参见sun.security.provider.JavaKeyStore类,此密钥库是特定于Java平台的,通常具有jks的扩展名。此类型的密钥库可以包含私钥和证书,但不能用于存储密钥。由于它是Java特定的密钥库,因此不能在其他编程语言中使用。存储在JKS中的私钥无法在Java中提取。
JCEKS,JCE密钥库(Java Cryptography Extension KeyStore)。可以认为是增强式的JKS密钥库,支持更多算法。可以参考com.sun.crypto.provider.JceKeyStore类,此密钥库具有jceks的扩展名。可以放入JCEKS密钥库的条目是私钥,密钥和证书。此密钥库通过使用Triple DES加密为存储的私钥提供更强大的保护。
JCEKS的提供者是SunJCE,它是在Java 1.4中引入的。因此,在Java 1.4之前,只能使用JKS。
PKCS12,一种标准的密钥库类型,可以在Java和其他语言中使用。可以参考sun.security.pkcs12.PKCS12KeyStore类。它通常具有p12或pfx的扩展名。可以在此类型上存储私钥,密钥和证书。与JKS不同,PKCS12密钥库上的私钥可以用Java提取。此类型是可以与其他语言(如C,C ++或C#)编写的其他库一起使用。
目前,Java中的默认密钥库类型是JKS,即如果在使用keytool创建密钥库时未指定-storetype,则密钥库格式将为JKS。但是,默认密钥库类型将在Java 9中更改为PKCS12,因为与JKS相比,它具有增强的兼容性。可以在$ JRE / lib / security / java.security文件中检查默认密钥库类型。
PKCS11,一种硬件密钥库类型。 为Java库提供了一个接口,用于连接硬件密钥库设备,如智能卡。 可以参考sun.security.pkcs11.P11KeyStore类。 此密钥库可以存储私钥,密钥和证书。 加载密钥库时,将从密钥库中检索条目,然后将其转换为软件成勋可识别的条目;
BKS,BoucyCastle密钥库,是一种密钥库格式,提供了流行的第三方Java加密库提供程序–BouncyCastle。它是一个类似于Oracle JDK提供的JKS的密钥库。支持存储密钥,私钥和证书,经常用于移动应用程序开发。
在Java中,有一些关于如何处理密钥库的选择。编写Java代码是一种选择。除此之外,还可以使用JDK附带的工具keytool;
keytool是一个命令行工具。它可用于创建密钥库,生成密钥,导入和导出证书等。有关keytool支持的命令的完整列表,可以参考Oracle keytool准则;
如果使用的是IBM JDK,还有一个可以使用的工具,它是ikeyman。 ikeyman是一个GUI工具,可以提供密钥库的直观视图。密钥库中的条目。也可以使用ikeyman创建密钥和证书。它是系统管理员经常使用的工具。
JKS,Java Key Store 详细介绍
JKS是Java Keystore,一种专为Java设计的专有密钥库类型。 可以用于存储用于SSL通信的私钥和证书,但是它不能存储密钥。 JDK附带的keytool无法提取存储在JKS上的私钥。 这种类型的密钥库通常具有jks的扩展名。
接下来,将展示如何使用Java代码操作JKS密钥库;
创建JKS密钥库
创建JKS密钥库以创建空密钥库的最简单方法。 可以先获取KeyStore的实例,然后加载null密钥库。 加载null密钥库后,只需要使用密钥库的密钥库名称和密码调用KeyStore.store();
以下是一个简单的演示:
try{
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null,null);
keyStore.store(new FileOutputStream("mytestkey.jks"), "password".toCharArray());
}catch(Exception ex){
ex.printStackTrace();
}
执行上述调用后,将在当前工作目录中看到名为mytestkey.jks的密钥库。 现在该密钥库是空的,没有任何条目。
存储私钥
现在将一个私钥及其关联的证书链存储到密钥库中。 注意,无法使用JDK将没有关联证书链的私钥存储到密钥库中。
try{
KeyStore keyStore = KeyStore.getInstance(“JKS”);
keyStore.load(new FileInputStream(“mytestkey.jks”),”password”.toCharArray());
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(“mykey”, key, “password”.toCharArray(), chain);
keyStore.store(new FileOutputStream(“mytestkey.jks”), “password”.toCharArray());
}catch(Exception ex){
ex.printStackTrace();
}
首先,将创建一个私钥和一个自签名证书,然后使用指定的别名,密钥,密钥及其相关证书链的密码调用KeyStore.setKeyEntry(),需要调用**KeyStore.store()**将密钥存储到密钥库中。
存储证书
可以在JKS密钥库上存储证书。 要存储的证书应该是X509证书格式。 可以存储在密钥库中,而无需关联的私钥。 此过程类似于存储私钥。
try{
KeyStore keyStore = KeyStore.getInstance(“JKS”);
keyStore.load(new FileInputStream(“mytestkey.jks”),”password”.toCharArray());
CertAndKeyGen gen = new CertAndKeyGen(“RSA”,”SHA1WithRSA”);
gen.generate(1024);
X509Certificate cert=gen.getSelfCertificate(new X500Name(“CN=SINGLE_CERTIFICATE”), (long)365*24*3600);
keyStore.setCertificateEntry(“single_cert”, cert);
keyStore.store(new FileOutputStream(“mytestkey.jks”), “password”.toCharArray());
}catch(Exception ex){
ex.printStackTrace();
}
加载私钥
存储密钥后,还可以在密钥库中加载条目,如加载私钥,如前所述,私钥不能使用Java从JKS中提取。 在本例中,实际上提取了私钥的证书链;
try{
KeyStore keyStore = KeyStore.getInstance(“JKS”);
keyStore.load(new FileInputStream(“mytestkey.jks”),”password”.toCharArray());
Key key = keyStore.getKey(“mykey”, “password”.toCharArray());
// System.out.println(“Private key : “+key.toString()); //You will get a NullPointerException if you uncomment this line
java.security.cert.Certificate[] chain = keyStore.getCertificateChain(“mykey”);
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: 90980299845597512779139009881469177009407272139633139241921529845092210461181243924599150259446249079941561941533303439718936138867375776965995893255358889228584415558006141961051402385279285497775776996780406808976543439543789816486513982581378223575354716191394304768315366544413052547926792470794374067383
public exponent: 65537
Validity: [From: Sat Sep 06 09:57:28 CST 2014,
To: Sun Sep 06 09:57:28 CST 2015]
Issuer: CN=ROOT
SerialNumber: [ 206b697b]
]
Algorithm: [SHA1withRSA]
Signature:
0000: 53 6A FD FE E6 3A 5E 6E A6 43 C4 F4 D1 56 D4 08 Sj…:^n.C…V..
0010: 7E 3B 8B 73 68 71 56 AB 96 FE 24 E7 2D DC 04 BB .;.shqV…$.-…
0020: 14 B0 C6 71 8D F0 3E EC FE D8 5B BB 8C 0F 55 63 …q..>…[…Uc
0030: 2B 38 8E 45 F1 2D F0 BB 8C 6D 13 A8 11 37 E1 FA +8.E.-…m…7..
0040: 77 AF C7 73 72 2B 40 4F 74 32 F6 3C 24 E6 AB ED w..sr+@Ot2.<$…
0050: 2C 6F 19 2E DC 58 5F CB 75 62 40 2F 3E BE 59 99 ,o…X_.ub@/>.Y.
0060: C0 1F 7A 70 15 AF C3 66 B3 4F C9 11 C3 45 59 EF ..zp…f.O…EY.
0070: 36 F4 1C C9 9B FA 5E 43 A0 28 DB 07 0D F2 53 6E 6…..^C.(….Sn
]
加载证书
这类似于加载私钥,需要传递想要提取的证书的别名;
try{
KeyStore keyStore = KeyStore.getInstance(“JKS”);
keyStore.load(new FileInputStream(“mytestkey.jks”),”password”.toCharArray());
java.security.cert.Certificate cert = keyStore.getCertificate(“single_cert”);
System.out.println(cert.toString());
}catch(Exception ex){
ex.printStackTrace();
}
输出为:
[
[
Version: V3
Subject: CN=SINGLE_CERTIFICATE
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
Key: Sun RSA public key, 1024 bits
modulus: 99756834215197288877309915243024788596281418171661241282881476656110879586349799740269767889529808199104172091786860877280382867461569439907754755558759387462421169749111354565793974372777424046360810758009149155148290676527032833774084635148674232352006810533640038723102562578516643345287042787777951043863
public exponent: 65537
Validity: [From: Sat Sep 06 10:14:33 CST 2014,
To: Sun Sep 06 10:14:33 CST 2015]
Issuer: CN=SINGLE_CERTIFICATE
SerialNumber: [ 6943e549]
]
Algorithm: [SHA1withRSA]
Signature:
0000: 35 58 70 96 F4 35 82 2A 95 9F BB 31 02 6E 7C 29 5Xp..5.*…1.n.)
0010: 4A FE AF EB 2D B5 3A A7 C7 9D 4C 9A 34 2C 5C 46 J…-.:…L.4,\F
0020: C2 82 A8 AC 1A C0 98 A5 67 21 74 7B 1E E2 E5 AC ……..g!t…..
0030: DE B2 1D 87 BE 16 45 9B D0 2A D3 2B F6 E1 4B 35 ……E..*.+..K5
0040: 27 8B A7 0A EF F2 07 41 90 A6 69 07 BE 87 C5 B1 ‘……A..i…..
0050: 54 DE DB A2 5A 41 47 3B 3F A7 74 6F 5C C8 8D B4 T…ZAG;?.to\…
0060: C8 65 2B 0F 8E 94 A8 80 C7 8B B5 78 FA C2 9C ED .e+……..x….
0070: 8E EC 28 E4 8E 62 A1 59 6A BC 37 7B 0D FC C7 AF ..(..b.Yj.7…..
]
导入密钥和证书
首先需要加载要导入证书的密钥库,然后加载另一个需要导入证书的密钥库。 接下来,需要从源密钥库获取证书并将其放入目标密钥库。
由于无法从JKS中提取私钥,因此只能将证书导入JKS。 但是可以从其他类型的密钥库(PKCS12)中提取私钥,然后将它们存储在JKS密钥库中;
需要注意的是Oracle提供了两个版本的JKS密钥库:区分大小写且不区分大小写。 调用KeyStore.getInstance(“JKS”)时,会创建一个不区分大小写的JKS实例版本,当调用KeyStore.getInstance(“CaseExactJKS”)时,将创建一个区分大小写的JKS实例版本。
Java中不同类型的密钥库 – JCEKS
JCEKS代表Java Cryptography Extension KeyStore,是Java平台的另一种密钥库格式。 在KeyStore中存储密钥是一种防止加密密钥泄漏的措施。 Java KeyStore安全地包含可由别名引用的证书和密钥,以便在Java程序中使用。
在JCEKS中存储和加载不同条目的过程与JKS类似。 所以请参阅Java中的不同类型的密钥库—JKS上一节。 在调用KeyStore.getInstance()时,相应地将密钥库类型从JKS更改为JCEKS。在JCEKS密钥库中存储密钥的过程。 密钥条目将被存储在密钥库中以保护密钥数据。 将条目存储到密钥库时,请提供密码。
存储密钥
密钥可以存储在JCEKS密钥库中,代码如下:
try{
KeyStore keyStore = KeyStore.getInstance(“JCEKS”);
keyStore.load(null, null);
KeyGenerator keyGen = KeyGenerator.getInstance(“DES”);
keyGen.init(56);;
Key key = keyGen.generateKey();
keyStore.setKeyEntry(“secret”, key, “password”.toCharArray(), null);
keyStore.store(new FileOutputStream(“output.jceks”), “password”.toCharArray());
} catch (Exception ex) {
ex.printStackTrace();
}
加载密钥
可以从Java中的JCEKS密钥库中提取存储的密钥。 提取的密钥可以用于正常加密/解密数据:
try{
KeyStore keyStore = KeyStore.getInstance(“JCEKS”);
keyStore.load(new FileInputStream(“output.jceks”), “password”.toCharArray());
Key key = keyStore.getKey(“secret”, “password”.toCharArray());
System.out.println(key.toString());
} catch (Exception ex) {
ex.printStackTrace();
}
输出为:
javax.crypto.spec.SecretKeySpec@fffe7b9b
Java中不同类型的密钥库 – PKCS12
PKCS12用于将加密对象,如密钥等,存储为单个文件, 它可用于存储密钥,私钥和证书。它是RSA实验室发布的标准化格式,它不仅可以用于Java,还可以用于C,C ++或C#等其他库。该文件格式 经常用于从其他密钥库类型导入和导出条目。
创建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)时,会传递两个空值作为输入密钥库流和密码。 运行此程序后,当前工作目录中应该有一个名为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();
}
存储私钥
私钥及其关联的证书链可以存储在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的私钥。 可以使用提供的正确密码正确提取私钥。
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……….
加载证书
通过调用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密钥库是推荐的密钥库类型。