PHP mcrypt 到 openssl BF-CBC:如何获得相同的加密值
PHP mcrypt to openssl BF-CBC: how to get the same encrypted value
需要将使用 mcrypt blowfish cbc 模式的现有加密和解密函数替换为等效的 openssl。
它需要能够处理旧值,因此方法需要保持兼容。
解密正常,加密'almost'正常,但还不够。
这是我的代码:
$value = "myTextValue";
$key = 'c40f5b7ad3b7c787d400e923e461064b141fa878ce61cb0d1782593a5a2d842832c80fc2';
$enc = @encrypt_openssl($value, $key);
//$enc = @encrypt_mcrypt($value, $key);
$original_openssl = @decrypt_openssl($enc, $key);
$original_mcrypt = @decrypt_mcrypt($enc, $key);
echo $original_mcrypt."\n";
echo $original_openssl."\n";
function encrypt_openssl($string, $key) {
$iv_size = openssl_cipher_iv_length("BF-CBC");
$iv = openssl_random_pseudo_bytes($iv_size);
$enc = openssl_encrypt($string, "BF-CBC", pack('H*',$key), OPENSSL_RAW_DATA, $iv);
return base64_encode($iv.$enc);
}
function encrypt_mcrypt($string, $key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$enc = mcrypt_encrypt(MCRYPT_BLOWFISH, pack('H*', $key), $string, MCRYPT_MODE_CBC, $iv);
return base64_encode($iv.$enc);
}
function decrypt_openssl($enc, $key) {
$iv_size = openssl_cipher_iv_length("BF-CBC");
$dec = base64_decode($enc);
$iv = substr($dec, 0, $iv_size);
$string = openssl_decrypt(substr($dec, $iv_size), "BF-CBC", pack('H*',$key), OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING | OPENSSL_DONT_ZERO_PAD_KEY, $iv);
return rtrim($string, "\x00");
}
function decrypt_mcrypt($enc, $key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$dec = base64_decode($enc);
$iv = substr($dec, 0, $iv_size);
$string = mcrypt_decrypt(MCRYPT_BLOWFISH, pack('H*', $key), substr($dec, $iv_size), MCRYPT_MODE_CBC, $iv);
return rtrim($string, "\x00");
}
使用 openssl 加密时会添加一些额外的二进制数据。
不是加密大师,只得到一半,这是我在其他 Whosebug 帖子和全能者的帮助下得到的 google
编辑
根据 Topaco 的建议,我得到了以下现在有效的代码:
function encrypt_openssl($string, $key) {
$string_padded = $string;
if (strlen($string_padded) % 8) {
$string_padded = str_pad($string_padded,
strlen($string_padded) + 8 - strlen($string_padded) % 8, "[=12=]");
}
$iv_size = openssl_cipher_iv_length("BF-CBC");
$iv = openssl_random_pseudo_bytes($iv_size);
$enc = openssl_encrypt($string_padded, "BF-CBC", pack('H*',$key), OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $iv);
return base64_encode($iv.$enc);
}
uses PKCS7-padding by default, uses Zero-Byte-padding.
解密后观察到的额外数据是 PKCS7-padding 的 padding-bytes:当前代码使用 PKCS7-padding 作为 openssl_encrypt
-调用(OPENSSL_ZERO_PADDING
-标志未设置).由于 openssl_decrypt
-调用(OPENSSL_ZERO_PADDING
-标志集)没有使用填充,因此解密后填充仍然存在。请注意,OPENSSL_ZERO_PADDING
标志禁用填充,它 而不是 意味着 Zero-Byte-padding。
虽然 PKCS7-padding 通常是比 Zero-Byte-padding 更好的选择(因为后者不可靠),在这种情况下使用旧数据的填充更有意义,即 Zero-Byte-padding,关于旧数据的兼容性。否则会有具有不同填充的数据,这些数据通常不能从数据中导出,例如填充的最终块 41 42 43 44 45 46 02 02
可能是由 PKCS7-padding 或 Zero-Byte-padding 创建的:
before padding after padding
41 42 43 44 45 46 __ __ -> 41 42 43 44 45 46 02 02 PKCS7-Padding
41 42 43 44 45 46 02 02 -> 41 42 43 44 45 46 02 02 Zero-Byte-Padding (variant of mcrypt_encrypt)
这会使 un-padding 更加复杂。使用 Zero-Byte-padding 可以避免这个问题。
由于openssl_encrypt/decrypt
不支持Zero-Byte-padding,所以必须显式实现。使用 mcrypt_encrypt
的 Zero-Byte-padding 变体是有意义的:如果明文已经可以被块大小整除(Blowfish 为 8 字节),则不会添加额外的零字节块。否则,填充为零字节,直到明文的长度对应于块大小的整数倍。
Zero-Byte-padding 必须在 openssl_encrypt
调用之前发生。此外,必须禁用 openssl_encrypt
-调用本身中的填充(设置 OPENSSL_ZERO_PADDING
-标志,类似于 openssl_decrypt
-调用)。
需要将使用 mcrypt blowfish cbc 模式的现有加密和解密函数替换为等效的 openssl。
它需要能够处理旧值,因此方法需要保持兼容。
解密正常,加密'almost'正常,但还不够。
这是我的代码:
$value = "myTextValue";
$key = 'c40f5b7ad3b7c787d400e923e461064b141fa878ce61cb0d1782593a5a2d842832c80fc2';
$enc = @encrypt_openssl($value, $key);
//$enc = @encrypt_mcrypt($value, $key);
$original_openssl = @decrypt_openssl($enc, $key);
$original_mcrypt = @decrypt_mcrypt($enc, $key);
echo $original_mcrypt."\n";
echo $original_openssl."\n";
function encrypt_openssl($string, $key) {
$iv_size = openssl_cipher_iv_length("BF-CBC");
$iv = openssl_random_pseudo_bytes($iv_size);
$enc = openssl_encrypt($string, "BF-CBC", pack('H*',$key), OPENSSL_RAW_DATA, $iv);
return base64_encode($iv.$enc);
}
function encrypt_mcrypt($string, $key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$enc = mcrypt_encrypt(MCRYPT_BLOWFISH, pack('H*', $key), $string, MCRYPT_MODE_CBC, $iv);
return base64_encode($iv.$enc);
}
function decrypt_openssl($enc, $key) {
$iv_size = openssl_cipher_iv_length("BF-CBC");
$dec = base64_decode($enc);
$iv = substr($dec, 0, $iv_size);
$string = openssl_decrypt(substr($dec, $iv_size), "BF-CBC", pack('H*',$key), OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING | OPENSSL_DONT_ZERO_PAD_KEY, $iv);
return rtrim($string, "\x00");
}
function decrypt_mcrypt($enc, $key) {
$iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC);
$dec = base64_decode($enc);
$iv = substr($dec, 0, $iv_size);
$string = mcrypt_decrypt(MCRYPT_BLOWFISH, pack('H*', $key), substr($dec, $iv_size), MCRYPT_MODE_CBC, $iv);
return rtrim($string, "\x00");
}
使用 openssl 加密时会添加一些额外的二进制数据。
不是加密大师,只得到一半,这是我在其他 Whosebug 帖子和全能者的帮助下得到的 google
编辑
根据 Topaco 的建议,我得到了以下现在有效的代码:
function encrypt_openssl($string, $key) {
$string_padded = $string;
if (strlen($string_padded) % 8) {
$string_padded = str_pad($string_padded,
strlen($string_padded) + 8 - strlen($string_padded) % 8, "[=12=]");
}
$iv_size = openssl_cipher_iv_length("BF-CBC");
$iv = openssl_random_pseudo_bytes($iv_size);
$enc = openssl_encrypt($string_padded, "BF-CBC", pack('H*',$key), OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $iv);
return base64_encode($iv.$enc);
}
解密后观察到的额外数据是 PKCS7-padding 的 padding-bytes:当前代码使用 PKCS7-padding 作为 openssl_encrypt
-调用(OPENSSL_ZERO_PADDING
-标志未设置).由于 openssl_decrypt
-调用(OPENSSL_ZERO_PADDING
-标志集)没有使用填充,因此解密后填充仍然存在。请注意,OPENSSL_ZERO_PADDING
标志禁用填充,它 而不是 意味着 Zero-Byte-padding。
虽然 PKCS7-padding 通常是比 Zero-Byte-padding 更好的选择(因为后者不可靠),在这种情况下使用旧数据的填充更有意义,即 Zero-Byte-padding,关于旧数据的兼容性。否则会有具有不同填充的数据,这些数据通常不能从数据中导出,例如填充的最终块 41 42 43 44 45 46 02 02
可能是由 PKCS7-padding 或 Zero-Byte-padding 创建的:
before padding after padding
41 42 43 44 45 46 __ __ -> 41 42 43 44 45 46 02 02 PKCS7-Padding
41 42 43 44 45 46 02 02 -> 41 42 43 44 45 46 02 02 Zero-Byte-Padding (variant of mcrypt_encrypt)
这会使 un-padding 更加复杂。使用 Zero-Byte-padding 可以避免这个问题。
由于openssl_encrypt/decrypt
不支持Zero-Byte-padding,所以必须显式实现。使用 mcrypt_encrypt
的 Zero-Byte-padding 变体是有意义的:如果明文已经可以被块大小整除(Blowfish 为 8 字节),则不会添加额外的零字节块。否则,填充为零字节,直到明文的长度对应于块大小的整数倍。
Zero-Byte-padding 必须在 openssl_encrypt
调用之前发生。此外,必须禁用 openssl_encrypt
-调用本身中的填充(设置 OPENSSL_ZERO_PADDING
-标志,类似于 openssl_decrypt
-调用)。