Здесь у меня есть программа на С ++, которая кодирует строку, и мне нужно расшифровать в 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');
    ?>
1
Smit Modi 30 Май 2019 в 13:51

2 ответа

Лучший ответ

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

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

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

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-коде для расшифровки. Это делается путем передачи тега аутентификации в качестве шестого параметра {{ X0 } } -метод :

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

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

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

f7c18e8b99587f3063383d68230c0e35

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

0
Topaco 31 Май 2019 в 22:13

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

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

Больше информации о потенциальной проблеме

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

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

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

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

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

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

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

  • либо добавьте необходимые операторы для использования двоичного режима с обеих сторон.
  • или добавьте Base64 кодировку для преобразования двоичного кода в надежную строку ascii на стороне записи и повторного преобразования Base64 в двоичный файл на стороне чтения (openssl обеспечивает кодировку base64 и PHP также имеет все необходимое)
0
Christophe 30 Май 2019 в 12:36