Openssl и Windows CryptoAPI проблема совместимости

0

У меня проблема с зашифрованной связью между приложением 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=
  • 0
    Я думаю, что вы забыли дополнить свои данные с помощью pkcs7; см. также этот ответ .
  • 0
    Спасибо за хороший ответ, он пролил некоторый свет, но, к сожалению, не помог. Согласно MSDN CryptoAPI использует PKCS # 5, но его ручное отключение и реализация дополнения PKCS # 7 не имеют никакого значения для результата (также наблюдение значений нераспределенных байтов в конце буфера после дешифрования дает хороший отпечаток PKCS # 7). Я попытался использовать AES-128 и проверить результаты кодирования на примере RFC3602 и получить правильный вывод (со встроенным и ручным заполнением) в PHP. Но не в программе на Си. Я полагаю, что крипто-контекст неправильно настроен (имя провайдера, тип, алгоритм хеширования, может быть что-то еще)
Показать ещё 1 комментарий
Теги:
openssl
cryptoapi
mismatch

2 ответа

0
Лучший ответ

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

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

  • 0
    Спасибо за ответ, но я не совсем понимаю, как это сделать без хэша. Может быть пример или ссылка?
  • 0
    В CryptDeriveKey прямо передайте пароль.
Показать ещё 3 комментария
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;
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню