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_簇 的转换。


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

查看所有标签

猜你喜欢:

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

Web信息架构(第3版)

Web信息架构(第3版)

[美] Peter Morville、Louis Rosenfeld / 陈建勋 / 电子工业出版社 / 2013-10 / 99.00元

本书内容涵盖了信息架构基本原理和实践应用的方方面面。全书共7个部分,包括信息架构概述、信息架构的基本原理、信息架构的开发流程和方法论、信息架构实践、信息架构与组织、两个案例研究,以及参考资料清单。 本书兼具较高的理论价值和实用价值,曾被Web设计领域多本书籍重点推荐,是信息架构领域公认的经典书籍,不论新手还是专家都能各取所需。本书可供Web设计与开发者、Web架构师、网站管理者及信息管理相关......一起来看看 《Web信息架构(第3版)》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

MD5 加密
MD5 加密

MD5 加密工具