openssl C API "EVP_BytesToKey()" returns key和iv使用时错误
Openssl C API "EVP_BytesToKey()" returns wrong key and iv when used
使用 OpenSSL C API ,我想使用密码解密文件,为此我需要使用 EVP_BytesToKey()
从给定的密码手动导出密钥和 iv,但是函数给了我错误的密钥和 iv.
我像这样使用命令行工具加密文件:
openssl enc -nosalt -p -in sample.txt -out sample.txt.enc -e -aes256 -k PASSWORD
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
key=0BE64AE89DDD24E225434DE95D501711339BAEEE18F009BA9B4369AF27D30D60
iv =44A783DFFE1463B81E65064750797FA4
这是我的代码(大部分来自我见过的堆栈溢出示例):
#include <string>
#include <fstream>
#include <openssl/ssl.h>
#include <iostream>
#include <vector>
#include <locale>
#include <windows.h>
int main(){
// Initializing OpenSLL
const EVP_CIPHER *cipher;
const EVP_MD *dgst = NULL;
unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
// Setting password
const char *password = "PASSWORD";
const unsigned char *salt = NULL;
int i;
OpenSSL_add_all_algorithms();
// Getting key and iv
cipher = EVP_get_cipherbyname("aes-256-cbc");
if(!cipher) { fprintf(stderr, "no such cipher\n"); return 1; }
dgst=EVP_get_digestbyname("md5");
if(!dgst) { fprintf(stderr, "no such digest\n"); return 1; }
if(!EVP_BytesToKey(cipher, dgst, salt,
(unsigned char *) password,
strlen(password), 1, key, iv))
{
fprintf(stderr, "EVP_BytesToKey failed\n");
return 1;
}
// Output key and iv to the screen
printf("Key: "); for(i=0; i< 32; ++i) { printf("%02x", key[i]); } printf("\n");
printf("IV: "); for(i=0; i< 16; ++i) { printf("%02x", iv[i]); } printf("\n");
}
调试时,程序将key和iv输出到屏幕上,它们包含随机字节?!?
不知何故,当使用“%02x”将密钥和 iv 打印到屏幕时,它显示有效的十六进制值。
这是我的程序输出:
Key: 319f4d26e3c536b5dd871bb2c52e3178dcbc5de3a413ea043012cf3506b6956e
IV: c23b1986151650adf58ba93c7a10f73f
输出与原始密钥和 iv 不同。我见过其他人这样做并且对他们有用。
谢谢
解决方案:
(阅读到最后以获得改进)
自 openSSL 1.1 起,指定无摘要输出 SHA256 摘要而不是 MD5。如果你 运行 这个命令(注意 -md 开关):
openssl enc -nosalt -p -in sample.txt -out sample.txt.enc -e -aes256 -k PASSWORD -md md5
您得到以下输出:
iv =C23B1986151650ADF58BA93C7A10F73F
和你的C程序一样。
相反,如果您将 C 源代码切换为 sha256 摘要:
dgst=EVP_get_digestbyname("sha256");
您获得与命令行相同的输出。
改进
您可能已经注意到,openssl
给您一个警告 *** WARNING : deprecated key derivation used.
查看 openssl 文档,我们对此类警告有解释:
Newer applications should use a more modern algorithm such as PBKDF2 as defined in PKCS#5v2.1 and provided by PKCS5_PBKDF2_HMAC.
然后我们可以使用此算法直接获取密钥,同时使用 openssl
可执行开关 iter 1
自动使用 -pbkdf2 Use password-based key derivation function 2
:
openssl enc -nosalt -p -in a.out -out a.out.enc -e -aes256 -k PASSWORD -md sha256 -iter 1
得到
key=75753C1B22D09354380EB2DA277B994738CA4C5AC09191E6285E9F99E04AA7A0
iv =7AE8C8B15B535D4197B5EC8E59F65809
并修改我们的 C 程序以使用相同的更安全的算法获取密钥:
#include "string.h"
#include "openssl/ssl.h"
int main(){
// Initializing OpenSLL
const EVP_CIPHER *cipher;
const EVP_MD *dgst = NULL;
unsigned char keyivpair[EVP_MAX_KEY_LENGTH+EVP_MAX_IV_LENGTH];
unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
// Setting password
const char *password = "PASSWORD";
const unsigned char *salt = NULL;
int i;
OpenSSL_add_all_algorithms();
// Getting key and iv
cipher = EVP_get_cipherbyname("aes-256-cbc");
if(!cipher) { fprintf(stderr, "no such cipher\n"); return 1; }
dgst=EVP_get_digestbyname("sha256");
if(!dgst) { fprintf(stderr, "no such digest\n"); return 1; }
int iklen = EVP_CIPHER_key_length(cipher);
int ivlen = EVP_CIPHER_iv_length(cipher);
if( !PKCS5_PBKDF2_HMAC ((unsigned char *) password,
strlen(password),
salt,
0,
1,
dgst, //sha256
iklen + ivlen,
keyivpair) )
{
fprintf(stderr, "PKCS5_PBKDF2_HMAC failed\n");
return 1;
}
memcpy(key, keyivpair, iklen);
memcpy(iv, keyivpair + iklen, ivlen);
// Output key and iv to the screen
printf("Key: "); for(i=0; i< 32; ++i) { printf("%02x", key[i]); } printf("\n");
printf("IV: "); for(i=0; i< 16; ++i) { printf("%02x", iv[i]); } printf("\n");
输出:
Key: 75753c1b22d09354380eb2da277b994738ca4c5ac09191e6285e9f99e04aa7a0
IV: 7ae8c8b15b535d4197b5ec8e59f65809
使用 OpenSSL C API ,我想使用密码解密文件,为此我需要使用 EVP_BytesToKey()
从给定的密码手动导出密钥和 iv,但是函数给了我错误的密钥和 iv.
我像这样使用命令行工具加密文件:
openssl enc -nosalt -p -in sample.txt -out sample.txt.enc -e -aes256 -k PASSWORD
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
key=0BE64AE89DDD24E225434DE95D501711339BAEEE18F009BA9B4369AF27D30D60
iv =44A783DFFE1463B81E65064750797FA4
这是我的代码(大部分来自我见过的堆栈溢出示例):
#include <string>
#include <fstream>
#include <openssl/ssl.h>
#include <iostream>
#include <vector>
#include <locale>
#include <windows.h>
int main(){
// Initializing OpenSLL
const EVP_CIPHER *cipher;
const EVP_MD *dgst = NULL;
unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
// Setting password
const char *password = "PASSWORD";
const unsigned char *salt = NULL;
int i;
OpenSSL_add_all_algorithms();
// Getting key and iv
cipher = EVP_get_cipherbyname("aes-256-cbc");
if(!cipher) { fprintf(stderr, "no such cipher\n"); return 1; }
dgst=EVP_get_digestbyname("md5");
if(!dgst) { fprintf(stderr, "no such digest\n"); return 1; }
if(!EVP_BytesToKey(cipher, dgst, salt,
(unsigned char *) password,
strlen(password), 1, key, iv))
{
fprintf(stderr, "EVP_BytesToKey failed\n");
return 1;
}
// Output key and iv to the screen
printf("Key: "); for(i=0; i< 32; ++i) { printf("%02x", key[i]); } printf("\n");
printf("IV: "); for(i=0; i< 16; ++i) { printf("%02x", iv[i]); } printf("\n");
}
调试时,程序将key和iv输出到屏幕上,它们包含随机字节?!?
不知何故,当使用“%02x”将密钥和 iv 打印到屏幕时,它显示有效的十六进制值。
这是我的程序输出:
Key: 319f4d26e3c536b5dd871bb2c52e3178dcbc5de3a413ea043012cf3506b6956e
IV: c23b1986151650adf58ba93c7a10f73f
输出与原始密钥和 iv 不同。我见过其他人这样做并且对他们有用。
谢谢
解决方案:
(阅读到最后以获得改进)自 openSSL 1.1 起,指定无摘要输出 SHA256 摘要而不是 MD5。如果你 运行 这个命令(注意 -md 开关):
openssl enc -nosalt -p -in sample.txt -out sample.txt.enc -e -aes256 -k PASSWORD -md md5
您得到以下输出:
iv =C23B1986151650ADF58BA93C7A10F73F
和你的C程序一样。
相反,如果您将 C 源代码切换为 sha256 摘要:
dgst=EVP_get_digestbyname("sha256");
您获得与命令行相同的输出。
改进
您可能已经注意到,openssl
给您一个警告 *** WARNING : deprecated key derivation used.
查看 openssl 文档,我们对此类警告有解释:
Newer applications should use a more modern algorithm such as PBKDF2 as defined in PKCS#5v2.1 and provided by PKCS5_PBKDF2_HMAC.
然后我们可以使用此算法直接获取密钥,同时使用 openssl
可执行开关 iter 1
自动使用 -pbkdf2 Use password-based key derivation function 2
:
openssl enc -nosalt -p -in a.out -out a.out.enc -e -aes256 -k PASSWORD -md sha256 -iter 1
得到
key=75753C1B22D09354380EB2DA277B994738CA4C5AC09191E6285E9F99E04AA7A0
iv =7AE8C8B15B535D4197B5EC8E59F65809
并修改我们的 C 程序以使用相同的更安全的算法获取密钥:
#include "string.h"
#include "openssl/ssl.h"
int main(){
// Initializing OpenSLL
const EVP_CIPHER *cipher;
const EVP_MD *dgst = NULL;
unsigned char keyivpair[EVP_MAX_KEY_LENGTH+EVP_MAX_IV_LENGTH];
unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
// Setting password
const char *password = "PASSWORD";
const unsigned char *salt = NULL;
int i;
OpenSSL_add_all_algorithms();
// Getting key and iv
cipher = EVP_get_cipherbyname("aes-256-cbc");
if(!cipher) { fprintf(stderr, "no such cipher\n"); return 1; }
dgst=EVP_get_digestbyname("sha256");
if(!dgst) { fprintf(stderr, "no such digest\n"); return 1; }
int iklen = EVP_CIPHER_key_length(cipher);
int ivlen = EVP_CIPHER_iv_length(cipher);
if( !PKCS5_PBKDF2_HMAC ((unsigned char *) password,
strlen(password),
salt,
0,
1,
dgst, //sha256
iklen + ivlen,
keyivpair) )
{
fprintf(stderr, "PKCS5_PBKDF2_HMAC failed\n");
return 1;
}
memcpy(key, keyivpair, iklen);
memcpy(iv, keyivpair + iklen, ivlen);
// Output key and iv to the screen
printf("Key: "); for(i=0; i< 32; ++i) { printf("%02x", key[i]); } printf("\n");
printf("IV: "); for(i=0; i< 16; ++i) { printf("%02x", iv[i]); } printf("\n");
输出:
Key: 75753c1b22d09354380eb2da277b994738ca4c5ac09191e6285e9f99e04aa7a0
IV: 7ae8c8b15b535d4197b5ec8e59f65809