2012-04-24 114 views
2

PHP和ColdFusion9中的AES加密产生不同的结果。有人可以帮我吗?PHP ColdFusion9 AES加密 - 不同的结果

的下面PHP代码

$key = "12345678123456781234567812345678"; 
$iv = "1234567812345678"; 
$data = "This is a plain string."; 

echo base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv)); 

给我G + tdEOfQTtVCQGxW3N5uzkqN207OyfIPxS6zf2xrKKY =

虽然下面ColdFusion代码

<cfset thePlainData = "This is a plain string." /> 
<cfset theKey = "12345678123456781234567812345678" /> 
<cfset theAlgorithm = "AES/CBC/PKCS5Padding" /> 
<cfset theEncoding = "base64" /> 
<cfset theIV = "1234567812345678" /> 

<cfset encryptedString = encrypt(thePlainData, theKey, theAlgorithm, theEncoding, theIV) /> 

给我KLt55n5/T3ee6xVq9VGFbyCacJznkHEqC/RDRhL + 4NW =

任何想法,我错了吗?提前致谢。

+0

上面的PHP为我运行,但上面的CF会引发一个错误:“指定的密钥不是此加密的有效密钥:非法密钥大小”......确实是*确切的CF代码?这是CF9企业吗? – 2012-04-24 16:02:25

+0

非常感谢您的快速响应。我正在使用ColdFusion9试用版。 – user812120 2012-04-24 16:03:22

+0

CF期望'theKey'在base64中。将'theKey'转换为base64可以让你更接近,但并不是那样。所以它可能是一种编码差异。你能用hex或者base64打印'theKey/iv'吗? – Leigh 2012-04-24 19:51:44

回答

6

不幸的是,ColdFusion和PHP实现之间在使用明文填充风格方面存在轻微的不兼容性。 AES需要一个可被128整除的明文块大小。要达到此目的,可使用PHP will pad the plaintext input with NULL characters来获得适当的块大小。 ColdFusion可以使用各种padding techniques that are supported by Java。不幸的是,ColdFusion和Java都支持NULL填充模式,这使得互操作性变得更加困难。 ColdFusion的字符串处理不支持NULL字符,所以您需要implement a PKCS5Padding schema within PHP来代替它们以正确地进行交互操作。

此外,如在评论中提到的,ColdFusion的将期望键进行base64编码,所以你需要的关键的设置看起来像:

<cfset theKey = toBase64("12345678123456781234567812345678") /> 

此外,Java的默认(和ColdFusion由扩展名)只支持高达128位的密钥。在这里,您使用的是一个256位密钥,它将require the Java Unlimited Strength extension(对于那些试图测试代码并获得非法密钥大小错误的人)。

产生的PHP代码如下所示:

// Function from http://us3.php.net/manual/en/ref.mcrypt.php#69782 
function pkcs5_pad ($text, $blocksize) 
{ 
    $pad = $blocksize - (strlen($text) % $blocksize); 
    return $text . str_repeat(chr($pad), $pad); 
} 

$key = "12345678123456781234567812345678"; 
$iv = "1234567812345678"; 
// Pad data with PKCS #5 to prevent PHP from using NULL padding. 
$data = pkcs5_pad("This is a plain string.", 16); 

echo base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv)); 

生成的ColdFusion代码的样子:

<cfset thePlainData = "This is a plain string." /> 
<cfset theKey = toBase64("12345678123456781234567812345678") /> 
<cfset theAlgorithm = "AES/CBC/PKCS5Padding" /> 
<cfset theEncoding = "base64" /> 
<cfset theIV = "1234567812345678" /> 

<cfset encryptedString = encrypt(thePlainData, theKey, theAlgorithm, theEncoding, theIV) /> 

<cfoutput>#encryptedString#</cfoutput> 

两个输出相同的base64编码字符串:

G+tdEOfQTtVCQGxW3N5uzlu0mGabRKNxuIdAXArQE80= 
+0

非常感谢贾斯廷的详细解答。它解决了这个问题。 – user812120 2012-04-25 08:31:52

+0

是否可以使用MCRYPT_RIJNDAEL_256而不是MCRYPT_RIJNDAEL_128来加密PHP中的数据,然后使用AES Decryption在ColdFusion或Ruby中对其进行解密。 – user812120 2012-04-25 08:49:03

+0

不是开箱即用,没有。 AES基于Rijndael,但对块大小和密钥长度限制更多。 AES仅支持128位的明文块大小,所以如果您一起使用Rijndael和AES,则必须在Rijndael一侧使用128位块大小,以便它们兼容。 (回想一下,“MCRYPT_RIJNDAEL_128”中的“128”指定了块的大小,而不是密钥长度。) – 2012-04-25 14:10:42

0

我知道这是一个古老的线索,但最近出现了类似的问题。

虽然本地不支持,但事实证明有一种方法可以在CF中生成空填充。 This answer by Artjom B.同意在PHP中调整填充可能会更简单,但指出使用“NoPadding”方案可以实现将纯文本以0x00填充到algorithm's block size的倍数的相同结果。

在CF中生成空字符有点棘手,但可以执行using URLDecode("%00")。由于encrypt()始终将输入视为UTF-8编码,因此您还可以使用charsetEncode()从单个元素字节数组创建空字符,即charsetEncode(javacast("byte[]", [0]), "utf-8")

不严格测试,但这样的事情应该产生CF10相同的结果:

代码:

thePlainData = nullPad("This is a plain string.", 16); 
// NB: JCE unlimited policy files required for 256 bit keys 
theKey = toBase64("12345678123456781234567812345678"); 
theIV = "1234567812345678"; 

encryptedString = encrypt(thePlainData, theKey, "AES/CBC/NoPadding", "base64", theIV); 

结果:

G+tdEOfQTtVCQGxW3N5uzkqN207OyfIPxS6zf2xrKKY= 

功能:

/* 
    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; 
}