Swift(iOS)和PHP中AES256加密的不同结果


Different results in AES256 encryption in Swift (iOS) and PHP

我在AES256中工作,以便能够使用不安全的通道在iOS和PHP之间加密/解密。

我见过很多类似的问题,比如密钥大小、模式(CBC或ECB)、随机iv的使用等。但在这种情况下,我发现了一种奇怪的行为,如下所示。

两种环境中的配置:-密钥:32字节(256位)-块大小:128位(标准)-iv:16字节(静态,用于测试目的)-模式:CBC

如果我加密一个16或32字节的文本(以匹配AES块大小),Swift和PHP中的结果相似但不完全相同:

key="12345678901234567890123456789012"plainText="12345678901234567890123456789012"iv="1234567890123456"

Swift密码=e5RnnlJkv4QGNHkMwfvgMHr80NWUVhbvvvfCdPQ5V2KyKJTx4KfWmn4HXi4dG0b8PHP密码=e5RnnlJkv4QGNHkMwfvgMHr80NWUVhbvvvfCdPQ5V2I=

正如您所看到的,PHP Base64字符串的密码长度和最后2个字符存在差异。

但是,如果我使用的文本不是AES128块大小乘数,让我们说"Hello World",机器人程序环境报告不同(但大小相同)的密码,如下

Swift密码=bdwO/5C8a+pliIoIXtuzfA==

PHP密码=oPotHCkxpOwQhIaCz6hNMw==

在这两种情况下(Swift和PHP),无论明文大小,密码都会被正确解密。此外,Swift的结果与代码的Objective-C版本一致

附上使用的简化代码:

PHP

    $key = "12345678901234567890123456789012"; 
    $iv = "1234567890123456";
    $plaintext = "Hello World";
    $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_CBC, $iv);
    $ciphertext_base64 = base64_encode($ciphertext);
    echo  "ciphertext: ".$ciphertext_base64."</br>";

Swift

let keyData: NSData! = (key as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
let keyBytes         = UnsafePointer<UInt8>(keyData.bytes)
let keyLength        = size_t(kCCKeySizeAES256)
let plainData = (plainText as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
let dataLength    = UInt(plainData.length)
let dataBytes     = UnsafePointer<UInt8>(plainData.bytes)
var bufferData    = NSMutableData(length: Int(dataLength) + kCCBlockSizeAES128)
var bufferPointer = UnsafeMutablePointer<UInt8>(bufferData.mutableBytes)
let bufferLength  = size_t(bufferData.length)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm:  CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options = UInt32(kCCOptionPKCS7Padding)
let ivData: NSData! = (iv as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
let ivPointer = UnsafePointer<UInt8>(ivData.bytes)
var numBytesEncrypted: UInt = 0
var cryptStatus = CCCrypt(operation, algoritm, options, keyBytes, keyLength, ivPointer, dataBytes, dataLength, bufferPointer, bufferLength, &numBytesEncrypted)
bufferData.length = Int(numBytesEncrypted)
let base64cryptString = bufferData.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
println(base64cryptString)

为什么这些不同?

这是由于填充模式的差异。

PHP使用"零填充"如果纯文本不是块大小的N倍。因此,PHP为128位块密码(如AES)填充了值为00的0..15字节。对于在块边界上结束的明文,它不会添加任何填充字节。

大多数其他语言使用PKCS#7填充,它填充到下一个块边界,其中填充字节反映添加的字节数。因此,这将是值为1..16的1..16字节(或十六进制的0110)。对于在块边界上结束的明文,它将添加16字节的填充。

PKCS#7填充是确定性的,不依赖于明文值(可以由具有任何值的字节组成,而不仅仅是文本);换句话说,它总是可以独立于内容应用和删除。

零填充的问题是,以00字节结尾的纯文本可能会在取消添加期间删除这些00字节。这通常不是ASCII兼容字符串的问题,因为00是一个控制字符,通常意味着文件结束(EOF)。

请查看mcrypt_encrypt上的评论,了解如何将PKCS#7填充应用于PHP。