【问题标题】:Java PBKDF2WithHmacSHA1 issueJava PBKDF2WithHmacSHA1 问题
【发布时间】:2014-01-05 19:55:15
【问题描述】:

我有三个应用程序(.NET、Android、Java)。所涉及的加密由三个应用程序之一生成消息以及密钥、IV 和盐,以传递给其他两个。 .Net Android 完美适用于非对称和对称加密/解密,以及输出相同值的密钥生成函数。普通的 Java 版本给我带来了问题。

密钥、iv 和 salt 是使用 RNG 生成的,例如

KeyGenerator kg = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
kg.init(len * 8, sr);
return kg.generateKey().getEncoded();

然后将其转换为要传递的十六进制字符串。当我在 Android 或 Java 上进行解密时,我将密钥的十六进制表示转换回 byte[] 并将其传递给如下所示的 SecretKey 函数。

public void SecretKey(byte[] key) {
    char[] ca = null;
    PBEKeySpec keySpec = null;
    SecretKeyFactory factory = null;

    _secretKey = null;
    try {
            ca = new char[key.length];  
            for (int x = 0; x < key.length; x++) {
                    ca[x] = (char)(key[x]);
            }
            keySpec = new PBEKeySpec(ca, _salt, 1024, 256);
            factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            _secretKey = factory.generateSecret(keySpec);
    .
    .
    .

现在,在非 Android Java 版本上,我可以验证密码和 salt 是否与 .NET 和 Android 版本逐字节地相同,因此似乎不存在转换问题。但是,getEncoded() 键输出不同。

.Net
PW: 228 24 51 237 189 94 87 183 124 144 173 217 195 24 106 199 55 30 74 93 7 206 159 156 245 51 6 131 123 230 255 21 
Salt: 87 241 248 158 129 101 47 36 255 31 30 26 211 50 204 156 
Key: 204 26 176 226 255 40 25 163 60 85 75 208 230 192 214 150 136 5 155 55 228 199 98 36 230 84 210 6 164 113 48 128 

Java:
PW: 228 24 51 237 189 94 87 183 124 144 173 217 195 24 106 199 55 30 74 93 7 206 159 156 245 51 6 131 123 230 255 21 
Salt: 87 -15 -8 -98 -127 101 47 36 -1 31 30 26 -45 50 -52 -100 
Key: -55 6 -4 80 96 9 85 18 72 -43 24 -48 -48 94 17 -113 74 108 -124 -118 -42 -29 -83 -88 -70 11 47 -4 4 -108 11 17 

我使用的是 JDK1.7.0_45,安装了非限制性策略文件,同时使用策略和代码路径将 BouncyCastle 1.50 设置为首选提供程序。

在 Android 上,我使用的是 SpongyCastle,而 .Net 只是使用标准的 Microsoft CryptAPI。

任何帮助将不胜感激。谢谢。

【问题讨论】:

  • 您可以使用new BigInteger(1, bytes).toString(16); 或org.bouncycastle.util.encoders.Hex 之类的东西来提高字节数组的可读性。
  • 问题在于 Java 中的 PBKDF 功能并不是真正为处理字节而设计的。这实际上很愚蠢,因为它使您无法使用自己的字符编码。要使上述代码正常工作(我不知道它应该做什么),请确保密码库 KDF 的输入使用相同的密码。
  • 密钥派生函数主要用于基于密码的加密。您正在使用随机生成的密钥——在这种情况下,通过 PBKDF2 运行它们并不会真正添加任何内容。
  • @owlstead - 同意不处理字节的愚蠢。但就像我的帖子所说的那样,我已经验证了输入以确保它们是相同的。
  • 通过散列函数运行输入不会增加任何熵 (crypto.stackexchange.com/questions/4037/…)。昂贵的密钥派生函数的目的是使某些暴力攻击更加困难。这种攻击对随机生成的密钥是不合理的。我还想指出,SecureRandom 很可能对此“足够随机”,您不必担心。

标签: java android .net cryptography bouncycastle


【解决方案1】:

问题源于这样一个事实,即 PBKDF2 旨在用于密码(文本)而不是密钥(不形成有效文本编码的随机字节序列)。

按照 PKCS #5 中的建议,密钥派生的第一步应该是使用 UTF-8 编码将密码文本转换为字节。 Java 做到了这一点,并且使用帖子中给出的特定键,编码是一个 48 字节的序列。但是,旧版本的 Android(可能还有 .NET)不使用 UTF-8 编码。他们只是丢弃每个字符的低 8 位以外的所有位,从而为您提供帖子中提供的 32 字节序列。

最新的 Android 版本(如 Java)使用 UTF-8 执行字符到字节的转换,并且需要使用不同的密钥派生算法才能与旧的 Android 版本互操作。

在此处不尝试使用 PBKDF2 可以避免这些问题。它并没有提高安全性,它使您的代码难以编写和阅读,并且它引入了许多互操作性陷阱。一切都是徒劳的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-31
    • 2013-10-21
    • 1970-01-01
    • 2021-08-10
    • 2013-11-20
    • 2014-12-06
    • 2016-03-31
    • 2014-06-11
    相关资源
    最近更新 更多