我在 AES256 中工作,以便能够使用不安全的 channel 在 iOS 和 PHP 之间加密/解密。
我见过许多围绕 key 大小、模式(CBC 或 ECB)、随机 iv 的使用等移动的类似问题。但在这种情况下,我发现了如下奇怪的行为。
两种环境下的配置: - key :32 字节(256 位) - block 大小:128 位(标准) - iv:16 字节(用于测试目的的静态) - 模式:CBC
如果我加密 16 或 32 字节的文本(以匹配 AES block 大小),Swift 和 PHP 中的结果相似但不完全相同:
key = "12345678901234567890123456789012" plainText = "12345678901234567890123456789012" iv = "1234567890123456"
Swift cipher = e5RnnlJkv4QGnGhkMwfvgMHr80NWUVhbvvfCdPQ5V2KyKJTx4KfWmn4HXi4dG0b8 PHP cipher = e5RnnlJkv4QGnGhkMwfvgMHr80NWUVhbvvfCdPQ5V2I=
如您所见,密码长度和 PHP Base64 字符串的最后 2 个字符存在差异。
但是如果我使用不是 AES128 block 大小乘数的文本,比如说“Hello World”,机器人环境会报告不同(但大小相同)的密码,如下所示
Swift cipher = bdwO/5C8a+pliIoIXtuzfA==
PHP cipher = 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 使用“零填充”如果纯文本不是 block 大小的 N 倍。因此 PHP 为 128 位 block 密码(例如 AES)填充值 00
的 0..15 字节。对于以 block 边界结束的明文,它不会添加任何填充字节。
大多数其他语言使用 PKCS#7 填充,填充到下一个 block 边界,其中填充字节反射(reflect)添加的字节数。所以这将是 1..16 个字节,值为 1..16(或十六进制的 01
到 10
)。对于以 block 边界结束的明文,它将添加 16 个字节的填充。
PKCS#7 填充是确定性的,不依赖于明文值(明文值可以由任何值的字节组成,而不仅仅是文本);换句话说,它始终可以独立于内容应用和删除。
零填充有一个问题,即以 00
字节结尾的纯文本可能会在取消填充期间删除那些 00
字节。对于 ASCII 兼容字符串,这通常不是问题,因为 00
是一个控制字符,通常表示文件结束 (EOF)。
请查看关于 mcrypt_encrypt
的评论,了解如何将 PKCS#7 填充应用于 PHP。
关于php - Swift (iOS) 和 PHP 中 AES256 加密的不同结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25980212/