Openssl 和 Windows CryptoAPI 兼容性问题
Openssl and Windows CryptoAPI compatibility issue
我在 windows 应用程序和服务器应用程序之间的加密通信中遇到问题。客户端应用程序 运行 在 Windows,用 C 编写并使用 CryptoAPI。服务器应用程序使用 PHP 和 Openssl 扩展。选择 AES-256-CBC 作为密码算法。使用相同的算法 Openssl 和 CryptoAPI 会产生不同的结果。我在 RC2-CBC 算法中遇到了同样的问题。这个在线工具 http://asecuritysite.com/Encryption/openssl 生成与 openssl 相同的结果,所以我断定是 C 代码中的错误。
PHP代码:
<?php
//$flag = OPENSSL_RAW_DATA;
$flag = false;
//this string will encode
$dataString = 'some data string';
$pass = "1234567812345678";
$method = "aes-256-cbc";
$iv = "Zievrs8NZievrs8N";
echo "original:\n";
var_dump($dataString);
$encryptedMessage = openssl_encrypt($dataString, $method, $pass, $flag, $iv);
echo "after encrypt:\n";
var_dump($encryptedMessage);
echo "vector:\n";
var_dump($iv);
$decryptedMessage = openssl_decrypt($encryptedMessage, $method, $pass, $flag, $iv);
echo "after decrypt:\n";
var_dump($decryptedMessage);
输出:
original:
string(16) "some data string"
after encrypt:
string(44) "9O8UAaRRCfneeRbRCeiYi9nOM8F2KA6gtkAsvPliUdA="
vector:
string(16) "Zievrs8NZievrs8N"
after decrypt:
string(16) "some data string"
C代码:
BOOL SetKey(BYTE* szKey, DWORD dwKeySize, HCRYPTPROV* m_hProv, HCRYPTHASH* m_hHash, HCRYPTKEY* m_hKey)
{
BOOL m_fOK= TRUE;
if (*m_hProv == 0) {
m_fOK = CryptAcquireContextA(m_hProv, NULL,
NULL, //MS_DEF_PROV_A,
PROV_RSA_AES,
CRYPT_VERIFYCONTEXT
);
}
if (m_fOK && (*m_hHash != 0)) {
m_fOK = CryptDestroyHash(*m_hHash);
m_hHash = 0;
}
if (m_fOK && (*m_hHash == 0)) {
m_fOK = CryptCreateHash(*m_hProv, CALG_SHA_256, 0, 0, m_hHash);
}
if (m_fOK) {
m_fOK = CryptHashData(*m_hHash, (BYTE*)szKey, dwKeySize, 0);
}
if (m_fOK) {
m_fOK = CryptDeriveKey(*m_hProv, CALG_AES_256, *m_hHash, CRYPT_EXPORTABLE | CRYPT_NO_SALT, m_hKey);
}
if (m_fOK) {
DWORD mode = CRYPT_MODE_CBC;
m_fOK = CryptSetKeyParam(*m_hKey, KP_MODE, (BYTE*)&mode, 0);
}
if (m_fOK) {
BYTE iv[] = {'Z','i','e','v','r','s','8','N','Z','i','e','v','r','s','8','N',0};
m_fOK = CryptSetKeyParam(*m_hKey, KP_IV, (BYTE*)iv, 0);
}
return m_fOK;
}
BOOL EncryptDecrypt(BYTE* pData, BYTE** pRes, DWORD* dwDataLen, BYTE* pKey, DWORD dwKeySize, BOOL fEncrypt)
{
HCRYPTPROV m_hProv = 0;
HCRYPTHASH m_hHash = 0;
HCRYPTKEY m_hKey = 0;
BOOL m_fOK= TRUE;
m_fOK = SetKey(pKey, dwKeySize, &m_hProv, &m_hHash, &m_hKey);
if (fEncrypt) {
DWORD dwTotalBufferSize = 0;
DWORD dwNewLen = *dwDataLen;
if((m_fOK = CryptEncrypt(m_hKey, 0, TRUE, 0, NULL, &dwNewLen, dwTotalBufferSize))) {
*pRes = (BYTE*)malloc(dwNewLen);
memcpy(*pRes, pData, *dwDataLen);
dwTotalBufferSize = dwNewLen;
if(!(m_fOK = CryptEncrypt(m_hKey, 0, TRUE, 0, *pRes, dwDataLen, dwTotalBufferSize))) {
free(*pRes);
*pRes = NULL;
*dwDataLen = 0;
}
}
}
else {
*pRes = (BYTE*)malloc(*dwDataLen);
memcpy(*pRes, pData, *dwDataLen);
if(!(m_fOK = CryptDecrypt(m_hKey, 0, TRUE, 0, *pRes, dwDataLen))) {
DWORD err = GetLastError();
char msg[100];
wsprintfA(msg, "err = %d\n", err);
OutputDebugStringA(msg);
free(*pRes);
*pRes = NULL;
*dwDataLen = 0;
}
}
if (m_hKey) CryptDestroyKey(m_hKey);
if (m_hHash) CryptDestroyHash(m_hHash);
if (m_hProv) CryptReleaseContext(m_hProv, 0);
return m_fOK;
}
void main() {
const char* data = "some data string";
BYTE* res = NULL;
DWORD len = strlen(data);
EncryptDecrypt((BYTE*)data, &res, &len, (BYTE*)"1234567812345678", 16, TRUE);
size_t len_en = 0;
char* base64 = base64_encode(res, len, &len_en);
printf("base64 = %s\n", base64);
}
输出:
base64 = miFMwk4/ZwjMLsnV4Po9UdWxix32TrK5BcSgSKYr384=
加密输出不同。这意味着最终使用的密钥不同或数据不同。但是数据是一样的,所以key肯定是不同的。
这意味着在生成密钥的过程中,有一些不同。 OpenSSL 可能正在使用此处不可见的其他一些密钥派生函数。尝试使用一些标准算法来生成密钥。不要散列,尽量不要使用散列。
通过将密钥导入为 PLAINTEXTBLOB,我终于从 CryptoAPI 获得了正确的结果。
这不是最终版本,而是对 SetKey 函数的可行修复。
#include <WinCrypt.h>
typedef struct {
BLOBHEADER hdr;
DWORD dwKeySize;
BYTE rgbKeyData[16];
} PLAINTEXTKEYBLOB_t;
BOOL SetKey(BYTE* szKey, DWORD dwKeySize, HCRYPTPROV* m_hProv, HCRYPTHASH* m_hHash, HCRYPTKEY* m_hKey)
{
BOOL m_fOK= TRUE;
if (*m_hProv == 0) {
m_fOK = CryptAcquireContextW(m_hProv, NULL,
MS_ENH_RSA_AES_PROV, //MS_DEF_PROV_A,
PROV_RSA_AES,
CRYPT_NEWKEYSET | CRYPT_VERIFYCONTEXT
);
}
if (m_fOK && (*m_hHash != 0)) {
m_fOK = CryptDestroyHash(*m_hHash);
m_hHash = 0;
}
if(m_fOK) {
BYTE key[] = {0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b, 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06};
PLAINTEXTKEYBLOB_t blob;
blob.hdr.bType = PLAINTEXTKEYBLOB;
blob.hdr.bVersion = 2;
blob.hdr.reserved = 0;
blob.hdr.aiKeyAlg = CALG_AES_128;
blob.dwKeySize = 16;
for(int i=0; i<16; i++) {
//blob.rgbKeyData[16-1-i] = key[i];
blob.rgbKeyData[i] = key[i];
}
m_fOK = CryptImportKey(*m_hProv, (BYTE*)&blob, sizeof(PLAINTEXTKEYBLOB_t), 0, NULL, m_hKey);
}
if (m_fOK) {
DWORD mode = CRYPT_MODE_CBC;
m_fOK = CryptSetKeyParam(*m_hKey, KP_MODE, (BYTE*)&mode, 0);
}
if (m_fOK) {
DWORD mode = 0;
DWORD dwDataLen = sizeof(mode);
m_fOK = CryptGetKeyParam(*m_hKey, KP_PADDING, (BYTE*)&mode, &dwDataLen, 0);
mode = 0;
//m_fOK = CryptSetKeyParam(*m_hKey, KP_PADDING, (BYTE*)&mode, 0);
}
if (m_fOK) {
BYTE iv[] = {0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30, 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41};
m_fOK = CryptSetKeyParam(*m_hKey, KP_IV, (BYTE*)iv, 0);
}
return m_fOK;
}
我在 windows 应用程序和服务器应用程序之间的加密通信中遇到问题。客户端应用程序 运行 在 Windows,用 C 编写并使用 CryptoAPI。服务器应用程序使用 PHP 和 Openssl 扩展。选择 AES-256-CBC 作为密码算法。使用相同的算法 Openssl 和 CryptoAPI 会产生不同的结果。我在 RC2-CBC 算法中遇到了同样的问题。这个在线工具 http://asecuritysite.com/Encryption/openssl 生成与 openssl 相同的结果,所以我断定是 C 代码中的错误。
PHP代码:
<?php
//$flag = OPENSSL_RAW_DATA;
$flag = false;
//this string will encode
$dataString = 'some data string';
$pass = "1234567812345678";
$method = "aes-256-cbc";
$iv = "Zievrs8NZievrs8N";
echo "original:\n";
var_dump($dataString);
$encryptedMessage = openssl_encrypt($dataString, $method, $pass, $flag, $iv);
echo "after encrypt:\n";
var_dump($encryptedMessage);
echo "vector:\n";
var_dump($iv);
$decryptedMessage = openssl_decrypt($encryptedMessage, $method, $pass, $flag, $iv);
echo "after decrypt:\n";
var_dump($decryptedMessage);
输出:
original:
string(16) "some data string"
after encrypt:
string(44) "9O8UAaRRCfneeRbRCeiYi9nOM8F2KA6gtkAsvPliUdA="
vector:
string(16) "Zievrs8NZievrs8N"
after decrypt:
string(16) "some data string"
C代码:
BOOL SetKey(BYTE* szKey, DWORD dwKeySize, HCRYPTPROV* m_hProv, HCRYPTHASH* m_hHash, HCRYPTKEY* m_hKey)
{
BOOL m_fOK= TRUE;
if (*m_hProv == 0) {
m_fOK = CryptAcquireContextA(m_hProv, NULL,
NULL, //MS_DEF_PROV_A,
PROV_RSA_AES,
CRYPT_VERIFYCONTEXT
);
}
if (m_fOK && (*m_hHash != 0)) {
m_fOK = CryptDestroyHash(*m_hHash);
m_hHash = 0;
}
if (m_fOK && (*m_hHash == 0)) {
m_fOK = CryptCreateHash(*m_hProv, CALG_SHA_256, 0, 0, m_hHash);
}
if (m_fOK) {
m_fOK = CryptHashData(*m_hHash, (BYTE*)szKey, dwKeySize, 0);
}
if (m_fOK) {
m_fOK = CryptDeriveKey(*m_hProv, CALG_AES_256, *m_hHash, CRYPT_EXPORTABLE | CRYPT_NO_SALT, m_hKey);
}
if (m_fOK) {
DWORD mode = CRYPT_MODE_CBC;
m_fOK = CryptSetKeyParam(*m_hKey, KP_MODE, (BYTE*)&mode, 0);
}
if (m_fOK) {
BYTE iv[] = {'Z','i','e','v','r','s','8','N','Z','i','e','v','r','s','8','N',0};
m_fOK = CryptSetKeyParam(*m_hKey, KP_IV, (BYTE*)iv, 0);
}
return m_fOK;
}
BOOL EncryptDecrypt(BYTE* pData, BYTE** pRes, DWORD* dwDataLen, BYTE* pKey, DWORD dwKeySize, BOOL fEncrypt)
{
HCRYPTPROV m_hProv = 0;
HCRYPTHASH m_hHash = 0;
HCRYPTKEY m_hKey = 0;
BOOL m_fOK= TRUE;
m_fOK = SetKey(pKey, dwKeySize, &m_hProv, &m_hHash, &m_hKey);
if (fEncrypt) {
DWORD dwTotalBufferSize = 0;
DWORD dwNewLen = *dwDataLen;
if((m_fOK = CryptEncrypt(m_hKey, 0, TRUE, 0, NULL, &dwNewLen, dwTotalBufferSize))) {
*pRes = (BYTE*)malloc(dwNewLen);
memcpy(*pRes, pData, *dwDataLen);
dwTotalBufferSize = dwNewLen;
if(!(m_fOK = CryptEncrypt(m_hKey, 0, TRUE, 0, *pRes, dwDataLen, dwTotalBufferSize))) {
free(*pRes);
*pRes = NULL;
*dwDataLen = 0;
}
}
}
else {
*pRes = (BYTE*)malloc(*dwDataLen);
memcpy(*pRes, pData, *dwDataLen);
if(!(m_fOK = CryptDecrypt(m_hKey, 0, TRUE, 0, *pRes, dwDataLen))) {
DWORD err = GetLastError();
char msg[100];
wsprintfA(msg, "err = %d\n", err);
OutputDebugStringA(msg);
free(*pRes);
*pRes = NULL;
*dwDataLen = 0;
}
}
if (m_hKey) CryptDestroyKey(m_hKey);
if (m_hHash) CryptDestroyHash(m_hHash);
if (m_hProv) CryptReleaseContext(m_hProv, 0);
return m_fOK;
}
void main() {
const char* data = "some data string";
BYTE* res = NULL;
DWORD len = strlen(data);
EncryptDecrypt((BYTE*)data, &res, &len, (BYTE*)"1234567812345678", 16, TRUE);
size_t len_en = 0;
char* base64 = base64_encode(res, len, &len_en);
printf("base64 = %s\n", base64);
}
输出:
base64 = miFMwk4/ZwjMLsnV4Po9UdWxix32TrK5BcSgSKYr384=
加密输出不同。这意味着最终使用的密钥不同或数据不同。但是数据是一样的,所以key肯定是不同的。
这意味着在生成密钥的过程中,有一些不同。 OpenSSL 可能正在使用此处不可见的其他一些密钥派生函数。尝试使用一些标准算法来生成密钥。不要散列,尽量不要使用散列。
通过将密钥导入为 PLAINTEXTBLOB,我终于从 CryptoAPI 获得了正确的结果。
这不是最终版本,而是对 SetKey 函数的可行修复。
#include <WinCrypt.h>
typedef struct {
BLOBHEADER hdr;
DWORD dwKeySize;
BYTE rgbKeyData[16];
} PLAINTEXTKEYBLOB_t;
BOOL SetKey(BYTE* szKey, DWORD dwKeySize, HCRYPTPROV* m_hProv, HCRYPTHASH* m_hHash, HCRYPTKEY* m_hKey)
{
BOOL m_fOK= TRUE;
if (*m_hProv == 0) {
m_fOK = CryptAcquireContextW(m_hProv, NULL,
MS_ENH_RSA_AES_PROV, //MS_DEF_PROV_A,
PROV_RSA_AES,
CRYPT_NEWKEYSET | CRYPT_VERIFYCONTEXT
);
}
if (m_fOK && (*m_hHash != 0)) {
m_fOK = CryptDestroyHash(*m_hHash);
m_hHash = 0;
}
if(m_fOK) {
BYTE key[] = {0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b, 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06};
PLAINTEXTKEYBLOB_t blob;
blob.hdr.bType = PLAINTEXTKEYBLOB;
blob.hdr.bVersion = 2;
blob.hdr.reserved = 0;
blob.hdr.aiKeyAlg = CALG_AES_128;
blob.dwKeySize = 16;
for(int i=0; i<16; i++) {
//blob.rgbKeyData[16-1-i] = key[i];
blob.rgbKeyData[i] = key[i];
}
m_fOK = CryptImportKey(*m_hProv, (BYTE*)&blob, sizeof(PLAINTEXTKEYBLOB_t), 0, NULL, m_hKey);
}
if (m_fOK) {
DWORD mode = CRYPT_MODE_CBC;
m_fOK = CryptSetKeyParam(*m_hKey, KP_MODE, (BYTE*)&mode, 0);
}
if (m_fOK) {
DWORD mode = 0;
DWORD dwDataLen = sizeof(mode);
m_fOK = CryptGetKeyParam(*m_hKey, KP_PADDING, (BYTE*)&mode, &dwDataLen, 0);
mode = 0;
//m_fOK = CryptSetKeyParam(*m_hKey, KP_PADDING, (BYTE*)&mode, 0);
}
if (m_fOK) {
BYTE iv[] = {0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30, 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41};
m_fOK = CryptSetKeyParam(*m_hKey, KP_IV, (BYTE*)iv, 0);
}
return m_fOK;
}