将PBKDF2与OpenSSL库结合使用

时间:2014-03-31 22:05:13

标签: cryptography openssl pbkdf2

我想利用SHA1 HMAC的PBKDF2算法(基于this答案)。

如何通过加密库来利用它?

我首先查看man openssl,但openssl passwd命令(man page)仅支持少量算法。查看crypto文档,evp模块采用EVP_BytesToKey方法。

  

仔细选择参数将提供PKCS#5 PBKDF1兼容实现。但是,新应用程序通常不应使用此功能(例如,首选PCKS#5的PBKDF2)。

这让我回到原来的问题,如何通过加密利用PBKDF2?我是否需要深入研究代码并调用非API暴露的方法(例如PKCS5_PBKDF2_HMAC)? (如果是的话,是什么让它不被暴露?)

1 个答案:

答案 0 :(得分:9)

我通过my github repository的OpenSSL库确实有一个工作但很差的CKDF2 C示例,包括在Linux和Windows(通过MinGW)下编译的脚本。源代码位于"发布"众所周知;主分支中的源代码是WIP。除了SSLeay许可证OpenSSL之外,此变体还使用相同的4条款BSD进行许可。

我还在努力添加一些功能,然后我将回到Code Review StackExchange网站上的优秀输入并升级到C99语法等等。

核心代码非常原始,尽管传递了非常广泛的基于字符串的测试向量,但可能包含缺陷。它尚未针对纯二进制输入进行测试。

#include <openssl/evp.h>
#include <openssl/sha.h>
// crypto.h used for the version
#include <openssl/crypto.h>

void PBKDF2_HMAC_SHA_1nat_string(const char* pass, const unsigned char* salt, int32_t iterations, uint32_t outputBytes, char* hexResult)
{
    unsigned int i;
    unsigned char digest[outputBytes];
    PKCS5_PBKDF2_HMAC_SHA1(pass, strlen(pass), salt, strlen(salt), iterations, outputBytes, digest);
    for (i = 0; i < sizeof(digest); i++)
        sprintf(hexResult + (i * 2), "%02x", 255 & digest[i]);
}

如果您有64位系统,我强烈建议您转而使用PBKDF2-HMAC-SHA-512或PBKDF2-HMAC-SHA-384:

#include <openssl/evp.h>
#include <openssl/sha.h>
// crypto.h used for the version
#include <openssl/crypto.h>


void PBKDF2_HMAC_SHA_384_string(const char* pass, const unsigned char* salt, int32_t iterations, uint32_t outputBytes, char* hexResult)
{
    unsigned int i;
    unsigned char digest[outputBytes];
    PKCS5_PBKDF2_HMAC(pass, strlen(pass), salt, strlen(salt), iterations, EVP_sha384(), outputBytes, digest);
    for (i = 0; i < sizeof(digest); i++)
        sprintf(hexResult + (i * 2), "%02x", 255 & digest[i]);
}

void PBKDF2_HMAC_SHA_512_string(const char* pass, const unsigned char* salt, int32_t iterations, uint32_t outputBytes, char* hexResult)
 {
     unsigned int i;
     unsigned char digest[outputBytes];
     PKCS5_PBKDF2_HMAC(pass, strlen(pass), salt, strlen(salt), iterations, EVP_sha512(), outputBytes, digest);
     for (i = 0; i < sizeof(digest); i++)
         sprintf(hexResult + (i * 2), "%02x", 255 & digest[i]);
 }    

使用的一个例子是:

// 2*outputBytes+1 is 2 hex bytes per binary byte, 
// and one character at the end for the string-terminating \0
char hexResult[2*outputBytes+1];
memset(hexResult,0,sizeof(hexResult));
PBKDF2_HMAC_SHA_1nat_string(pass, salt, iterations, outputBytes, hexResult);
printf("%s\n", hexResult);

// 2*outputBytes+1 is 2 hex bytes per binary byte, 
// and one character at the end for the string-terminating \0
char hexResult[2*outputBytes+1];
memset(hexResult,0,sizeof(hexResult));
PBKDF2_HMAC_SHA_512_string(pass, salt, iterations, outputBytes, hexResult);
printf("%s\n", hexResult);

使用8到16个二进制字节的随机,每用户盐,即16到32个十六进制数字 - 我的代码没有生成此内容的示例

无论您选择什么,请务必根据测试向量进行验证(有些是在我的存储库中的pbkdf2_test.bat / sh中)。

此外,在您的系统上,进行一些基准测试 - 当然在PBKDF2-HMAC-SHA-384和PBKDF2-HMAC-SHA-512变体上,在64位系统下进行编译可以产生显着更好的结果。将其与my equally poor C++ Crypto++和/或my poor C PolarSSL示例或Jither's C# implementation example进行比较,具体取决于您的目标系统。

您关心速度的原因是您必须根据生产系统可用的性能与在高峰时间登录/创建密码的用户数量选择迭代计数,以免产生太多投诉缓慢。

攻击者会使用像oclHashcat这样的东西,它在具有8x AMD R9 290Xstock核心时钟的单台PC上能够每30天针对PBKDF2-HMAC-SHA尝试3.4E12(2 ^ 41)次猜测1(SSID为salt,密码,32字节输出长度,4096次迭代,又名WPA / WPA2),它或多或少等同于PBKDF2-HMAC-SHA-1(salt,pw,20字节输出长度,8192次迭代)。

  • 如果您使用65536次迭代,攻击者将只能每30天尝试8.5E11(~2 ^ 39)猜测。
  • 如果您使用1024次迭代,攻击者将能够每30天尝试一次2.7E13(~2 ^ 44)猜测。

当攻击者开始选择攻击时,差异变得很重要。

  • 在这两种情况下,攻击者都会使用非常小的密钥;没有理由不花几分钟甚至几个小时的时间在低垂的果实上。
    • 这是长度为1-n的所有十六进制字符,然后是长度为n + 1到n + m的所有可打印字符,然后继续向下直到它们在n + y处用硬编码!最后:))。
  • 在这两种情况下,攻击者都会使用微小的词典和微小的规则集;说出184389个单词和Best64规则集的phpbb字典
    • 如果您有1000个用户使用1000种不同的随机盐并且使用了PBKDF2-HMAC-SHA-1和65536次迭代,那么我们将使用我们的单台PC,8位GPU攻击者(184389 * 64 * 1000)/( 8.5E11 / 30)天= 0.41天。非常值得!
  • 现在,同样的phpbb字典和4089规则的中型T0X1C规则集怎么样?
    • 如果您有1000个用户使用1000种不同的随机盐并且使用PB​​KDF2-HMAC-SHA-1和65536次迭代,那么我们将使用我们的单台PC,8位GPU攻击者(184389 * 4089 * 1000)/( 8.5E11 / 30)天= 26.61天。
      • 还是值得的,但是这台机器在一次攻击上花了将近4周的时间!
    • 如果您有1000个用户使用1000种不同的随机盐并且使用PB​​KDF2-HMAC-SHA-1和65536次迭代,那么我们将使用我们的单台PC,8位GPU攻击者(184389 * 4089 * 1000)/( 2.7E13 / 30)天= 0.83天。
      • 完全值得!
  • 现在,那同样的phpbb字典和35404规则的优秀d3ead0ne规则集怎么样?
    • 如果您有1000个用户使用1000种不同的随机盐并且使用了PBKDF2-HMAC-SHA-1和65536次迭代,那么我们将使用我们的单台PC,8位GPU攻击者(184389 * 35404 * 1000)/( 8.5E11 / 30)天= 230.39天。
      • 是的,是时候买更多的机器或在关机时间工作。
    • 如果您有1000个用户使用1000种不同的随机盐并且使用PB​​KDF2-HMAC-SHA-1和65536次迭代,那么我们将使用我们的单台PC,8位GPU攻击者(184389 * 4089 * 1000)/( 2.7E13 / 30)天= 7.18天。
      • 值得!这只是一个星期和一顿饭。

现在,对于PBKDF2,还有其他一些事情要知道:

  • 对于密码散列,请勿选择大于本机散列大小的二进制输出大小。就个人而言,我不建议使用20字节以下的二进制输出大小,因此这对SHA-1来说有点限制。
    • SHA-1的大小= 20字节
    • SHA-224是20&lt; = size&lt; = 28字节
    • SHA-256是20&lt; = size&lt; = 32字节
    • SHA-384是20&lt; = size&lt; = 48字节
    • SHA-512是20&lt; = size&lt; = 64字节
    • 当然,如果它不是纯二进制,则调整存储哈希的方式。
    • 原因:PBKDF2首先运行为一个本机输出大小请求的迭代次数(右侧的数字,上方)。如果你想要更多,它会重新运行整个迭代计数。如果你想要少于它,它会截断。
    • 攻击者只会匹配第一个本机大小 - 如果第一个字节匹配,是,那就是密码,所以你最好不要增加你的迭代次数。