PHP加解密算法使用 openssl 替换 mcrypt 扩展的一个小坑

由于 PHP 7.2 不再支持 mcrypt,因此需要将 mcrypt 替换为 openssl。

但是在替换发现,php 的 openssl 和 mcrypt 实现有一些不同,有几个坑需要注意。

  1. mcrypt 中的 RIJNDAEL_128 算法是 AES 算法的超集,RIJNDAEL_128 的 128 指的是 Block Size,而 AES-128 中的 128 是 Key Size。因此,同是 RIJNDAEL_128 算法,如果你使用的 Key 是 128 位的(16 个字符)那就等同 AES-128。如果 Key 是 192 位的(24 个字符),那就等同与 AES-192。如果 Key 是 256 位的(32 个字符),则等同与 AES-256
  2. 如果使用 RIJNDAEL_256,则无法对应到 AES 算法。因为 AES 固定了 Block Size 是 128,没有 256 的选择
  3. php 的 openssl 实现的 AES 算法,只有 PKCS#7 padding 和 no padding 两个选择,而 mcrypt 默认是 zero padding。因此,如果原先没有手动处理 padding,则切换到 openssl 的时候需要启用 no padding 并手动处理 padding。
  4. 一些函数的替换:mcrypt_get_iv_size  替换为 openssl_cipher_iv_lengthmcrypt_create_iv  替换为 openssl_random_pseudo_bytes

修改前后代码如下:

<?php
define('ENCRYPT_METHOD', 'AES-256-CBC');
define('IV_SIZE', openssl_cipher_iv_length(ENCRYPT_METHOD));

function encrypt_mcrypt($payload, $key)
{
  // 初始化 IV
  $iv = openssl_random_pseudo_bytes(IV_SIZE);
  // 调用加密
  $crypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $payload, MCRYPT_MODE_CBC, $iv);
  // 拼接 IV 和 密文
  $combo = $iv . $crypt;
  // 编码拼接后的密文
  $garble = base64_encode($combo);
  return $garble;
}

function decrypt_mcrypt($garble, $key)
{
  // 解码密文
  $combo = base64_decode($garble);
  // 根据 IV SIZE 提取 IV
  $iv = substr($combo, 0, IV_SIZE);
  // 获取剩余密文
  $crypt = substr($combo, IV_SIZE);
  // 解密
  $payload = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $crypt, MCRYPT_MODE_CBC, $iv);
  return $payload;
}

function encrypt_openssl($payload, $key)
{
  // 初始化 IV
  $iv = openssl_random_pseudo_bytes(IV_SIZE);
  // 对密文进行 padding, 16 = 128 / 8
  if (strlen($payload) % 16) {
    $payload = str_pad(
      $payload,
      strlen($payload) + 16 - strlen($payload) % 16,
      "\0"
    );
  }
  // 加密
  $encryptedMessage = openssl_encrypt($payload, ENCRYPT_METHOD, $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $iv);
  // 编码,拼接 IV
  return base64_encode($iv . $encryptedMessage);
}
function decrypt_openssl($garble, $key)
{
  // 解码密文
  $raw = base64_decode($garble);
  // 根据 IV SIZE 提取 IV
  $iv = substr($raw, 0, IV_SIZE);
  // 获取剩余密文
  $data = substr($raw, IV_SIZE);
  // 解密
  return openssl_decrypt($data, ENCRYPT_METHOD, $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $iv);
}

$key = md5("password");
echo (decrypt_openssl(encrypt_mcrypt("4db8c44de8f16b4f0c39ef3b38d47c6bdd000082b3b628ee25c20b308d02cf29", $key), $key) . "\n");
echo (decrypt_mcrypt(encrypt_openssl("4db8c44de8f16b4f0c39ef3b38d47c6bdd000082b3b628ee25c20b308d02cf29", $key), $key) . "\n");

 

CC BY-NC-SA 4.0 PHP加解密算法使用 openssl 替换 mcrypt 扩展的一个小坑 by 桔子小窝 is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据