OpenSSL 在 C++ 和 PHP 中的不一致

OpenSSL inconsistency in C++ and PHP

本文关键字:不一致 PHP C++ OpenSSL      更新时间:2023-10-16

我正在尝试编写一段代码来加密/解密将存储在数据库中的数据(例如密码)。目标是数据可以在C++,PHP和Java中使用。问题是我没有在C++和PHP中获得相同的加密数据(我不是在处理Java jet)。C++代码:

void CSSLtestDlg::TryIt()
{
    CString msg, tmp;
    /* A 256 bit key */
    unsigned char *key = (unsigned char *)"01234567890123456789012345678901";
    /* A 128 bit IV */
    unsigned char *iv = (unsigned char *)"01234567890123456";
    /* Message to be encrypted */
    unsigned char *plaintext = (unsigned char *)"Some_plain_text";
    msg = "Original text is: Some_plain_text";
    /* Buffer for ciphertext. Ensure the buffer is long enough for the
    * ciphertext which may be longer than the plaintext, dependant on the
    * algorithm and mode
    */
    unsigned char ciphertext[128];
    /* Buffer for the decrypted text */
    unsigned char decryptedtext[128];
    int decryptedtext_len, ciphertext_len;
    /* Initialise the library */
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
    OPENSSL_config(NULL);
    /* Encrypt the plaintext */
    ciphertext_len = encryptIt (plaintext, strlen ((char *)plaintext), 
        key, iv, ciphertext);
    ciphertext[ciphertext_len] = '';
    tmp.Format("nEncrypted text (in HEX) is: %s", StrToHex(ciphertext));
    msg += tmp;
    /* Decrypt the ciphertext */
    decryptedtext_len = decryptIt(ciphertext, ciphertext_len, 
        key, iv, decryptedtext);
    /* Add a NULL terminator. We are expecting printable text */
    decryptedtext[decryptedtext_len] = '';
    tmp.Format("nDecrypted text is: %s", decryptedtext);
    msg += tmp;
    AfxMessageBox(msg);
    /* Clean up */
    EVP_cleanup();
    ERR_free_strings();
}
int CSSLtestDlg::encryptIt(unsigned char *plaintext, int plaintext_len, 
 unsigned char *key,  unsigned char *iv, unsigned char *ciphertext)
{
  EVP_CIPHER_CTX *ctx;
  int len;
  int ciphertext_len;
  /* Create and initialise the context */
  if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
  /* Initialise the encryption operation. IMPORTANT - ensure you use a key
   * and IV size appropriate for your cipher
   * In this example we are using 256 bit AES (i.e. a 256 bit key). The
   * IV size for *most* modes is the same as the block size. For AES this
   * is 128 bits */
  if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
    handleErrors();
  /* Provide the message to be encrypted, and obtain the encrypted output.
   * EVP_EncryptUpdate can be called multiple times if necessary
   */
  if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
    handleErrors();
  ciphertext_len = len;
  /* Finalise the encryption. Further ciphertext bytes may be written at
   * this stage.
   */
  if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors();
  ciphertext_len += len;
  /* Clean up */
  EVP_CIPHER_CTX_free(ctx);
  return ciphertext_len;
}
int CSSLtestDlg::decryptIt(unsigned char *ciphertext, int ciphertext_len, 
unsigned char *key, unsigned char *iv, unsigned char *plaintext)
{
  EVP_CIPHER_CTX *ctx;
  int len;
  int plaintext_len;
  /* Create and initialise the context */
  if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
  /* Initialise the decryption operation. IMPORTANT - ensure you use a key
   * and IV size appropriate for your cipher
   * In this example we are using 256 bit AES (i.e. a 256 bit key). The
   * IV size for *most* modes is the same as the block size. For AES this
   * is 128 bits */
  if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv))
    handleErrors();
  /* Provide the message to be decrypted, and obtain the plaintext output.
   * EVP_DecryptUpdate can be called multiple times if necessary
   */
  if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
    handleErrors();
  plaintext_len = len;
  /* Finalise the decryption. Further plaintext bytes may be written at
   * this stage.
   */
  if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) handleErrors();
  plaintext_len += len;
  /* Clean up */
  EVP_CIPHER_CTX_free(ctx);
  return plaintext_len;
}

结果:

Original text is: Some_plain_text
Encrypted text (in HEX) is: 39D35A2E6EF8F8E36F3BC1FD8B6B32
Decrypted text is: Some_plain_text

PHP代码:

$key = "01234567890123456789012345678901";
$iv = "01234567890123456";
$msg = "Some_plain_text";
if(function_exists('openssl_encrypt'))
{
    echo("Original text is:" . $msg . "<br>");
    $encrypted_msg = openssl_encrypt($msg, "aes-256-cbc", $key, OPENSSL_RAW_DATA, $iv);
    if ($encrypted_msg == FALSE)
        $log->logDebug("openssl_encrypt error!");
    else
    {
        echo("Encrypted text (in HEX) is:" . strToHex($encrypted_msg) . "<br>");
        $decrypted_msg = openssl_decrypt($encrypted_msg, "aes-256-cbc", $key, OPENSSL_RAW_DATA , $iv);
        echo("Decrypted text is:" . $decrypted_msg . "<br>");
    }
}
else echo("openssl_encrypt doesn't exists!<br>");
function strToHex($string)
{
    $hex = '';
    for ($i=0; $i<strlen($string); $i++)
    {
        $ord = ord($string[$i]);
        $hexCode = dechex($ord);
        $hex .= substr('0'.$hexCode, -2);
    }
    return strToUpper($hex);
}

结果:

Original text is:Some_plain_text
Encrypted text (in HEX) is:39D35A2E6EF8F8E36F3BC1FD8B06B302
Decrypted text is:Some_plain_text

所以我得到 39D35A2E6EF8F8E36F3BC1FD8B6B32 (C++) 与 39D35A2E6EF8F8E36F3BC1FD8B06B302 (PHP) 相反。最后,十六进制数据将存储在数据库中,并在以后检索。提前感谢所有的帮助,对不起我的糟糕语言。

所以问题出在 StrToHex() 函数C++代码中(我忘了在原始问题中发布),所以这里是:

CString StrToHex(unsigned char* str)
{       
    CString tmp;
    CString csHexString;    
    int nCount = strlen((char *)str);            
    for( int nIdx =0; nIdx < nCount; nIdx++ )      
    {          
        int n = str[nIdx];
        //tmp.Format( _T("%X"), n ); --> this is wrong
        tmp.Format( _T("%02X"), n ); // --> this is OK
        csHexString += tmp;      
    }   
    return csHexString;
}

第二个输出(B6/B06)中还有另一个额外的0。 这些可能是输出中唯一的低字节,并且您有不同的格式设置。

如果您显示C++ StrToHex,则可能是一个简单的格式字符串修复,例如 %x 到 %0x。