PHP 将 mcrypt_encrypt 迁移至 openssl_encrypt 的方法

栏目: PHP · 发布时间: 5年前

内容简介:注:php 的 mcrypt_ 函数簇在 7.1.0 版本中开始 deprecated,并在 7.2.0 版本中彻底废弃。其实在 2015 就已经开始建议大家使用 openssl_encrypt/openssl_decrypt 来代替 mcrypt_encrypt/mcrypt_decrypt,缓冲了 N 久,这一天终于在 7.2.0 版本上到来了。为什么要提及迁移,比如 A & B 两套系统使用 AES 加密做数据传输,且 A 作为客户端,B 则是第三方的服务,且 A 已经在使用 7.2.0+ 版本,而

注:php 的 mcrypt_ 函数簇在 7.1.0 版本中开始 deprecated,并在 7.2.0 版本中彻底废弃。其实在 2015 就已经开始建议大家使用 openssl_encrypt/openssl_decrypt 来代替 mcrypt_encrypt/mcrypt_decrypt,缓冲了 N 久,这一天终于在 7.2.0 版本上到来了。

为什么要提及迁移,比如 A & B 两套系统使用 AES 加密做数据传输,且 A 作为客户端,B 则是第三方的服务,且 A 已经在使用 7.2.0+ 版本,而 B 作为长期运行的服务仍在使用 7.1.0-,那我们能做的就是在 A 上使用 openssl_簇 原样的实现 mcrypt_簇 的加解密功能,以便兼容 B 服务,且 mcrypt_簇 是有一些需要多加注意的地方,否则迁移之路略微坎坷。

mcrypt_簇 虽说被遗弃了,但 文档页 上依然有很多值得注意的文档贡献,有助于我们将 mcrypt_簇 迁移至 openssl_簇,大家应该仔细看一下。

2.Also, MCRYPT_RIJNDAEL_256 is not AES-256, it's a different variant of the Rijndael block cipher. If you want AES-256 in mcrypt, you have to use MCRYPT_RIJNDAEL_128 with a 32-byte key. OpenSSL makes it more obvious which mode you are using (i.e. 'aes-128-cbc' vs 'aes-256-ctr').
3.OpenSSL also uses PKCS7 padding with CBC mode rather than mcrypt's NULL byte padding. Thus, mcrypt is more likely to make your code vulnerable to padding oracle attacks than OpenSSL.

1、即刻起,应尽可能的使用 openssl_簇 代替 mcrypt_簇 来实现数据的加密功能。

2、MCRYPT_RIJNDAEL_256 并不是 AES-256,如果想使用 mcrypt_簇 实现 AES-256,则你应该使用 MCRYPT_RIJNDAEL_128 算法 + 32位的 key,openssl_簇 则更为清晰的明确了各种模式。这里我整理了一下对应关系供大家参考:

MCRYPT_RIJNDAEL_128 & CBC + 16位Key = openssl_encrypt(AES-128-CBC, 16位Key) = AES-128
MCRYPT_RIJNDAEL_128 & CBC + 24位Key = openssl_encrypt(AES-192-CBC, 24位Key) = AES-192
MCRYPT_RIJNDAEL_128 & CBC + 32位Key = openssl_encrypt(AES-256-CBC, 32位Key) = AES-256

(注:AES-128, 192 and 256 的加密 key 的长度分别为 16, 24 and 32 位)

openssl_簇 的确更为清晰明了,而且 mcrypt_get_key_size 得到的 key 长度都是 32 位,所以不太靠谱。

iv 到是会根据 cipher 方法变更 16 、24、32,但 openssl_簇的 AES cipher 的 iv 长度适中要求为 16 位。

所以,我们为了最大的适配,即便现在不会再用,也要知道 mcrypt_簇 实现 AES-128/192/256 的标准方式为:

  • cipher 选 MCRYPT_RIJNDAEL_128
  • 根据 cipher 和 mode 获取 iv 长度并生成 iv
  • 根据实际业务来确定 key 长度: AES-128 16位 / AES-192 24位 / AES-256 32位,而不是使用 mcrypt_get_key_size

3、这一点其实蛮重要的,涉及 加密算法数据块 & 填充算法PKCS7 的概念。我在支付宝 alipay SDK 中有看到此算法的实现,虽然 sdk 中仍然使用的 mcrypt_簇,但已结合了 PKCS7 填充算法,为什么要这样做呢?其一是为了安全&兼容,php mcrypt 会使用 null('0') 对数据块进行填充,java/.net 则是使用 PKCS7。其二则是为后期迁移至 openssl_簇 的准备,openssl 的填充模式默认是使用 PKCS7 填充的(当然也可以指定使用 null('0') 填充模式,但极力不推荐的)。

mcrypt_encrypt/mcrypt_decrypt

相关的支持函数

// 支持的算法 rijndael-128|rijndael-192|rijndael-256(此算法并非AES-256,需使用rijndael-128 + key32byte实现)
mcrypt_list_algorithms()
// 支持的模式 cbc ecb 等
mcrypt_list_modes()
// 算法所对应的 key 长度:AES-128, 192 and 256 的加密 key 的长度分别为 16, 24 and 32 位
mcrypt_get_key_size(string $cipher , string $mode)
// 算法所对应的加密向量 iv 的长度
mcrypt_get_iv_size(string $cipher , string $mode)
// 生成 iv
mcrypt_create_iv(mcrypt_get_iv_size(string $cipher , string $mode))
// 加密算法数据块的大小 主要用于填充算法
mcrypt_get_block_size(string $cipher , string $mode)

PKCS7 填充算法的实现

/**
 * 填充算法
 * @param string $source
 * @return string
 */
function addPKCS7Padding($source, $cipher = MCRYPT_RIJNDAEL_128, $mode = MCRYPT_MODE_CBC)
{
    $source     = trim($source);
    // 获取加密算法数据块大小 用于计算需要填充多少位
    $block_size = mcrypt_get_block_size($cipher, $mode);
    $pad        = $block_size - (strlen($source) % $block_size);
    if ($pad <= $block_size) {
        $char = chr($pad);
        $source .= str_repeat($char, $pad);
    }
    return $source;
}

/**
 * 移去填充算法
 * @param string $source
 * @return string
 */
function stripPKCS7Padding($source)
{
    $source = trim($source);
    $char   = substr($source, -1);
    $num    = ord($char);
    if ($num == 62) {
        return $source;
    }

    $source = substr($source, 0, -$num);
    return $source;
}

openssl_encrypt/openssl_decrypt

简单讲解一下日常开发中用到的参数

/**
 * $data 待加密内容
 * $method 加密算法
 * $key 加密key
 * $options 数据块填充模式
 * $iv 加密向量
**/
openssl_encrypt(string $data, string $method, string $key[, int $options = 0[, string $iv = ""
[, string &$tag = NULL[, string $aad = ""[, int $tag_length = 16 ]]]]]): string

openssl_decrypt(string $data, string $method, string $key[, int $options = 0[, string $iv = ""
[, string $tag = "" [, string $aad = "" ]]]] ) : string

这里需要特别注意的就是 options 选项,很多人 mcrypt_簇 迁移至 openssl_簇 时二者加密结果内容不一致,大都是此处没有搞清楚的原因。options 共 3 个值可选

0 默认值 使用 PKCS7 填充算法,不对加密结果进行 base64encode

1 OPENSSL_RAW_DATA 使用 PKCS7 填充算法,且对加密结果进行 base64encode

2 OPENSSL_ZERO_PADDING 使用 null('0') 进行填充,且对加密结果进行 base64encode

所以要注意填充算法及对结果是否进行了 base64encode 编码。

mcrypt_簇 迁移至 openssl_簇

mcrypt_簇

/**
 * 加密算法
 * @param  string $content    待加密数据
 * @param  string $key    加密key
 * @param  string $iv     加密向量
 * @param  string $cipher 加密算法
 * @param  string $mode   加密模式
 * @return string         加密后的内容且base64encode
 */
function encrypt($content, $key, $iv, $cipher = MCRYPT_RIJNDAEL_128, $mode = MCRYPT_MODE_CBC)
{
    //AES, 128 模式加密数据 CBC
    $content           = addPKCS7Padding($content);
    $content_encrypted = mcrypt_encrypt($cipher, $key, $content, $mode, $iv);
    return base64_encode($content_encrypted);
}

/**
 * 解密算法
 * @param  [type] $content [description]
 * @param  [type] $key     [description]
 * @param  [type] $iv      [description]
 * @param  [type] $cipher  [description]
 * @param  [type] $mode    [description]
 * @return [type]          [description]
 */
function decrypt($content_encrypted, $key, $iv, $cipher = MCRYPT_RIJNDAEL_128, $mode = MCRYPT_MODE_CBC)
{
    //AES, 128 模式加密数据 CBC
    $content_encrypted = base64_decode($content_encrypted);
    $content           = mcrypt_decrypt($cipher, $key, $content_encrypted, $mode, $iv);
    $content           = stripPKSC7Padding($content);
    return $content;
}

/**
 * PKCS7填充算法
 * @param string $source
 * @return string
 */
function addPKCS7Padding($source, $cipher = MCRYPT_RIJNDAEL_128, $mode = MCRYPT_MODE_CBC)
{
    $source = trim($source);
    $block  = mcrypt_get_block_size($cipher, $mode);
    $pad    = $block - (strlen($source) % $block);
    if ($pad <= $block) {
        $char = chr($pad);
        $source .= str_repeat($char, $pad);
    }
    return $source;
}
/**
 * 移去PKCS7填充算法
 * @param string $source
 * @return string
 */
function stripPKSC7Padding($source)
{
    $source = trim($source);
    $char   = substr($source, -1);
    $num    = ord($char);
    if ($num == 62) {
        return $source;
    }

    $source = substr($source, 0, -$num);
    return $source;
}

openssl_簇

转换实例

以 AES-128 为例

// 固定使用此算法 然后通过 key 的长度来决定具体使用的是何种 AES
$cipher = MCRYPT_RIJNDAEL_128;
$mode   = MCRYPT_MODE_CBC;

// openssl_簇 iv 固定为 16 位,mcrypt_簇 MCRYPT_RIJNDAEL_128 是 16位
// 但改为 MCRYPT_RIJNDAEL_192/256 就是 24/32 位了,会不兼容 openssl_簇
// 所以务必注意向量长度统一固定 16 位方便两套算法对齐
// $iv = mcrypt_create_iv(mcrypt_get_iv_size($cipher, $mode), MCRYPT_RAND);

// 根据需要自行定义相应的 key 长度 aes-128=16 aes-192=24 aes-256=32
$key = '0123456789012345';
// 固定为 16 位
$iv  = '0123456789012345';

$content = "hello world";

// mcrypt 加解密
$mcrypt_data = encrypt($content, $key, $iv, $cipher, $mode);
var_dump($mcrypt_data);
$content = decrypt($mcrypt_data, $key, $iv, $cipher, $mode);
var_dump($content);

// mcrypt 时使用了 PKCS7 填充 并对结果 base64encode
// 如果 +PKCS7 +base64encode 则 option = 0
// 如果 +PKCS7 -base64encode 则 option = 1
// 如果 -PKCS7 +base64encode 则 option = 2
$openssl_data = openssl_encrypt($content, "AES-128-CBC", $key, 0, $iv)
var_dump($openssl_data);
$content = openssl_decrypt($openssl_data, "AES-128-CBC", $key, 0, $iv)
var_dump($content);

// 相互转换
$content = openssl_decrypt($mcrypt_data, "AES-128-CBC", $key, 0, $iv)
var_dump($content);
$content = decrypt($openssl_data, $key, $iv, $cipher, $mode);
var_dump($content);

总结

1、PKCS7 填充算法。

2、openssl_encrypt / openssl_decrypt 三种模式所表示的 PKCS7/base64encode。

3、mcrypt_簇 的 cipher/mode 同 openssl_簇 的转换。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

微机原理与接口技术

微机原理与接口技术

李文英、李勤、刘星、宋蕴新 / 清华大学出版社 / 2001-9 / 26.00元

《高等院校计算机应用技术规划教材•应用型教材系列•微机原理与接口技术》是“高职高专计算机系列教材”之一。全书包括微机原理、汇编语言、接口技术三部分内容。微机原理部分讲述了80x86的内部结构及工作原理、半导体存储器及其系统、微型机总线结构等。汇编语言部分讲述了指令系统、编程技巧。接口技术部分讲述了中断系统、中断控制器、并行接口、串行接口、DMA控制器、定时器,以及A/D、D/A转换器等常用芯片的硬......一起来看看 《微机原理与接口技术》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具