如何在 Java 中创建安全的随机 AES 密钥?

Posted

技术标签:

【中文标题】如何在 Java 中创建安全的随机 AES 密钥?【英文标题】:How to create a secure random AES key in Java? 【发布时间】:2013-08-16 05:16:01 【问题描述】:

使用标准 JDK 在 Java 中生成安全、随机 AES 密钥的推荐方法是什么?

在其他帖子中,我发现了这一点,但使用 SecretKeyFactory 可能是一个更好的主意:

KeyGenerator keyGen = KeyGenerator.getInstance("AES");
SecureRandom random = new SecureRandom(); // cryptograph. secure random 
keyGen.init(random); 
SecretKey secretKey = keyGen.generateKey();

如果答案包括解释为什么它是生成随机密钥的好方法,那就太好了。谢谢!

【问题讨论】:

这可能会帮助***.com/questions/10252449/is-aes-key-random @Tala 那是我找到cited code 的地方。但从那篇文章中,我无法就如何创建随机密钥以及为什么它是一种安全方式达成共识。 【参考方案1】:

我会使用您建议的代码,但稍微简化一下:

KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256); // for example
SecretKey secretKey = keyGen.generateKey();

让提供者选择它计划如何获得随机性 - 不要定义可能不如提供者已经选择的东西。

此代码示例假定 (as Maarten points out below) 您已将 java.security 文件配置为在列表顶部包含您的首选提供商。如果您想手动指定提供程序,只需调用KeyGenerator.getInstance("AES", "providerName");

要获得真正安全的密钥,您需要使用hardware security module (HSM) 来生成和保护密钥。 HSM 制造商通常会提供一个 JCE 提供程序,使用上面的代码为您生成所有密钥。

【讨论】:

通常我也会省略提供者名称。配置平台(例如,使用java.security 文件或使用Security.addProvider() 以编程方式配置Provider 等)。否则代码的可移植性会降低,并且不会让用户更改为例如无需更改应用程序代码的 HSM。 为什么需要指定提供者?默认行为是使用支持该算法的最高优先级已安装提供程序,这对我来说似乎足够了。 并非所有密钥生成方法都是平等的,您可能需要明确选择例如提供者的密钥生成方法。这尤其适用于安全令牌的提供者。不过,对于 AES,随机数生成器可能更重要 - 例如,您可能希望使用更慢、更安全、经 FIPS 认证的随机数生成器,而不是默认的。 keyGen.init(256); 有什么区别?或 keyGen.init(128); ? @HemanthPeela 它定义了要生成的密钥的长度。 256 位密钥比 128 位密钥强。【参考方案2】:

使用KeyGenerator 将是首选方法。正如邓肯所说,我肯定会在初始化期间给出密钥大小。 KeyFactory 是一种应该用于预先存在的密钥的方法。

好的,让我们来看看这个的本质。原则上,AES 密钥可以具有任何值。 (3)DES 中没有“弱密钥”。也没有像 (3)DES 奇偶校验位那样具有特定含义的位。所以生成一个密钥可以像生成一个带有随机值的字节数组一样简单,然后在它周围创建一个SecretKeySpec

但您使用的方法仍有优势:KeyGenerator 专门用于生成密钥。这意味着代码可能会针对这一代进行优化。这可能具有效率和安全优势。例如,它可能被编程以避免会暴露密钥的定时侧信道攻击。请注意,清除任何包含关键信息的byte[] 可能已经是一个好主意,因为它们可能会泄漏到交换文件中(尽管如此)。

此外,如上所述,并非所有算法都使用完全随机的密钥。所以使用KeyGenerator 会更容易切换到其他算法。不过,更现代的密码将只接受完全随机的密钥;这被认为是一个主要的好处,例如DES。

最后,就我而言,最重要的原因是,KeyGenerator 方法是在安全令牌(智能卡、TPM、USB 令牌或 HSM)中处理 AES 密钥的唯一有效方法。如果您使用SecretKeySpec 创建byte[],则密钥必须来自内存。这意味着密钥可以放在安全令牌中,但无论如何密钥都会暴露在内存中。通常,安全令牌仅适用于在安全令牌中生成或由例如注入的密钥。智能卡或钥匙仪式。 KeyGenerator 可以与提供程序一起提供,以便直接在安全令牌中生成密钥。

如Duncan's answer 中所述:始终明确指定密钥大小(和任何其他参数)。不要依赖提供者的默认值,因为这使你的应用程序在做什么变得不清楚,而且每个提供者可能有自己的默认值。

【讨论】:

【参考方案3】:

其他帖子中有很多好的建议。这是我使用的:

Key key;
SecureRandom rand = new SecureRandom();
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(256, rand);
key = generator.generateKey();

如果您需要另一个随机性提供程序(我有时会出于测试目的这样做),只需将 rand 替换为

MySecureRandom rand = new MySecureRandom();

【讨论】:

我第一次指定随机生成器,第二次指定要使用的 AES 密钥的长度(256)。它们是独立的初始化方法。

以上是关于如何在 Java 中创建安全的随机 AES 密钥?的主要内容,如果未能解决你的问题,请参考以下文章

aes加密安全吗

如果我用随机密钥加密字符串,如何解密它

Android Okhttp/Retrofit网络请求加解密实现方案

如何使用我们在 SD 卡中创建的密钥(兼容全球平台的智能卡)

在 C# 中创建加密随机数的最快、线程安全的方法?

如何在clojure中创建一个惰性随机数序列