keytool 如何保护密钥?

Posted

技术标签:

【中文标题】keytool 如何保护密钥?【英文标题】:How does keytool protect keys? 【发布时间】:2010-12-29 15:53:49 【问题描述】:

当您使用 Java Keytool 实用程序构建密钥库时,如何保护密钥?我通读了文档,发现每个私钥都有一个key密码,然后store就有了store密码。

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

【问题讨论】:

【参考方案1】:

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

Sun 随 JDK 提供的默认密钥库会创建一个带有 three encryption options 的软令牌(在磁盘文件上):

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

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

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

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

【讨论】:

赞成,但也添加了更彻底/深入的答案,您可能会感兴趣。【参考方案2】:

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


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

  sun.security.provider.KeyProtector

是算法的描述,

这是一个 Sun 专有的、可导出算法的实现,用于保护(或恢复其明文版本)敏感密钥。该算法不打算用作通用密码。这就是算法如何用于密钥保护: p - 用户密码 s - 随机盐 X - xor 密钥 P - 待保护密钥 Y - 受保护密钥 R - 存储在密钥库中的内容 第 1 步:获取用户密码,附加一个随机盐(固定大小),并对其进行散列: d1 = digest(p, s) 将 d1 存储在 X 中。 第 2 步:获取用户的密码,附加上一步的摘要结果,并对其进行散列: dn = 摘要(p,dn-1)。将 dn 存储在 X 中(将其附加到先前存储的摘要中)。重复此步骤,直到 X 的长度与私钥 P 的长度匹配。 Step 3:XOR X 和 P,并将结果存储在 Y 中:Y = X XOR P。 Step 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))字节进行比较,如果匹配,则恢复的密钥确实与原始密钥相同。 p>

【讨论】:

【参考方案3】:

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 密钥存储格式已经过时了,Java 的 keytoolPKCS12KeyStore 实现也是如此。保持合理安全的唯一方法是使用密码生成器和密码存储(例如,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 的事实并不一定能保证它的安全。这个问题(甚至)比这里回答的要多。

【讨论】:

以上是关于keytool 如何保护密钥?的主要内容,如果未能解决你的问题,请参考以下文章

Java结合keytool实现非对称加密和解密

如何使用 keytool 在 PKCS12 密钥库中创建证书?

如何使用keytool将证书创建到PKCS12密钥库?

信任存储与密钥存储 - 使用 keytool 创建

如何使用 keytool 列出存储在 PKCS12 密钥库中的证书?

如何在 Keytool 中使用 pem 和密钥文件生成 p12 文件