如何使用 PKCS5_PBKDF2_HMAC_SHA1()
Posted
技术标签:
【中文标题】如何使用 PKCS5_PBKDF2_HMAC_SHA1()【英文标题】:How to use PKCS5_PBKDF2_HMAC_SHA1() 【发布时间】:2012-04-03 23:59:49 【问题描述】:我正在尝试使用PKCS5_PBKDF2_HMAC_SHA1()
,下面是我的示例程序。我想确定PKCS5_PBKDF2_HMAC_SHA1()
的结果是否正确,所以我在http://anandam.name/pbkdf2/ 网站上验证了相同的结果,我看到了不同的结果。我是否正确使用了 API?
我怀疑我是否正确传递了盐值。
我已经在程序之后粘贴了我的结果和网站结果。
请帮助我理解这一点。
#include <stdio.h>
#include <types.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/engine.h>
#include <openssl/aes.h>
#include <openssl/rand.h>
#include <proto.h>
#define KEY_LEN 32// 32 bytes - 256 bits
#define KEK_KEY_LEN 5
#define ITERATION 1000
unsigned char salt_value[KEY_LEN];
unsigned char AESkey[KEY_LEN];
unsigned char XTSkey[KEY_LEN];
u8 fuse_key[KEY_LEN];
void main()
s32 i=0;
s32 len =0;
u8 *out;
u8 *rspHMAC;
const s8 pwd[] = "test";
s8 rspPKCS5[KEK_KEY_LEN * 2];
s32 ret;
rspHMAC = (unsigned char *) malloc(sizeof(char) * KEY_LEN);
out = (unsigned char *) malloc(sizeof(char) * KEK_KEY_LEN);
RAND_bytes(salt_value, KEY_LEN);
printf("\n salt_value[0] = %x; salt_value[31]= %x", salt_value[0], salt_value[31]);
printf("\n strlen(salt_value) = %d; sizeof(salt_value) = %d\n", strlen(salt_value), sizeof(salt_value));
for(i = 0; i < KEY_LEN; i++)
printf("%02x", salt_value[i]);
ret = PKCS5_PBKDF2_HMAC_SHA1(pwd, strlen(pwd), salt_value, strlen(salt_value), ITERATION, KEK_KEY_LEN, out);
printf("\n PKCS#5 :");
for(len = 0; len < KEK_KEY_LEN; len++)
printf("%02x", out[len]);
sprintf(&rspPKCS5[len * 2], "%02x", out[len]);
printf("\n");
样本输出:
salt_value[0] = e2; salt_value[31]= 12
strlen(salt_value) = 32; sizeof(salt_value) = 32
e258017933f3e629a4166cece78f3162a3b0b7edb2e94c93d76fe6c38198ea12
PKCS#5 :7d7ec9f411
网站结果:
The derived 40-bit key is: a5caf6a0d3
【问题讨论】:
相关,现在有PKCS5_PBKDF2_HMAC
和PKCS5_PBKDF2_HMAC_SHA1
的手册页。见PKCS5_PBKDF2_HMAC (3)
。
【参考方案1】:
首先,让我们看一下 PBKDF2 HMAC-SHA1 的 official test vector:
Input:
P = "password" (8 octets)
S = "salt" (4 octets)
c = 1
dkLen = 20
Output:
DK = 0c 60 c8 0f 96 1f 0e 71
f3 a9 b5 24 af 60 12 06
2f e0 37 a6 (20 octets)
所以现在我们知道了我们在网络和您的程序中要拍摄的内容。因此,使用该信息,我们发现网站希望将您的 salt 作为 ASCII 字符串,然后将其转换为字节。这很重要,因为如果您使用RAND_bytes
生成盐,您将永远无法匹配网页的输出。
password
salt
1
20
0c60c80f961f0e71f3a9b524af6012062fe037a6
而且你错误地使用了盐。在您的注释行中,您正在生成一个带有 ASCII 字符的字符串。如果要使用该盐,则必须将其声明为字节数组。另外,你少了一个数字。
unsigned char salt_value[]= 0x5d, 0x85, 0x94, 0x7b, … /* and so on */ ;
在未注释的代码中,您正在生成一个字节数组,但将其视为字符串。您不要在字节数组上调用 strlen
,因为字节数组可以包含 0,strlen 会将其解释为空终止符。因此,您可以手动跟踪大小(例如,您的 KEK_KEY_LEN 为您 malloc 的数组定义)或在适当的时候使用 sizeof
。
PKCS5_PBKDF2_HMAC_SHA1(pwd, strlen(pwd), salt_value, sizeof(salt_value), ITERATION, KEK_KEY_LEN, out);
既然我们知道了所有这些事情,我们就可以组合一个完整的程序来匹配网站的输出和官方的测试向量。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/evp.h>
#define KEY_LEN 32
#define KEK_KEY_LEN 20
#define ITERATION 1
int main()
size_t i;
unsigned char *out;
const char pwd[] = "password";
unsigned char salt_value[] = 's','a','l','t';
out = (unsigned char *) malloc(sizeof(unsigned char) * KEK_KEY_LEN);
printf("pass: %s\n", pwd);
printf("ITERATION: %u\n", ITERATION);
printf("salt: "); for(i=0;i<sizeof(salt_value);i++) printf("%02x", salt_value[i]); printf("\n");
if( PKCS5_PBKDF2_HMAC_SHA1(pwd, strlen(pwd), salt_value, sizeof(salt_value), ITERATION, KEK_KEY_LEN, out) != 0 )
printf("out: "); for(i=0;i<KEK_KEY_LEN;i++) printf("%02x", out[i]); printf("\n");
else
fprintf(stderr, "PKCS5_PBKDF2_HMAC_SHA1 failed\n");
free(out);
return 0;
(请注意 main 需要返回 int
并且您应该释放分配的内存)
gcc pkcs5.c -o pkcs5 -g -lcrypto -Wall
./pkcs5
pass: password
ITERATION: 1
salt: 73616c74
out: 0c60c80f961f0e71f3a9b524af6012062fe037a6
【讨论】:
感谢 indiv 的回复。当我使用 RAND_bytes() 生成盐时,将其转换为 ASCII 我能够匹配结果(就像我的程序那样),但我正在考虑使用 PKCS5_PBKDF2_HMAC_SHA1() 的二进制值。他们有什么方法可以验证使用二进制生成的结果是否与程序输出匹配? 我链接到的文档有一个测试向量,它将通过使用密码“pass\0word”和盐“sa\0lt”来捕捉二进制实现中的错误。\0
代表字节 0x00。如果您的实现可以通过所有测试向量测试用例,则认为它在功能上是正确的。
感谢您的回答,@indiv。您的解决方案对我有用,但我必须在调用 PKCS5_PBKDF2_HMAC_SHA1() 时使用 strlen(salt_value) 而不是 sizeof(salt_value)。
@MaxMarchuk:我不知道为什么会这样(但我猜你在指针上使用 sizeof 而不是数组),但要小心strlen
。 strlen
在第一个 0x00 字节处停止扫描。如果您使用二进制(非文本)盐,任何值为 0 的字节都会破坏它。例如,对于这个盐,unsigned char salt_value[] = 0x00, 0x01, 0x02;
,你会得到一个 0 的 strlen,这是错误的。如果你只使用文本(ASCII)字符串作为盐,你应该没问题。以上是关于如何使用 PKCS5_PBKDF2_HMAC_SHA1()的主要内容,如果未能解决你的问题,请参考以下文章
RAND_bytes 不会从同一个种子中给出相同的结果[重复]