2012-04-24 72 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; 
}