PBKDF2WithHmacSHA1 密钥生成在 Android 上花费的时间太长

Posted

技术标签:

【中文标题】PBKDF2WithHmacSHA1 密钥生成在 Android 上花费的时间太长【英文标题】:PBKDF2WithHmacSHA1 key generation Takes too long on Android 【发布时间】:2014-08-30 09:57:58 【问题描述】:

我想使用 PBKDF2WithHmacSHA1 生成密钥,但是在 android 上计算需要很长时间。我在 ios 上使用相同数量的迭代和普通加密,大约需要 6 秒,而在 android 上需要 100 秒。

代码如下:

public static String generateStorngPasswordHash(String password)

    try
    
        char[] chars = password.toCharArray();
        byte[] salt = getSalt();

        PBEKeySpec spec = new PBEKeySpec(chars, salt, 1010101, 32 * 8);
        SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        byte[] hash = skf.generateSecret(spec).getEncoded();

        return toHex(salt) + ":" + toHex(hash);
     catch (Exception e)
    
        Logger.e("Exception: Error in generating password" + e.toString());
    
    return "";


private static byte[] getSalt() throws NoSuchAlgorithmException

    SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
    byte[] salt = new byte[32];
    sr.nextBytes(salt);
    return salt;

如果此代码有任何问题,请告诉我?

编辑

还有一件事,我也在我的应用程序中使用 sqlCipher。他们使用 openssl 来计算 PKDF2,我在某处读到 openssl 实现比 java 实现更快地找到 PKDF2。所以我的问题是:

    openssl 可以在 android 上给我更好的性能,这是真的吗? 我可以使用 sqlCipherg 中使用的 openssl 实现吗? 如果是,我如何通过 openssl 使用 caclulate PKDF2? 如果否,我如何使用 openssl 在 android 中查找 PBKDF2WithHmacSHA1。我有 在网上搜索但没有找到任何示例。

【问题讨论】:

该代码没有问题。这是一个小时内关于慢速 android 加密的第二个问题,请查看the first and questions linked to it。 您使用什么安卓手机进行测试?希望你不是在谈论模拟器...... 你确定iOS代码是等价的吗?在 3.6GHz Core i5 上的 J2SE 中执行 generateStorngPasswordHash() 大约需要 2.2 秒... 我正在三星 Galaxy S4 上测试这个东西。在 iOS 上,我使用的是 CommonCrypto。有没有其他方法可以加快速度? 【参考方案1】:

是的,在循环执行 SHA 方面,Java 比(Objective)C 慢得多。与编译语言相比,Java 在加密方面并不是特别快。另一方面,它允许相对安全的代码。所以你的结果不会让我感到震惊。除非您可以使用另一个更快的使用本机代码的提供程序,否则您应该期望得到这样的结果。

但是,有一些方法可以减轻正在发生的事情。例如,您可以减少迭代次数。更高的数字更安全,但它们只会帮助您线性增加攻击者的工作量。采取其他措施,例如有限的尝试次数、更安全的数据库或要求更好的密码短语,可能比仅仅增加迭代次数提供更好的安全性。如果可能,不要过度依赖 PBKDF2 提供的密钥强化。

此外,您正在向 PBKDF2 请求 32 个字节。由于 PBKDF2 内部循环的单次运行的输出仅产生 20 字节输出(SHA-1 创建 160 位散列),该循环运行两次,一次用于最初的 20 字节,然后再次用于剩下的 12 个字节。如果攻击者只需要最初的 16 个字节来验证密码,那么你的工作量是攻击者的两倍。如果您只是将其用作密码哈希,则只需要求输出 20 个字节。如果您需要 32 字节的密钥或 IV,则在 PBKDF 的输出上使用 KBKDF。或者,如果您需要快捷方式,请对输出执行 SHA-256。

下面最后一部分的代码:

/**
 * Same security, twice the speed.
 */
public static String generateStorngPasswordHashWithSHA256(String password) 
    try 
        char[] chars = password.toCharArray();
        byte[] salt = getSalt();

        PBEKeySpec spec = new PBEKeySpec(chars, salt, 1010101,
                20 * Byte.SIZE);
        SecretKeyFactory skf = SecretKeyFactory
                .getInstance("PBKDF2WithHmacSHA1");
        byte[] hash = skf.generateSecret(spec).getEncoded();

        MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
        hash = sha256.digest();

        return toHex(salt) + ":" + toHex(hash);
     catch (Exception e) 
        System.out.println("Exception: Error in generating password"
                + e.toString());
    
    return "";

【讨论】:

在我看来,"PBKDF2WithHmacSHA1" 是应该使用本机代码优化的功能的主要示例。

以上是关于PBKDF2WithHmacSHA1 密钥生成在 Android 上花费的时间太长的主要内容,如果未能解决你的问题,请参考以下文章

Android 中的 AES 密钥生成

Java 中带有 bouncycastle 的 PBKDF2

如何为 android api 8 (Froyo) 添加 PBKDF2WithHmacSHA1

在 Java 中使用 PBKDF2 进行密码验证

Java Cipher - PBE 线程安全问题

https如何生成密钥