3

当您使用 Java Keytool 实用程序构建密钥库时,如何保护密钥?我通读了文档,我意识到每个私钥都有一个密钥密码,然后商店有一个商店密码。

但是使用什么机制来保护数据呢?它是加密密码吗?如果是这样,算法是什么?我特别关注 keytool 在构建 JKS 文件时如何进行保护。

4

3 回答 3

7

注意从 Java 9 开始的 Java 版本默认为 PKCS#12 密钥存储类型,而不是 JKS。


Sun 的默认 JKS 密钥库使用专有算法,主要是为了绕过对标准算法的导出限制。该算法在这个类中实现,

  sun.security.provider.KeyProtector

是算法的描述,

这是 Sun 专有的、可导出算法的实现,旨在保护(或恢复其明文版本)敏感密钥时使用。该算法不打算用作通用密码。这就是该算法用于密钥保护的方式: p - 用户密码 s - 随机盐 X - 异或密钥 P - 待保护密钥 Y - 受保护密钥 R - 存储在密钥库中的内容 第 1 步:获取用户密码,将随机盐(固定大小)附加到它,并对其进行散列: d1 = digest(p, s) 将 d1 存储在 X 中。 第 2 步:获取用户密码,附加上一步的摘要结果,并对其进行散列: dn = 摘要(p,dn-1)。将 dn 存储在 X 中(将其附加到先前存储的摘要中)。重复此步骤,直到 X 的长度与私钥 P 的长度匹配。步骤 3:XOR X 和 P,并将结果存储在 Y:Y = X XOR P。步骤 4:将 s、Y 和 digest(p, P) 存储在结果缓冲区 R 中:R = s + Y + digest(p, P),其中“+”表示连接。(注意:digest(p, P) 存储在结果缓冲区中,以便在恢复密钥时,我们可以检查恢复的密钥是否确实与原始密钥匹配。) R 存储在密钥库中。保护密钥恢复如下:Step1和Step2同上,只是salt不是随机生成的,而是取自step4的结果R(第一个length(s)个字节)。第 3 步(XOR 操作)产生明文密钥。然后将密码与恢复的密钥连接起来,并与R的最后一个length(digest(p, P))字节进行比较。如果匹配,则恢复的密钥确实与原始密钥相同。(注意:digest(p, P) 存储在结果缓冲区中,以便在恢复密钥时,我们可以检查恢复的密钥是否确实与原始密钥匹配。) R 存储在密钥库中。保护密钥恢复如下:Step1和Step2同上,只是salt不是随机生成的,而是取自step4的结果R(第一个length(s)个字节)。第 3 步(XOR 操作)产生明文密钥。然后将密码与恢复的密钥连接起来,并与R的最后一个length(digest(p, P))字节进行比较。如果匹配,则恢复的密钥确实与原始密钥相同。(注意:digest(p, P) 存储在结果缓冲区中,以便在恢复密钥时,我们可以检查恢复的密钥是否确实与原始密钥匹配。) R 存储在密钥库中。保护密钥恢复如下:Step1和Step2同上,只是salt不是随机生成的,而是取自step4的结果R(第一个length(s)个字节)。第 3 步(XOR 操作)产生明文密钥。然后将密码与恢复的密钥连接起来,并与R的最后一个length(digest(p, P))字节进行比较。如果匹配,则恢复的密钥确实与原始密钥相同。Step1和Step2同上,只是salt不是随机生成的,而是取自step4的结果R(第一个length(s)个字节)。第 3 步(XOR 操作)产生明文密钥。然后将密码与恢复的密钥连接起来,并与R的最后一个length(digest(p, P))字节进行比较。如果匹配,则恢复的密钥确实与原始密钥相同。Step1和Step2同上,只是salt不是随机生成的,而是取自step4的结果R(第一个length(s)个字节)。第 3 步(XOR 操作)产生明文密钥。然后将密码与恢复的密钥连接起来,并与R的最后一个length(digest(p, P))字节进行比较。如果匹配,则恢复的密钥确实与原始密钥相同。

于 2009-12-23T23:10:47.273 回答
3

使用的算法取决于您使用的密钥库(例如,它可能是智能卡)。

Sun 随 JDK 一起提供的默认密钥库会创建一个具有三个加密选项的软令牌(在磁盘文件上) :

  1. 默认值:“jks”,专有密钥库类型(格式)。不确定算法。

  2. “jceks”,另一种专有格式,使用 3-DES

  3. “pkcs12”,一种标准格式(OpenSSL 可以读取),有多个选项,但通常是 3-DES 用于私钥,RC2-40 用于证书。

在所有这三种情况下,您都对私有数据进行了加密(对称地,使用单独的密码),并且整个密钥库的完整性受到加密摘要的保护(使用密钥库密码)。

于 2009-12-23T22:36:54.257 回答
1

Java 9-15:PFX 代替 JKS

从版本 9 开始的 Java 版本不再使用专有的 JKS 密钥存储类型。相反,它们默认使用 PKCS#12 密钥存储类型,也称为 PFX 文件。

这些密钥库受单独的 PBKDF 密钥派生机制保护,用于密钥库完整性、私钥和证书。

使用的算法

默认情况下,使用 HMAC-SHA-1 保护整个密钥存储的完整性,尽管使用了可以生成冲突的哈希算法,但它仍然是一种安全的。同样,这可能不是您想告诉审计员您仍在使用的算法。

密钥本身使用 3 密钥三重 DES 进行保护。同样的问题在这里出现,因为 3 密钥三重 DES 在这些场景中仍然提供大约 112 位的安全性。现在您当然更愿意使用 AES-256,尤其是在保护私钥方面。

证书使用 40 位 RC2 进行“保护”。基本上我称之为混淆而不是实际加密。幸运的是,整个密钥库的完整性无论如何都受到保护,并且证书通常被视为“公共”。但是,如果您(仍然)认为切换公钥和私钥是一个好主意,那么您将面临(另一个)大惊喜。

基于密码的加密 (PBE)

PBE 意味着用于提供(完整性和)机密性的密钥是根据密码计算的。一旦攻击者掌握了密钥存储文件,就可以使用“离线”攻击来攻击这些密码。

基于密码的加密安全性主要取决于所使用的密码或密码短语的安全性。如果它只有 6 个字符(keytool似乎可以接受的最小值),那么通常不提供安全性。如果每个字符的熵是 6 位,那么整个安全性就是 6 x 6 = 36 位。

然而,PBKDF 确实指定了在将密码和盐用作密钥之前对其进行哈希处理的迭代次数。这增加了一点安全性。但是,keytool仅使用最少 50,000 次迭代,而目前建议至少进行 100 万次迭代。50,000 次迭代为总数增加了另外 15/16 位的安全性。这很好,但是使用完全随机的 6 个字符的密码,你仍然会被困在 50 位的安全性上。

更糟糕的是,PKCS#12 似乎使用了更多的迭代来计算 HMAC、3DES 和 RC2 密钥的各种密钥。这意味着普通用户实际上必须为每个密码执行更多的工作来计算密钥,而攻击者只需要计算 RC2 40 位密钥来验证猜测是否正确。除此之外,Java 的 PBKDF 实现可能绝对不是目前最快的。最后,您不能真正依赖 PBKDF 来为使用的密码增加很多安全性。幸运的是,密钥库只加载一次;之后它被存储在内存中。

最后,PFX 密钥存储格式非常过时,JavakeytoolPKCS12KeyStore实现也是如此。保持合理安全的唯一方法是使用密码生成器和密码存储(例如,KeyPass`)来生成一个非常强大的密码。它建议使用字母数字字母(大写、小写和数字)的 12 个随机字符。这提供了大约 12 x ~6 + ~16 = ~88 位的安全性。

证据

因此,让我们验证这一切:

keytool -genkeypair -alias test -keyalg RSA -keysize 4096 -sigalg SHA256withRSA -keystore test.pfx

进而

openssl pkcs12 -info -in test.pfx

结果是:

MAC: sha1, Iteration 100000
MAC length: 20, salt length: 20
PKCS7 Data
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 50000
Bag Attributes
    friendlyName: test
    localKeyID: 54 69 6D 65 20 31 36 31 34 39 34 35 39 34 35 38 32 33 
Key Attributes: <No Attributes>
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----BEGIN ENCRYPTED PRIVATE KEY-----
<PKCS8ShroudedKeyBag in base 64>
-----END ENCRYPTED PRIVATE KEY-----
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 50000
Certificate bag
Bag Attributes
    friendlyName: test
    localKeyID: 54 69 6D 65 20 31 36 31 34 39 34 35 39 34 35 38 32 33 
subject=C = Unknown, ST = Unknown, L = Unknown, O = Unknown, OU = Unknown, CN = Test

issuer=C = Unknown, ST = Unknown, L = Unknown, O = Unknown, OU = Unknown, CN = Test

-----BEGIN CERTIFICATE-----
<base 64 encoded self-signed certificate>
-----END CERTIFICATE-----

不要假设安全

您想更好地保护您的密钥,然后请联系安全顾问。正如我们在这里展示的那样,全世界都在使用的事实keytool并不一定能保证它的安全。这个问题(甚至)比这里回答的要多。

于 2021-03-05T12:44:45.393 回答