В чем проблема с шифрованием в C ++ и расшифровкой в ​​php - PullRequest
1 голос
/ 30 мая 2019

Здесь у меня есть программа на С ++, которая кодирует строку, и мне нужно расшифровать в php.Я проверил, что ключ и iv одинаковы в обеих программах, но в команде openssl_decrypt () все равно ложно.

    int main(int argc, char** args)
    {
        unsigned char *salt = (unsigned char*)"12345678";                        
        unsigned char *data = (unsigned char*)"123456789123450";                 
        unsigned int count        = 5;                                           
        int dlen                  = strlen((char*)data);                         
        unsigned int ksize        = 16;
        unsigned int vsize        = 12;                                          
        unsigned char *key        = new unsigned char[ksize];                    
        unsigned char *iv         = new unsigned char[vsize];                    

        int ret = EVP_BytesToKey( EVP_aes_128_gcm() , EVP_sha1(), salt, data, dlen, count, key, iv);

        const EVP_CIPHER*     m_cipher = EVP_aes_128_gcm();
        EVP_CIPHER_CTX* m_encode;                                                
        EVP_CIPHER_CTX* m_decode;                                                
        if (!(m_encode = EVP_CIPHER_CTX_new()))                                  
           cout << "ERROR :: In encode Initiallization"<< endl; 

        EVP_EncryptInit_ex(m_encode, m_cipher, NULL, key, iv);

        if (!(m_decode = EVP_CIPHER_CTX_new()))
            cout << "ERROR :: In decode Initiallization"<< endl;
        EVP_DecryptInit_ex(m_decode, m_cipher, NULL, key, iv);
        unsigned char* plain = (unsigned char*)"My Name IS  DON !!!";
        int len  = strlen((char*)plain);
        unsigned char* encData = new unsigned char[len];

        int c_len = len;
        int f_len = 0;
        EVP_EncryptInit_ex(m_encode, NULL, NULL, NULL, NULL);
        EVP_EncryptUpdate(m_encode, encData, &c_len, plain, len);
        EVP_EncryptFinal_ex(m_encode, encData + c_len, &f_len);

        len = c_len + f_len;

        cout << string( encData, encData + len)<< endl;
    }

И следующий код расшифровки в php.«./abc_enc.txt» содержит строку шифрования кода C ++.Как я упоминал выше, я получаю одинаковые ключи и iv для обеих программ, но функция openssl_decrypt возвращает false.Может кто-нибудь разобраться в чем ошибка?

    <?
    function EVP_BytesToKey($salt, $password) {
        $ivlen = 12;
        $keylen = 16;
        $iterations = 5;
        $hash = "";
        $hdata = "";
        while(strlen($hash)<$ivlen+$keylen)
        {
            $hdata .= $password.$salt;
            $md_buf = openssl_digest($hdata, 'sha1');
            for ($i = 1; $i < $iterations; $i++) {
                $md_buf = openssl_digest ( hex2bin($md_buf),'sha1');
            }
            $hdata = hex2bin($md_buf);
            $hash.= $hdata;
         }
         return $hash;
    }
    function decrypt($ivHashCiphertext, $password) {
         $method = "aes-128-gcm";
         $salt = "12345678";
         $iterations = 5;
         $ivlen = openssl_cipher_iv_length($method);
         $ciphertext = $ivHashCiphertext;
         $genKeyData = EVP_BytesToKey($salt, $password);
         $keylen = 16;
         $key = substr($genKeyData,0,$keylen);
         $iv  = substr($genKeyData,$keylen,$ivlen);
         //var_dump($key);
         //var_dump($iv);
         $ret = openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv);
         var_dump($ret);
         return $ret;
    }
    $file = './abc_enc.txt';
    $fileData = (file_get_contents($file));
    $encrypted = $fileData;
    $decrypted = decrypt($encrypted, '123456789123450');
    ?>

Ответы [ 2 ]

0 голосов
/ 01 июня 2019

Режим GCM обеспечивает как конфиденциальность, так и аутентичность. Для проверки подлинности GCM-режим использует тег аутентификации и определяет длину между вкл. 12 и 16 байт для тега. Степень аутентификации зависит от длины тега, т. Е. Чем длиннее тег, тем надежнее доказательство подлинности.

Однако в текущем C ++ - коде тег аутентификации не определен! Это означает, что одна из основных функций режима GCM, аутентификация, не используется.

Хотя дешифрование в C ++ с использованием EVP не зависит от аутентификации (это означает, что дешифрование также выполняется, даже если теги аутентификации различаются), дешифрование в PHP с использованием openssl_decrypt выполняется только в случае успешной аутентификации, т.е. в PHP тег аутентификации является обязательным для расшифровки. Поэтому тег аутентификации должен быть определен в C ++ - коде. Для этого после EVP_EncryptFinal_ex -call необходимо добавить следующий код:

unsigned int tsize = 16;
unsigned char *tag = new unsigned char[tsize];
EVP_CIPHER_CTX_ctrl(m_encode, EVP_CTRL_GCM_GET_TAG, tsize, tag);

Здесь используется размер тега 16 байт. Кроме того, тег аутентификации должен использоваться в PHP-коде для расшифровки. Это делается путем передачи тега аутентификации в качестве шестого параметра openssl_decrypt -метода:

$ret = openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);

Расшифровка может быть выполнена, только если тег, используемый для расшифровки, соответствует тегу, используемому для шифрования

Для данных в опубликованном примере код C ++ генерирует следующий тег аутентификации (в виде шестнадцатеричной строки):

f7c18e8b99587f3063383d68230c0e35

Наконец, более подробное объяснение AES-GCM с OpenSSL можно найти здесь для шифрования и дешифрования (включая рассмотрение тега аутентификации).

0 голосов
/ 30 мая 2019

Короче говоря

Я не openSSL и не специалист по PHP.Но на первый взгляд кажется, что у вас могут возникнуть проблемы, потому что вы читаете и записываете двоичные данные, используя файлы в текстовом режиме .

Дополнительная информация о потенциальной проблеме

Зашифрованные данные, полученные из кода C ++, являются двоичными данными.Ваш код не показывает, как вы пишете файл.Но если вы явно не запросите файл в двоичном режиме , вы получите файл в текстовом режиме.

Это может вызвать множество проблем при записи данных, так как текстовый режим позволяет выполнять зависящие от ОС преобразования.Типичными примерами являются символы со значением 0x0A (новые строки), пропускающие завершающий 0x20 (пробел), добавление 0x0A в конце файла, если его нет, и аналогичное нежелательное преобразование.

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

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

Как ее решить?

Сначала проверьте с помощью бинарного редактора содержимое файла, чтобы увидеть, соответствует ли оно ожиданиям.Или проверьте ожидаемую длину зашифрованных исходных данных, длину файла и длину загруженного контента.Если они все совпадают, мой ответ не имеет значения.

Если это уместно, или если вы намереваетесь рано или поздно разрешить межплатформенный обмен (например, клиент Windows связывается с сервером Linux), то вы можете:

  • либо добавитьнеобходимые операторы для использования двоичного режима с обеих сторон.
  • или добавьте кодировку Base64 , чтобы преобразовать двоичный файл в надежную строку ascii на стороне записи и преобразовать Base64 в двоичный файл на стороне чтения (openssl обеспечивает кодирование base64 и PHP имеет все необходимое )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...