php - 如何在 PHP 中解密由 SQL Server 的 EncryptByPassPhrase() 加密的字符串?
问题描述
我有一个加密的字符串及其密钥,它是使用 SQL Server 使用“EncryptByPassPhrase”创建的,我如何在 PHP 中解密它?
我已阅读“EncryptByPassPhrase”的文档,其中指出这是 128 长度的三重 DES 加密。我尝试了 PHP 的 3DES 解密,但它没有返回预期的输出。
MS SQL 中的加密是通过
declare @encrypt varbinary(200)
select @encrypt = EncryptByPassPhrase('key', 'taskseq=10000&amt=200.5' )
select @encrypt
我在PHP中解密它如下:
function decryptECB($encrypted, $key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_3DES, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
// decrypting
$stringText = mcrypt_decrypt(MCRYPT_3DES, $key, $encrypted,
MCRYPT_MODE_ECB, $iv);
return $stringText;
}
解决方案
我冒昧地将这个 Stack Overflow 答案翻译成 PHP。
这是结果:
<?php
// SQL Server's DecryptByPassphrase translated into PHP.
function decrypt(string $data, string $password): ?string {
// SQL Server <2017 uses SHA1 for the key and the DES-EDE-CBC crypto algorithm
// whereas SQL Server >= 2017 uses SHA256 and AES-256-CBC.
// Version 1 is the SHA1 + DES-EDE-CBC version, Version 2 is the AES-256-CBC version.
// Version is stored in the first four bytes as a little endian int32.
$version_bytes = substr($data, 0, 4);
$version = unpack('V', $version_bytes)[1];
// Password must be converted to the UTF-16LE encoding.
$passwordUtf16 = mb_convert_encoding($password, 'UTF-16LE');
if ($version === 1) {
// Key is hashed using SHA1, The first 16 bytes of the hash are used.
$key = substr(hash('sha1', $passwordUtf16, true), 0, 16);
$method = 'des-ede-cbc';
$options = OPENSSL_RAW_DATA;
$iv = substr($data, 4, 8); // initialization vector of 8 bytes
$encrypted_data = substr($data, 12); // actual encrypted data
} else if ($version === 2) {
// Key is hashed using sha256. Key length is always 32 bytes.
$key = hash('sha256', $passwordUtf16, true);
$method = 'aes-256-cbc';
$options = OPENSSL_RAW_DATA;
$iv = substr($data, 4, 16); // iv of 16 bytes
$encrypted_data = substr($data, 20);
} else {
throw new \InvalidArgumentException('Invalid version');
}
$decrypted = openssl_decrypt($encrypted_data, $method, $key, $options, $iv);
if ($decrypted === false) {
return null;
}
// First 8 bytes contain the magic number 0xbaadf00d and the length
// of the decrypted data
$decrypted = substr($decrypted, 8);
// UTF-16 encoding should be converted to UTF-8. Note that
// there might be a better way to accomplish this.
$isUtf16 = strpos($decrypted, 0) !== false;
if ($isUtf16) {
return mb_convert_encoding($decrypted, 'UTF-8', 'UTF-16LE');
}
return $decrypted;
}
// A version 1 encrypted string. Taken directly from the Stack Overflow answer linked above
$s = '010000007854E155CEE338D5E34808BA95367D506B97C63FB5114DD4CE687FE457C1B5D5';
$password = 'banana';
$bin = hex2bin($s);
$d = decrypt($bin, $password);
var_dump($d); // string(6) "turkey"
// A version 2 encrypted string. Taken directly from the Stack Overflow answer linked above
$s = '02000000266AD4F387FA9474E825B013B0232E73A398A5F72B79BC90D63BD1E45AE3AA5518828D187125BECC285D55FA7CAFED61';
$password = 'Radames';
$bin = hex2bin($s);
$d = decrypt($bin, $password);
var_dump($d); // string(16) "LetTheSunShining"
推荐阅读
- javascript - 输入框在某一点停止收缩
- python - 在已经使用 OpenCV 但版本不同的 C++ 中嵌入带有 OpenCV 的 python 的内存损坏
- python - OpenCV 错误:断言失败(nimages > 0 && nimages == (int)imagePoints1.tot ........ line3106
- javascript - 使用模板字符串定义 HTML 属性
- c++ - 运算符重载错误:无法将“some-type”类型的非常量左值引用绑定到“some-type”类型的右值
- javascript - Meteor 调用结果有未定义的实例变量
- c# - WinUI 3 xaml ABI.Microsoft.UI 与 Microsoft.UI
- webpack - 使用 babel regenerator-runtime 的异步/等待在 ie11 中不起作用
- fluid-framework - Riddler 服务在 Fluid Framework 参考服务中有什么作用?
- reactjs - 使用 refs 修改 React.js 中的元素