AES совместимость между .Net и iPhone?
Мне нужно зашифровать строку на iPhone и отправить ее на веб-службу .Net для дешифрования. Я могу зашифровать/расшифровать на iPhone и с .Net, но зашифрованные строки с iPhone не могут быть расшифрованы .Net. Ошибка, которую я получаю, - "Заполнение недопустима и не может быть удалено".
Код .Net:
http://blog.realcoderscoding.com/index.php/2008/07/dot-net-encryption-simple-aes-wrapper/
Код iPhone использует пример кода: http://nootech.wordpress.com/2009/01/17/symmetric-encryption-with-the-iphone-sdk/
AFAIK мои настройки клавиш одинаковы:
result.BlockSize = 128; // iPhone: kCCBlockSizeAES128
result.KeySize = 128; // kCCBlockSizeAES128
result.Mode = CipherMode.CBC;
result.Padding = PaddingMode.PKCS7; // kCCOptionPKCS7Padding
Я пробовал разные способы генерации зашифрованного текста. hello/hello:
e0PnmbTg/3cT3W + 92CDw1Q == в .Net
yrKe5Z7p7MNqx9 + CbBvNqQ == на iPhone
и "openssl enc -aes-128-cbc -nosalt -a -in hello.txt -pass pass: hello" генерирует: QA + Ul + r6Zmr7yHipMcHSbQ ==
Обновление: Я разместил здесь рабочий код.
Ответы
Ответ 1
По крайней мере, вы используете разные векторы инициализации (IV).
-
Код .Net использует ключ для IV.
private static AesCryptoServiceProvider GetProvider(byte[] key)
{
//Set up the encryption objects
AesCryptoServiceProvider result = new AesCryptoServiceProvider();
byte[] RealKey = Encryptor.GetKey(key, result);
result.Key = RealKey;
result.IV = RealKey;
return result;
}
и
private static byte[] GetKey(byte[] suggestedKey, AesCryptoServiceProvider p)
{
byte[] kRaw = suggestedKey;
List kList = new List();
for (int i = 0; i < p.LegalKeySizes[0].MinSize; i += 8 )
{
kList.Add(kRaw[i % kRaw.Length]);
}
byte[] k = kList.ToArray();
return k;
}
который должен быть: kList.Add(kRaw[(i / 8) % kRaw.Length]);
. В противном случае ключ, длина которого% 8 == 0 будет использовать одну и ту же букву, doh!
Таким образом, IV (и ключ), используемый .Net: hleolhleolhleolh
. Это не часть API, а скорее из-за кода обертки, на который вы указали (что имеет серьезную ошибку в нем...).
-
В iPhone используется код 0 для IV.
// Initialization vector; dummy in this case 0's.
uint8_t iv[kChosenCipherBlockSize];
memset((void *) iv, 0x0, (size_t) sizeof(iv));
-
openssl по умолчанию добавляет случайно сгенерированную соль (поэтому выход длиннее!).
Выход openssl более безопасен, поскольку он добавляет случайный вектор инициализации. Похоже, что первые несколько байтов декодированной строки base64 - "Salted__". Вы также можете попросить openssl не использовать соль (-nosalt) и/или предоставить IV (-iv).
По сути, openssl,.Net и iPhone используют одно и то же шифрование, вам просто нужно быть осторожным, как вы инициализируете API с помощью ключа шифрования и вектора инициализации.
Ответ 2
Вы уверены, что используете те же AES-ключи в своих тестах? В примере OpenSSL в вашем сообщении используется пароль, который OpenSSL выводит ключ и IV из (и, вероятно, также использует соль.
Создайте случайный 128-битный ключ и укажите этот ключ в шестнадцатеричном формате в OpenSSL с помощью:
openssl enc -aes-128-cbc -a -in hello.txt -K KEY_IN_HEX -iv 0
Вы не должны использовать IV = 0 в любой защищенной системе, но для проверки совместимости это нормально.
Ответ 3
В С#
void test(){
string ctB64 = encrypt("hola");
Console.WriteLine(ctB64); // the same as in objective-c
}
string encrypt(string input)
{
try
{
// Create a new instance of the AesManaged class. This generates a new key and initialization vector (IV).
AesManaged myAes = new AesManaged();
// Override the cipher mode, key and IV
myAes.Mode = CipherMode.CBC;
myAes.IV = new byte[16] { 0x10, 0x16, 0x1F, 0xAD, 0x10, 0x10, 0xAA, 0x22, 0x12, 0x51, 0xF1, 0x1E, 0x15, 0x11, 0x1B, 0x10 }; // must be the same as in objective-c
myAes.Key = Encoding.UTF8.GetBytes("0123456789123456");
//CipherKey; // Byte array representing the key
myAes.Padding = PaddingMode.PKCS7;
// Create a encryption object to perform the stream transform.
ICryptoTransform encryptor = myAes.CreateEncryptor();
// perform the encryption as required...
MemoryStream ms = new MemoryStream();
CryptoStream ct = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);
byte[] binput = Encoding.UTF8.GetBytes(input);
ct.Write(binput, 0, binput.Length);
ct.Close();
byte [] result = ms.ToArray();
return Convert.ToBase64String(result);
}
catch (Exception ex)
{
// TODO: Log the error
Console.WriteLine(ex);
throw ex;
}
}
. В objective-c
добавить библиотеку CocoaSecurity из https://github.com/kelp404/CocoaSecurity
#import "CocoaSecurity.h"
#import "Base64.h"
…
- (void) test{
unsigned char bytes[] = { 0x10, 0x16, 0x1F, 0xAD, 0x10, 0x10, 0xAA, 0x22, 0x12, 0x51, 0xF1, 0x1E, 0x15, 0x11, 0x1B, 0x10 }; // must be the same as in c#
NSData *iv = [NSData dataWithBytesNoCopy:bytes length:16 freeWhenDone:YES];
NSData* key = [@"0123456789123456" dataUsingEncoding:NSUTF8StringEncoding];
CocoaSecurityResult *result = [CocoaSecurity aesEncrypt:@"hola" key:key iv:iv];
NSLog(@"%@", result.base64); // the same as in c#
NSData *data = [NSData dataWithBase64EncodedString:result.base64];
CocoaSecurityResult *result2 = [CocoaSecurity aesDecryptWithData:data key:key iv:iv];
NSLog(@"%@", result2.utf8String); // show "hola"
}