创建 AES 密钥比播种 SecureRandom 更好的方法

Posted

技术标签:

【中文标题】创建 AES 密钥比播种 SecureRandom 更好的方法【英文标题】:Better way to create AES keys than seeding SecureRandom 【发布时间】:2014-07-30 05:14:32 【问题描述】:

我需要将加密数据从Java 客户端发送到C# 服务器。现在我正在学习如何使用AES(要求)加密数据。按照这个公认的答案android encryption/decryption with AES 我正在做以下事情:

byte[] keyStart = "qweroiwejrwoejlsifeoisrn".getBytes(); // Random character string

byte[] toEncrypt = myMessageString.getBytes();

keyGen = KeyGenerator.getInstance("AES");
sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(keyStart);
keyGen.init(128, sr);
SecretKey secretKey = keyGen.generateKey();
byte[] secretKeyByte = secretKey.getEncoded();

SecretKeySpec skeySpec = new SecretKeySpec(secretKeyByte, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
cipher.doFinal(toEncrypt);

由于算法使用SecureRandom 使用keyStart,我不确定这是否可以在C# 甚至另一个Java 程序中解码,而无需SecureRandom

这种加密/解密是否只知道keyStart 的值,或者因为我使用SecureRandom 我仍然需要传递其他东西才能解密?

另外,有没有更好的方法,或者这个就可以了?

【问题讨论】:

【参考方案1】:

不,您应该使用 SecureRandom 从静态数据派生密钥的整个想法相当糟糕:

    SecureRandom的主要功能是生成随机值,不应该用作密钥流的生成器; SecureRandom,当用 "SHA1PRNG" 实例化时,没有实现定义明确的算法,而且实际上已知算法会发生变化,即使从一个 Sun JDK 到另一个; Oracle 提供的"SHA1PRNG" 实现将初始种子用作种子,其他人可能只是将种子添加到随机池中。

已知使用"SHA1PRNG" 作为密钥派生函数会在多个 Android 版本上产生问题,并且可能会在任何其他 Java RE 上失败。


那么你应该怎么做呢?

    使用new SecureRandom() 甚至更好的KeyGenerator 来生成真正的随机密钥,如果您需要全新的随机密钥,则无需播种随机数生成器; 直接将已知密钥的byte[] 提供给SecretKeySpec,或使用十六进制解码器将其解码为十六进制(注意String 实例很难从内存中删除,因此只有在没有其他方式); 如果您想从密码创建密钥,请使用 PBKDF2(但使用比链接中提供的迭代次数更高的迭代次数); 如果您想从一个密钥种子创建多个密钥,请使用真正的基于密钥的密钥派生机制,例如使用 HKDF(见下文)。

如果种子是由例如生成的,选项 4 将是首选。密钥协商算法,例如 Diffie-Hellman 或 ECDH。


请注意,对于选项 3,PBKDF2,您最好只使用 ASCII 密码。这是因为 Oracle 的 PBKDF2 实现不使用 UTF-8 编码。


至于选项 4,我已帮助将所有好的 KBKDF 添加到 Bouncy Castle libraries,因此如果您可以将 Bouncy Castle 添加到您的类路径和/或已安装安全性列表,则无需自己实现 KBKDF提供者。目前最好的 KBKDF 可能是 HKDF。如果您无法将 Bouncy Castle 添加到您的类路径中,那么您可能希望在派生数据上使用 SHA-256 输出的最左边字节作为“穷人的”KDF。

【讨论】:

以上是关于创建 AES 密钥比播种 SecureRandom 更好的方法的主要内容,如果未能解决你的问题,请参考以下文章

JOSEException:无法创建 AES/GCM/NoPadding 密码:非法密钥大小

关于 java中的SecureRandom在linux中每次生成不同结果

SecureRandom 提供程序“Crypto”在 Android N 中无法确定性地生成密钥

如何使用 AES 加密创建 Java 密钥库 (.jks) 文件

AES DES加密有啥区别啊?

aes算法填充方式