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

时间:2021-01-01 18:31:15

I am working in AES256 to be able to encrypt/decrypt between iOS and PHP using insecure channels.

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

I have seen many similar questions that move around the key size, the mode (CBC or ECB), the use of a random iv, etc. But in this case, I found a weird behaviour as follows.

我看到过许多类似的问题,它们围绕着密钥大小、模式(CBC或ECB)、使用随机iv等展开。但在这种情况下,我发现了如下奇怪的行为。

Configuration in both environments: - Key: 32 bytes(256 bits) - Block size: 128 bits (standard) - iv: 16 bytes (static for testing purposes) - Mode: CBC

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

If I encrypt a 16 or 32 bytes text (to match the AES block size), the result in Swift, and PHP are similar but not quite the same:

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

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

键= "123456789012 "明文= "123456789012 "

Swift cipher = e5RnnlJkv4QGnGhkMwfvgMHr80NWUVhbvvfCdPQ5V2KyKJTx4KfWmn4HXi4dG0b8 PHP cipher = e5RnnlJkv4QGnGhkMwfvgMHr80NWUVhbvvfCdPQ5V2I=

PHP cipher =

As you can see, there is a difference in the cipher length and in the last 2 characters of the PHP Base64 String.

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

But if I use a text that is not a AES128 Block Size multiplier, let´s say "Hello World", bot environments report different (but same size) ciphers as follows

但如果我使用一个文本不是一个乘数AES128块大小,让´s说“Hello World”,机器人环境报告不同(但同样大小)密码如下

Swift cipher = bdwO/5C8a+pliIoIXtuzfA==

斯威夫特密码= bdwO / 5 c8a + pliIoIXtuzfA = =

PHP cipher = oPotHCkxpOwQhIaCz6hNMw==

PHP密码= oPotHCkxpOwQhIaCz6hNMw = =

In both cases (Swift and PHP), the cipher is decrypted correctly regardless of the size of the plaintext. Also, the Swift results are consistent with the Objective-C version of the code

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

Attached the simplified code used:

附件是使用的简化代码:

PHP

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)

Why are these different?

为什么这些不同?

1 个解决方案

#1


8  

This is due to padding mode differences.

这是由于填充模式的不同。

PHP uses "zero padding" if the plain text is not N-times the block size. So PHP pads 0..15 bytes with value 00 for 128 bit block ciphers such as AES. For plaintext that ends on a block boundary it will not add any padding bytes.

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

Most other languages use PKCS#7 padding, which pads up to the next block boundary, where the padding byte reflects the number of bytes added. So that would be 1..16 bytes with a value of 1..16 (or 01 to 10 in hexadecimals). For plaintext that ends on a block boundary it will add 16 bytes of padding.

大多数其他语言都使用PKCS#7填充,填充到下一个块边界,填充字节反映添加的字节数。也就是1。16字节,值为1。16(十六进制)。对于结束在块边界上的纯文本,它将添加16字节的填充。

PKCS#7 padding is deterministic and does not depend on the plaintext value (which could consist of bytes with any value, not just text); in other words, it can always be applied and removed independent of the content.

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

Zero padding has the issue that plain text ending with 00 bytes may have those 00 bytes removed during unpadding. This is usually not an issue for ASCII compatible strings as 00 is a control character, usually meaning End Of File (EOF).

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

Please check the comments on mcrypt_encrypt to see how you can apply PKCS#7 padding to PHP.

请检查mcrypt_encrypt的注释,看看如何将PKCS#7填充应用到PHP。

#1


8  

This is due to padding mode differences.

这是由于填充模式的不同。

PHP uses "zero padding" if the plain text is not N-times the block size. So PHP pads 0..15 bytes with value 00 for 128 bit block ciphers such as AES. For plaintext that ends on a block boundary it will not add any padding bytes.

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

Most other languages use PKCS#7 padding, which pads up to the next block boundary, where the padding byte reflects the number of bytes added. So that would be 1..16 bytes with a value of 1..16 (or 01 to 10 in hexadecimals). For plaintext that ends on a block boundary it will add 16 bytes of padding.

大多数其他语言都使用PKCS#7填充,填充到下一个块边界,填充字节反映添加的字节数。也就是1。16字节,值为1。16(十六进制)。对于结束在块边界上的纯文本,它将添加16字节的填充。

PKCS#7 padding is deterministic and does not depend on the plaintext value (which could consist of bytes with any value, not just text); in other words, it can always be applied and removed independent of the content.

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

Zero padding has the issue that plain text ending with 00 bytes may have those 00 bytes removed during unpadding. This is usually not an issue for ASCII compatible strings as 00 is a control character, usually meaning End Of File (EOF).

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

Please check the comments on mcrypt_encrypt to see how you can apply PKCS#7 padding to PHP.

请检查mcrypt_encrypt的注释,看看如何将PKCS#7填充应用到PHP。