网络安全真相:PBKDF2 和 OpenSSL

本文介绍了在网络安全中,如何使用 PBKDF2 (Password-Based Key Derivation Function 2) 和 OpenSSL 来安全地从密码生成加密密钥。PBKDF2 通过增加迭代次数来减慢哈希过程,从而提高密码破解的难度,文章通过C代码展示了如何在OpenSSL中使用PBKDF2,并使用测试向量验证了其正确性。

网络安全的基本事实:PBKDF2 和 OpenSSL

那么,在网络安全中,我们如何定义“基本事实”,例如从给定密码生成的加密密钥是什么,或者给定消息的哈希输出是什么? 很有可能我们通常会求助于 OpenSSL 及其 C 代码实现。 有一种小方法很可能保护你的密码,并使黑客的生活变得困难:强大的 PBKDF2(基于密码的密钥派生函数 2)。 让我们使用 OpenSSL 和 C 实现此功能,并验证输出是否正确。

介绍

所有那些图表显示破解密码的哈希版本需要多长时间,它们的定义都是错误的。 大多数将采用快速哈希方法的破解速度并使用它。 通过适当的 KDF(密钥派生函数),我们通常会减慢整个过程,甚至可以达到每秒只能哈希几个密码,而不是数十亿个密码的程度。 因此,如果我们正确选择哈希方法,我们可以让某人极难发现原始密码。 从密码生成加密密钥也是如此。

当我们说我们存储密码的哈希版本时,我们通常不是指我们使用标准哈希方法。 这包括 MD5、SHA-1 和 SHA-256。 原因是这些哈希方法速度很快,并且攻击者每秒可以尝试数十亿个密码并尝试这些密码的哈希版本。 这包括使用盐值。

我们通常使用的方法是使用密钥派生函数 (KDF)。 为此,最广泛使用的方法之一是 PBKDF2(基于密码的密钥派生函数 2)。 它在 RFC 2898 中定义,并生成加盐的哈希。 我们还添加了许多迭代,这减慢了哈希过程,同时也禁用了 GPU 的使用。

通常,这用于从定义的密码创建加密密钥,并且无法从哈希值中反转密码。 PBKDF2 用于 TrueCrypt 中,以生成读取加密驱动器的标头信息所需的密钥,该信息存储加密密钥。 在 WPA-2 中,我们使用 4,096 次迭代。

在 OpenSSL 中,我们定义了 PBKDF2 的参数,例如消息 (msg)、盐值 (salt)、哈希方法(SHA1、SHA256 或 SHA256)以及迭代次数 [ 这里]:

OSSL_PARAM params[5];
 if (strcmp(hash,"SHA1")==0) params[0] =  OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, SN_sha1, strlen(SN_sha1));
 else if (strcmp(hash,"SHA512")==0) params[0] =  OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, SN_sha512, strlen(SN_sha512));
 else params[0] =  OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, SN_sha256, strlen(SN_sha256));

 params[1] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PASSWORD,
                                        msg,strlen(msg));
 params[2] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT,
                                         salt, strlen(salt));
 params[3] = OSSL_PARAM_construct_uint32(OSSL_KDF_PARAM_ITER, &iter);
 params[4] = OSSL_PARAM_construct_end();

然后我们可以通过以下方式生成给定长度的输出 [ 这里]:

EVP_KDF_derive(kctx, out, len, params);

其中 len 是输出中的字节数。 以下是使用 C 进行的编码 [ 这里]:

##include <stdio.h>
##include <string.h>
##include <stdlib.h>

##include <openssl/evp.h>
##include <openssl/pem.h>
##include <openssl/err.h>
##include <openssl/core_names.h>
##include <openssl/params.h>
##include <openssl/kdf.h>

static void hexdump(const char *label, const unsigned char *buf, size_t len) {
    printf("%s (%zu bytes): ", label, len);
    for (size_t i = 0; i < len; i++) printf("%02X", buf[i]);
    printf("\n");
}

int main(int argc, char** argv) {

 unsigned int iter=2048;
 EVP_KDF *kdf;
 EVP_KDF_CTX *kctx;
 unsigned char out[256];
 unsigned char *msg="Hello World!";
 unsigned char *salt="NaCl";
 unsigned char *hash="SHA256";
 int len=64;

 if (argc > 1) msg = argv[1];
 if (argc > 2) salt  = argv[2];
 if (argc > 3) iter  = atoi(argv[3]);
 if (argc > 4) hash = argv[4];
 if (argc > 5) len = atoi(argv[5]);

 kdf = EVP_KDF_fetch(NULL, "PBKDF2", NULL);

 kctx = EVP_KDF_CTX_new(kdf);

 EVP_KDF_free(kdf);

 OSSL_PARAM params[5];
 if (strcmp(hash,"SHA1")==0) params[0] =  OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, SN_sha1, strlen(SN_sha1));
 else if (strcmp(hash,"SHA512")==0) params[0] =  OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, SN_sha512, strlen(SN_sha512));
 else params[0] =  OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, SN_sha256, strlen(SN_sha256));

 params[1] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PASSWORD,
                                        msg,strlen(msg));
 params[2] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT,
                                         salt, strlen(salt));
 params[3] = OSSL_PARAM_construct_uint32(OSSL_KDF_PARAM_ITER, &iter);
 params[4] = OSSL_PARAM_construct_end();

 EVP_KDF_derive(kctx, out, sizeof(out), params);

 printf("Message: [%s]\n",msg);
 printf("Salt: [%s]\n",salt);
 printf("Iterations: [%d]\n",iter);
 printf("Hash: [%s]\n",hash);
 printf("Length: [%d]\n\n",len);

  hexdump("KDF: ",out,len);
 EVP_KDF_CTX_free(kctx);

 return EXIT_SUCCESS;
}

一个示例运行显示 [ 这里]:

Message: [password]
Salt: [NaCl]
Iterations: [2048]
Hash: [SHA256]
Length: [64]

KDF:  (64 bytes): 79A033D2E270A68D91127F5E6341505A65EDFF77442E073D4D2041B4B1428D321A82609957D0F89FD61EC98CDD529C3BEA336C6E68EBA133D3F18B0925840D5

一个示例运行如下:

要进行测试,这是一个测试向量 [ 这里]:

 Input:
       P = "passwordPASSWORDpassword" (24 octets)
       S = "saltSALTsaltSALTsaltSALTsaltSALTsalt" (36 octets)
       c = 4096
       dkLen = 25

     Output:

       PBKDF2-HMAC-SHA256 = 348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c (25 octets)

       PBKDF2-HMAC-SHA512 = 8c0511f4c6e597c6ac6315d8f0362e225f3c501495ba23b868 (25 octets)

如果我们尝试 SHA256:

对于 SHA-512:

你可以看到这两个测试向量都已正确实现。

结论

我们不会使用标准的哈希方法将密码哈希为加密密钥,因为存在安全问题,并且哈希速度很快。 一种改进的方法是使用 PBKDF2,这会减慢整个哈希过程。 你会发现每次连接到 Wi-fi 时,都是 PBKDF2 在保护你的密码。

这是 scrypt 的实现:

https://asecuritysite.com/scrypt/openssl_scrypt

  • 原文链接: medium.com/asecuritysite...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
asecuritysite
asecuritysite
江湖只有他的大名,没有他的介绍。