Keystore介绍

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密钥库是推荐的密钥库类型。

上一篇:c++11 for (auto & item:v)


下一篇:docker-compose mongodb 副本