Я работаю в AES256, чтобы иметь возможность шифровать / дешифровать между iOS и PHP с использованием небезопасных каналов.
Я видел много похожих вопросов, которые касаются размера ключа, режима (CBC или ECB), использования случайного iv и т. Д. Но в этом случае я обнаружил странное поведение следующим образом.
Конфигурация в обеих средах: – Ключ: 32 байта (256 бит) – Размер блока: 128 бит (стандартный) – iv: 16 байтов (статический для целей тестирования) – Режим: CBC
Если я зашифрую текст 16 или 32 байта (чтобы соответствовать размеру блока AES), результат в Swift и PHP похожи, но не совсем одинаковы:
ключ = "12345678901234567890123456789012" plainText = "12345678901234567890123456789012" iv = "1234567890123456"
Swift cipher = e5RnnlJkv4QGnGhkMwfvgMHr80NWUVhbvvfCdPQ5V2KyKJTx4KfWmn4HXi4dG0b8 PHP cipher = e5RnnlJkv4QGnGhkMwfvgMHr80NWUVhbvvfCdPQ5V2I =
Как вы можете видеть, разница в длине шифра и в последних двух символах строки PHP Base64.
Но если я использую текст, который не является множителем блока размера AES128, давайте скажем «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>";
стриж
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 накладывает 0..15 байт со значением 00
для 128-битных блочных шифров, таких как AES. Для открытого текста, который заканчивается на границе блока, он не будет добавлять байты заполнения.
В большинстве других языков используется дополнение PKCS # 7, которое заполняется до следующей границы блока, где байт заполнения отражает количество добавленных байтов. Таким образом, это будет 1,16 байт со значением 1,16 (или от 01
до 10
в гексадецималах). Для открытого текста, который заканчивается на границе блока, он добавит 16 байтов заполнения.
Заполнение PKCS # 7 является детерминированным и не зависит от значения открытого текста (которое может состоять из байтов с любым значением, а не только с текстом); другими словами, его всегда можно применять и удалять независимо от содержимого.
У нулевого заполнения возникает проблема, заключающаяся в том, что обычный текст, заканчивающийся на 00
байтов, может удалять эти 00
байтов во время переадресации. Обычно это не проблема для ASCII-совместимых строк, поскольку 00
является управляющим символом, обычно означающим End Of File (EOF).
Пожалуйста, ознакомьтесь с комментариями к mcrypt_encrypt
чтобы узнать, как вы можете применять дополнение PKCS # 7 к PHP.