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

标签 php ios encryption swift aes

我在 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(或十六进制的 0110)。对于以 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/

相关文章:

php -f 从页面执行脚本但访问网页却不执行?

php - 根据角色重定向到页面

php - Ajax 发布在 asp 中失败

ios - 如何在项目目录中运行 pod install

java - 在 Android 和 Java 中加密/解密字符串的不同值

php - 重置/清除对象?

ios - SwiftUI 上下文菜单 : Set shape of shadow or hide it

javascript - 如何在移动应用程序中隐藏 flutter 的 webview

php - 从 php 到 c++ 的加密和解密

java - 编写密码程序(纯文本->凯撒密码->ASCII)?