在Coldfusion中加密后在PHP中解密

7
我是一名有用的助手,可以为您翻译文本。

我遇到了一个问题,无法在Coldfusion中复现PHP生成的相同结果。

在PHP中,加密方式如下:

<?php
    $key = "$224455@";
    $Valor = "TESTE";

    $base = chop(base64_encode(mcrypt_encrypt(MCRYPT_DES, $key, $Valor, MCRYPT_MODE_ECB)));     
?>

我已经得到了结果:

TzwRx5Bxoa0=

在Coldfusion中这样做:
<cfset Valor = "TESTE">
<cfset Key = "$224455@">
<cfset base = Encrypt(Valor,ToBase64(Key),"DES/ECB/PKCS5Padding","BASE64")>

结果:

qOQnhdxiIKs=

为什么ColdFusion的值与PHP不同?

非常感谢。

2个回答

5
问题在于填充(padding)。PHP的mcrypt扩展只使用ZeroPadding。这意味着明文会被填充0x00字节,直到达到块尺寸的整数倍。
而PKCS#5/PKCS#7填充则是用表示下一个块尺寸所需字节数的字节来填充。DES的块尺寸为8字节。
所以你需要在php中对明文进行填充(可以参考这个代码段: A: How to add/remove PKCS7 padding from an AES encrypted string?) 或者在ColdFusion中使用不同的密码算法,例如"DES/ECB/NoPadding"。我建议前一种方法,因为如果使用NoPadding,则明文必须已经是块尺寸的整数倍。
$key = "$224455@";
$Valor = "TESTE";
function pkcs7pad($plaintext, $blocksize)
{
    $padsize = $blocksize - (strlen($plaintext) % $blocksize);
    return $plaintext . str_repeat(chr($padsize), $padsize);
}

$base = chop(base64_encode(mcrypt_encrypt(MCRYPT_DES, $key, pkcs7pad($Valor, 8), MCRYPT_MODE_ECB)));

结果:

qOQnhdxiIKs=

如果您在PHP中解密,请不要忘记取消填充恢复的明文。


@Leigh PHP使用二进制字符串。因此,输入的编码取决于PHP文件的编码,这可能是UTF-8。输出明确地进行Base64编码以匹配ColdFusion编码。我完全不了解ColdFusion,无法说出为什么需要ToBase64(Key)以及为什么它会产生与我答案中的PHP代码相同的输出。 - Artjom B.
@Leigh 那很好知道。在为这个答案进行研究期间,我尝试通过我最喜欢的搜索引擎找到ColdFusion的加密的适当文档,但却失败了。 - Artjom B.
@ArtjomB. - 是的,加密的常规文档在我看来没有足够详细的内部工作原理。上面的另一个链接在我看来更有帮助,但我也花了一些时间才找到它。 - Leigh
@HilsoVasquesJunior,我无法帮助你解决这个问题,因为我对ColdFusion一无所知。 - Artjom B.
@Leigh 或许你可以提供一个答案,其中 Valor 被填充为 0x00 (\0) 直到下一个8的倍数,就像 PHP 所做的那样。如果明文已经是8的倍数,则不应更改它。 - Artjom B.
显示剩余8条评论

5

Artjom B.已经在上面提供了答案Artjom B.写道

问题在于填充。PHP的mcrypt扩展只使用ZeroPadding[...]你需要在php中填充明文[...]或者在ColdFusion中使用不同的密码,如“DES/ECB/NoPadding”。我建议前者,因为如果使用NoPadding,则明文必须已经是块大小的倍数。

不幸的是,在CF中很难产生null character。据我所知,唯一有效的技术是使用URLDecode(“%00”)。如果您无法像@Artjom B.建议的那样修改PHP代码,则可以尝试使用下面的函数在CF中填充文本。免责声明:仅进行了轻微测试(CF10),但似乎产生了与上述相同的结果。

更新:由于CF encrypt()函数始终将明文输入解释为UTF-8字符串,因此您也可以使用charsetEncode(bytes, "utf-8")从单元素字节数组创建空字符,即charsetEncode(javacast("byte[]", [0]),"utf-8")


例子:

Valor = nullPad("TESTE", 8);
Key = "$224455@";
result = Encrypt(Valor, ToBase64(Key), "DES/ECB/NoPadding", "BASE64");
// Result: TzwRx5Bxoa0=
WriteDump( "Encrypted Text = "& Result ); 

功能:

/*
   Pads a string, with null bytes, to a multiple of the given block size

   @param plainText - string to pad
   @param blockSize - pad string so it is a multiple of this size
   @param encoding - charset encoding of text
*/
string function nullPad( string plainText, numeric blockSize, string encoding="UTF-8")
{
    local.newText = arguments.plainText;
    local.bytes = charsetDecode(arguments.plainText, arguments.encoding);
    local.remain = arrayLen( local.bytes ) % arguments.blockSize;

    if (local.remain neq 0) 
    {
        local.padSize = arguments.blockSize - local.remain;
        local.newText &= repeatString( urlDecode("%00"), local.padSize );
    }

    return local.newText;
}

个人的,目前在初步测试中表现完美,我稍后会进行几次测试,但我相信解决方案是一样的。感谢您的帮助和迅速响应。对我帮助很大。谢谢。 - Hilso Vasques Junior
ArtjomB. 发现了真正的问题,但很高兴上面所提供的帮助 :) - Leigh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接