Cryptography 结合OpenSSL库使用PBKDF2

Cryptography 结合OpenSSL库使用PBKDF2,cryptography,openssl,pbkdf2,Cryptography,Openssl,Pbkdf2,我想利用PBKDF2算法和SHA1 HMAC(基于答案) 我如何通过加密库利用它 我首先研究了manopenssl,但是opensslpasswd命令()只支持少量算法。查看文档,模块有一个方法 仔细选择参数将提供与PKCS#5 PBKDF1兼容的实现。但是,新的应用程序通常不应该使用此功能(例如,首选来自PCKS#5的PBKDF2) 这让我回到了我最初的问题,如何通过加密利用PBKDF2?我是否需要深入研究代码并调用非API公开的方法(例如)?(如果是这样的话,是什么阻止了它被公开?我确实有一

我想利用PBKDF2算法和SHA1 HMAC(基于答案)

我如何通过加密库利用它

我首先研究了
manopenssl
,但是
opensslpasswd
命令()只支持少量算法。查看文档,模块有一个方法

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


这让我回到了我最初的问题,如何通过加密利用PBKDF2?我是否需要深入研究代码并调用非API公开的方法(例如)?(如果是这样的话,是什么阻止了它被公开?

我确实有一个PBKDF2的C示例,它可以通过位于的OpenSSL库工作,但很差,包括在Linux和Windows下编译的脚本(通过MinGW)。位于“发布”下的源代码已知良好;主分支中的源代码是WIP。除了SSLeay许可证OpenSSL之外,此变体还根据相同的4条款BSD进行许可

我仍在努力添加一些功能,然后我将回到CodeReviewStackExchange站点上的优秀输入,并升级到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]);
}

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

不管您选择什么,请确保根据测试向量对其进行验证(其中一些在我的存储库中的pbkdf2_test.bat/sh中)

此外,在您的系统上,进行一些基准测试——当然是在PBKDF2-HMAC-SHA-384和PBKDF2-HMAC-SHA-512变体上,在64位系统下编译会产生显著更好的结果。根据目标系统的不同,将其与和/或示例或进行比较

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

攻击者将使用类似的东西,在一台具有8x AMD R9 290Xstock核心时钟的PC上,每30天可以尝试对PBKDF2-HMAC-SHA-1(SSID为salt、密码、32字节输出长度、4096次迭代,也称为WPA/WPA2)进行3.4E12(2^41)猜测,这或多或少相当于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,最后是硬编码的!)
  • 在这两种情况下,攻击者都会使用小字典和小规则集;说一说phpbb字典里的184389个单词和Best64规则集
    • 如果有1000个用户使用1000种不同的随机盐,并使用PBKDF2-HMAC-SHA-1进行65536次迭代,那么我们的单台PC、8个GPU攻击者(184389*64*1000)/(8.5E11/30)天=0.41天。很值得
  • 现在,同样的phpbb字典和4089条规则的中等大小的T0X1C规则集怎么样?
    • 如果您有1000个用户,使用1000种不同的随机盐,并使用PBKDF2-HMAC-SHA-1进行65536次迭代,那么我们的单台PC、8个GPU攻击者(184389*4089*1000)/(8.5E11/30)天=26.61天。
      • 仍然值得,但这台机器花在一次攻击上几乎需要4周
    • 如果有1000个用户使用1000种不同的随机盐,并使用PBKDF2-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种不同的随机盐,并使用PBKDF2-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种语言,几乎可以肯定它有一个已经实现了PBKDF2的库。这(IMHO)比直接发送到
      openssl
      命令行或尝试直接与(糟糕得可怕的)
      libssl
      API对话要好得多。的可能重复。另请参见和“我是否需要深入研究代码并调用非API公开的方法(如PKCS5_PBKDF2_HMAC)”——实际上,这是一个公共API乐趣
      #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);