2015-03-25 3 views
0

У меня проблема с зашифрованной связью между приложением Windows и серверным приложением. Клиентское приложение работает в Windows, написанное на C и использующее CryptoAPI. Серверное приложение использует расширение PHP и Openssl. В качестве алгоритма шифрования AES-256-CBC был выбран. Используя тот же алгоритм, Openssl и CryptoAPI дают разные результаты. Та же проблема, с которой я столкнулся с алгоритмом RC2-CBC. Этот онлайн-инструмент http://asecuritysite.com/Encryption/openssl генерирует тот же результат, что и openssl, поэтому я делаю вывод об ошибке в коде C.Ошибка совместимости с Openssl и Windows CryptoAPI

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= 
+0

Я думаю, что вы забыли проложить свои данные с помощью pkcs7; см. также [этот ответ] (http://stackoverflow.com/questions/10916284/how-to-encrypt-decrypt-data-in-php/10945097#10945097). –

+0

Спасибо за хороший ответ, он пролил некоторый свет, но, к сожалению, не помог. Согласно MSDN, CryptoAPI использует PKCS # 5, но вручную отключает его, а внедрение PKCS # 7 не влияет на результат (также наблюдая значения нераспределенных байтов в конце буфера после дешифрования, дает хороший отпечаток PKCS # 7). Я попытался использовать AES-128 и проверить результаты кодирования по примеру RFC3602 и получить правильный вывод (с встроенным и ручным дополнением) в PHP. Но не в программе C. Я предполагаю, что криптоконтекст неправильно сконфигурирован (имя поставщика, тип, хэш-алгоритм, возможно, что-то еще) –

+0

Забыл добавить ссылку [RFC3602] (https://www.ietf.org/rfc/rfc3602.txt) –

ответ

0

Зашифрованный вывод отличается. Это означает, что ключ, который в конечном счете используется, отличается или данные разные. Но данные одинаковы, поэтому ключ должен быть другим.

Это означает, что в процессе генерации ключей что-то другое. Возможно, OpenSSL может использовать какую-то другую функцию вывода ключей, которая здесь не видна. Попробуйте использовать стандартный алгоритм генерации ключей. Вместо хэширования попробуйте не использовать хэш.

+0

Спасибо за ответ , но я не совсем понимаю, как это сделать без хэша. Может быть, пример или ссылка? –

+0

В CryptDeriveKey прямо передайте пароль. – doptimusprime

+0

Я не думаю, что он должен работать таким образом. Здесь функция [объявление] (https://msdn.microsoft.com/en-us/library/windows/desktop/aa379916%28v=vs.85%29.aspx) из MSDN и нет упоминания о прямом пароле ввод в описание. 'BOOL WINAPI CryptDeriveKey (HCRYPTPROV hProv, ALG_ID Algid, HCRYPTHASH hBaseData, DWORD dwFlags, HCRYPTKEY * phKey);' –

0

Я, наконец, получил правильные результаты от CryptoAPI, импортировав ключ как PLAINTEXTBLOB.

Здесь не окончательные, но работоспособные исправления для функции 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; 
} 
Смежные вопросы