PHP 개발에서는 mcrypt 관련 기능을 사용하여 AES 암호화 및 복호화 작업을 쉽게 수행할 수 있습니다. 그러나 PHP7.1에서는 mcrypt 확장이 지원되지 않으므로 다른 구현을 찾아야 합니다. mcrypt를 openssl로 바꾸는 것은 이미 마이그레이션 매뉴얼에서 지적되었지만 구체적인 예는 제공되지 않습니다. 대부분의 시나리오를 대체할 수 있는 많은 예가 온라인에 있지만 자세한 내용은 설명되지 않습니다. 마찬가지로, 단순히 온라인 예제를 사용하면 특정 코드 시나리오에서 코드 교체 전후에 호환성 문제가 발생할 수 있습니다. 아래에서 구체적인 코드와 이유에 대해 이야기해 보겠습니다.
먼저 대체 코드를 직접 제공한 후, 코드에서 문제를 분석합니다. (이 글에서 분석한 알고리즘은 AES-128-CBC입니다.)
이 예시에서는 주로 padding의 차이로 인해 mcrypt를 사용하는 두 가지 방법을 보여줍니다. (padding에 대해서는 아래에서 설명하겠습니다.) . 전체 암호화 및 암호 해독 과정에서 더 완전한 코드는 자동으로 채우기 및 채우기 제거를 구현하고 간단한 코드는 채우기를 직접 무시하지만 두 방법 모두 실제 개발(7.1 이전 버전)에서는 정상적으로 실행될 수 있으므로 권장됩니다. 패딩을 추가합니다. 다음 구체적인 예를 참조하세요.
패딩이 없는 mcrypt
mcrypt 암호화:
$key = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; $iv = 'aaaaaaaaaaaaaaaa'; $data = 'dataString'; $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); mcrypt_generic_init($cipher, $key, $iv); $cipherText256 = mcrypt_generic($cipher, $data); mcrypt_generic_deinit($cipher); return bin2hex($cipherText256);
동일한 기능에 대한 openssl 암호화 코드:
$key = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; $iv = 'aaaaaaaaaaaaaaaa'; $data = 'dataString'; $data = $data . str_repeat("\x00", 16 - (strlen($data) % 16)); // 双引号可以解析asc-ii码\x00 return bin2hex(openssl_encrypt($data, "AES-256-CBC", $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv));
mcrypt with padding
엠크립트 암호화 :
$key = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; $iv = 'aaaaaaaaaaaaaaaa'; $data = 'dataString'; // 填充(移除填充反着移除即可) $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); $pad = $block - (strlen($data) % $block); if ($pad <= $block) { $char = chr($pad); $data .= str_repeat($char, $pad); } $cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, ''); mcrypt_generic_init($cipher, $key, $iv); $cipherText256 = mcrypt_generic($cipher, $data); mcrypt_generic_deinit($cipher); return bin2hex($cipherText256);
동일한 기능에 대한 openssl 암호화 코드:
$key = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; $iv = 'aaaaaaaaaaaaaaaa'; $data = 'dataString'; return bin2hex(openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv));
위 예제는 모두 성공적으로 실행됩니다. 첫 번째 예제(패딩을 사용하지 않고 openssl에서 패딩을 사용함)와 두 번째 예제(패딩 사용, openssl에서는 패딩이 사용되지 않습니다. 교체 전과 교체 후 출력이 동일하며 호환성 문제가 없습니다. 코드의 다양한 충전 방법에 따라 다양한 교체 솔루션을 선택할 수 있지만 세 가지 세부 사항을 설명해야 합니다
왜 충전이 있나요?
openssl로 교체한 후 알고리즘 이름이 다른 이유는 무엇인가요?
다음은 채우기와 알고리즘에 대한 자세한 분석입니다.
패딩이 있는 이유는 암호화 알고리즘에서 시작됩니다. AES-128-CBC 알고리즘에서는 암호화할 문자열이 길이가 16바이트마다 세그먼트로 분할되고 단계별로 계산되므로 16바이트 미만의 세그먼트가 채워지기 때문입니다. 따라서 두 가지 유형의 예제가 제공됩니다. 하나는 기본 채우기를 사용하는 것이고 다른 하나는 독립적 채우기를 사용하는 것입니다. openssl로 대체할 때 패딩 구성표를 선택하려면 mcrypt 및 openssl의 기본 및 자율 패딩에 대한 이해가 필요합니다.
mcrypt는 기본적으로 채워져 있습니다
php 소스코드를 보면 기본적으로 x00
으로 채워져 있는 것을 볼 수 있는데, 실제로는 x00에서 16비트 빈 문자열이 먼저 적용되는 것을 소스코드에서 확인할 수 있으므로 실제로는 초기화 시 각 바이트가 <code>x00
라고 할 수 있습니다. 패딩은 없지만 원래 x00
이므로 기본 패딩을 사용하여 얻은 암호화된 문자열은 다음 형식을 따릅니다.x00
进行填充,事实上,并非是以x00
进行填充,从源码中可以发现,首先申请了一个16位的空字符串,所以初始化时每位字节均为x00
, 实际上可以说其中并没有填充,只是它本来就是x00
,使用默认填充得到的加密字符串会是如下形式:
所以解密时则要移除多余的x00
。当然也可以懒一点,不移除x00
。 因为在php中字符串"stringx00"与字符串"string"除了长度不一样外,其他表现均一致,所以看起来并无区别,如下代码:
// 尾部包含若干个`\x00` 均可功输出true if ("string\x00" == "string") { // 用双引号可解析\x00 echo true; }
x00
填充后的示例:(请注意字符串的长度,由此可见用x00
填充会影响长度)
mcrypt自主填充
填充算法需以如下算法进行:
加入填充
/** * 填充算法 * @param string $source * @return string */ function addPKCS7Padding($source) { $source = trim($source); $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); $pad = $block - (strlen($source) % $block); if ($pad <= $block) { $char = chr($pad); $source .= str_repeat($char, $pad); } return $source; }
加入填充后字符串实际上如下形式:
移除填充
/** * 移去填充算法 * @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默认填充
其默认方式与标准的mcrypt的自主填充方式一致,所以在第二个示例中,对于使用了如上的填充算法后, 可直接使用openssl_encrypt替换,不会产生兼容问题。填充后的加密字符串如下形式:
需注意的是在openssl_encrypt
与openssl_decrypt
따라서 해독할 때 추가 x00
를 제거해야 합니다. 물론 게으른 상태에서 x00
을 제거하지 않을 수도 있습니다. 왜냐하면 php에서는 "stringx00"이라는 문자열과 "string"이라는 문자열이 길이가 다른 것 외에는 성능이 같기 때문에 차이가 없는 것 같습니다. 다음 코드는
// if we want to manage our own padding $padded_data = $data . str_repeat(' ', 16 - (strlen($data) % 16)); $encrypted = openssl_encrypt($padded_data, $method, $password, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv); $output = openssl_decrypt($encrypted, $method, $password, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv); var_dump(rtrim($output));
x00
입니다. 예: (문자열 길이에 주의하세요. x00
로 패딩하면 길이에 영향을 미치는 것을 볼 수 있습니다.)openssl_encrypt
및 openssl_decrypt
에 내장되어 있으므로 패딩을 독립적으로 구현해야 하는 경우가 아니면 직접 사용할 수 있습니다. , 고려할 필요가 없습니다. Filling🎜🎜🎜🎜🎜openssl 자율 채우기🎜openssl_encrypt提供了option参数以支持自主填充,但在查阅php源码中openssl的测试用例代码才找到正确用法:
// if we want to manage our own padding $padded_data = $data . str_repeat(' ', 16 - (strlen($data) % 16)); $encrypted = openssl_encrypt($padded_data, $method, $password, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv); $output = openssl_decrypt($encrypted, $method, $password, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv); var_dump(rtrim($output));
(备注:如上,OPENSSL_ZERO_PADDING
并非是为0填充的意思)
由此,我们就可以解释,在第一个示例中openssl_encrypt之前加入了自主点充\x00
的代码原因了
从以上的加、解密针对填充逻辑不同,针对上文中的示例可以很好地解释:
示例1:
mcrypt加密时未使用填充,故以\x00
进行了填充,所以在替换成openssl,需要自主实现\x00
填充。
示例2:
mcrypt加密时使用了标准的填充,同时openssl的填充方式也为PHP7.1에서 mcrypt를 대체하기 위해 openssl을 사용하는 방법 소개,故直接使用即可。
分析到这,可以发现,无论是何种填充策略都需注意在加密时加入填充,在解密时则必须要移除填充。至此,上文中示例中的填充相关则分析完成了,接下来我们再看看如何选择替换后的算法。
在以上的示例中,有一个问题在于,mcrypt中的AES-128-CBC算法,在openssl中怎么替换成了AES_256?
关于这一点, 我也未找到合理的解释,查看源码一时半会也没找到原因(能力有限~),但通过以下资料,还是完成了功能
openssl 解密 mcrypt AES 数据不兼容问题
Convert mcrypt_generic to openssl_encrypt Ask Question
若是有同学找到原因,欢迎给我留言,谢谢。
对于使用mcrypt AES 进行加密密的部分,若是在替换过程中问题, 可以从算法替换或填充这两方面着手考虑下。同时还是一必须满足的条件是根据不同的填充方式选择, 替换最重要的就要考虑兼容问题,保证替换后不发生任何改变。 虽然只是只是有细微的差别----尾部几个字符串的不同,但若是在多平台中同时进行修改也是一件麻烦事,但变动越少风险越小。
本文只是针对AES算法进行了简单说明,对于其他算法是否适用还有待研究。
以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!
相关推荐:
위 내용은 PHP7.1에서 mcrypt를 대체하기 위해 openssl을 사용하는 방법 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!