如何使用PKCS5_PBKDF2_HMAC_SHA1()

时间:2012-03-19 13:37:27

标签: openssl salt pbkdf2

我正在尝试使用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 

1 个答案:

答案 0 :(得分:15)

首先,让我们看一下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将其解释为空终止符。因此,您可以手动跟踪大小(例如,您对malloc阵列的KEK_KEY_LEN定义),或者在适当时使用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;
}

(并注意主要需要返回int并且你应该释放你分配的内存)

gcc pkcs5.c -o pkcs5 -g -lcrypto -Wall
./pkcs5
pass: password
ITERATION: 1
salt: 73616c74
out: 0c60c80f961f0e71f3a9b524af6012062fe037a6