2015-01-29 2 views
1

У меня есть приложение iOS, которое шифрует данные с использованием AES 128 CBC. Я могу расшифровать эти данные в приложении в объекте-c, так что, по крайней мере, мне говорят, что шифрование работает правильно в этом контексте (Common Crypto). Проблема в том, что мне нужно отправить эти зашифрованные данные на сервер и расшифровать его через PHP. Вот где я терплю неудачу.Ошибка дешифрования AES 128

Я вручную сделал шифрование в командной строке с помощью openssl. Используя этот вывод и вернув его в эту функцию php, я могу правильно расшифровать, поэтому я знаю, что функция php работает правильно, по крайней мере, относительно openssl. Итак, проблема заключается в том, как заставить Objective-C выводить зашифрованный текст так же, как и openssl (или как заставить php расшифровывать зашифрованный текст из Common Crypto). Другими словами, каждая из этих функций «работает» в своем собственном контексте.

<?php 

function jsonEncode ($result, $message) { 
    $arr = array('result' => $result, 'message' => $message); 
    echo json_encode($arr); 
} 


//$ciphertext = base64_decode($_POST['ciphertext']); 
//$iv   = $_POST['iv']; 

// overriding the http post values with values copied in from console output for testing 
$ciphertext = base64_decode('dwb7MWCQUUiVuJLzL2EzYm0NcodwORP47qPhLzaolAk='); 
// openssl on the command line yields ciphertext of 
// 'gRiOXseXTjhpPCiiHgnaQQoal3a9E87Gx3FVpZPtR1I=' which decrypts successfully 
$iv = 'hkPDfznq1t1UpKrW'; 
$key = 'T8ZvJba0HHsmiVSD'; 

//$plaintext = openssl_decrypt($ciphertext, 'aes-128-cbc', $key, false, $iv); 
//jsonEncode('success', $plaintext); 

// this is the line that fails 
$plaintext = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $ciphertext, MCRYPT_MODE_CBC, $iv); 
//$padding = ord($plaintext[strlen($plaintext) - 1]); 
//jsonEncode('success', substr($plaintext, 0, -$padding)); 
jsonEncode('success', $plaintext); 
?> 

EDIT: на основе очень полезный комментарий от @Zaph ниже, я переписал свою функцию шифрования в Objective-C, чтобы сделать отступы вручную. Я думаю, что это правильно. Однако, когда функция php возвращается, функция mcrypt_decrypt вычисляет значение false. Вот новая функция Objective C:

- (NSData *)AES128EncryptWithKey:(NSString *)key iv:(NSString *)iv 
{ 
    char keyPtr[kCCKeySizeAES128+1]; 
    bzero(keyPtr, sizeof(keyPtr)); 

    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; 

    char ivPtr[kCCBlockSizeAES128 + 1]; 
    bzero(ivPtr, sizeof(ivPtr)); 
    if (iv) { 
     [iv getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding]; 
    } 

    NSUInteger dataLength = [self length]; 
    int diff = kCCKeySizeAES128 - (dataLength % kCCKeySizeAES128); 
    int newSize = 0; 

    if(diff > 0) { 
     newSize = (int)(dataLength + diff); 
    } 

    // manually add padding to the end of the data array 
    char dataPtr[newSize]; 
    memcpy(dataPtr, [self bytes], [self length]); 
    for(int i = 0; i < diff; i++) { 
     dataPtr[i + dataLength] = diff; 
    } 
    dataPtr[newSize] = '\0'; 

    size_t bufferSize = newSize + kCCBlockSizeAES128; 
    void *buffer = malloc(bufferSize); 

    // print out the padded array for verification 
    NSLog(@"diff: %d new size: %d", diff, newSize); 
    for (int i=0; i<newSize; i++) 
     printf("0x%x ", dataPtr[i]); 

    printf("\n"); 


    size_t numBytesEncrypted = 0; 
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, 
              kCCAlgorithmAES128, 
              0x0000, //No padding 
              keyPtr, 
              kCCKeySizeAES128, 
              ivPtr, 
              dataPtr, 
              sizeof(dataPtr), 
              buffer, 
              bufferSize, 
              &numBytesEncrypted); 

    if(cryptStatus == kCCSuccess) { 
     return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted]; 
    } 

    return nil; 
} 

Objective C консоли Выход:

diff: 12 new size: 32 
0x65 0x6e 0x63 0x72 0x79 0x70 0x74 0x69 0x6f 0x6e 0x20 0x69 0x73 0x20 0x74 0x72 0x69 0x63 0x6b 0x79 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 
encrypted text: dwb7MWCQUUiVuJLzL2EzYm0NcodwORP47qPhLzaolAk= 

Простой текст "шифрование сложно.

+0

любая причина, по которой вы делаете свое собственное шифрование? Вы могли бы просто использовать ssl, который дал бы вам шифрование для «бесплатного». –

+1

Я не использую свое собственное шифрование. Я использую CommonCrypto из библиотеки Apple Developer. – Alex

+0

@MarcB Common Crypto - встроенные криптографические библиотеки Apple для iOS. OpenSSL не поставляется для iOS, поэтому он не свободен в том смысле, что нужно получить источник и скомпилировать его, что нелегко. – zaph

ответ

1

php не поддерживает kCCOptionPKCS7Padding, у него есть своя идея о том, как выполнять прокладку. Таким образом, вы должны будете предоставить заполнение до CCCrypt. Или выполните дополнение PKCS # 7 в вашем коде для php. (BTW: PKCS # 5 и PKCS # 7 заполняются по существу одинаковыми.)

Вы должны увидеть, что блоки proior для первого дешифруют в php ОК, если нет, есть еще одна проблема.

Вот мой нулевой (PHP) Метод заполнения:

+ (NSData *)phpPadData:(NSData *)data { 
    NSMutableData *newData = [data mutableCopy]; 
    NSUInteger paddLength = kCCBlockSizeAES128 - (data.length % kCCBlockSizeAES128); 
    [newData increaseLengthBy:paddLength]; 

    return [newData copy]; 
} 

Когда я использую это для кодирования «шифрования сложно» я получаю:

 
ciphertextData: 81188e5e c7974e38 693c28a2 1e09da41 a5d64983 e124b73c 36da520a 8198d3e7 
ciphertextBase64: gRiOXseXTjhpPCiiHgnaQaXWSYPhJLc8NtpSCoGY0+c= 

Который точно так же я, если Я использую онлайн-сайт: http://aes.online-domain-tools.com (который, вероятно, использует php mcrypt). Это отличается от theOpenSSL в вопросе.

Вот мой тестовый код, я отделяю строку от кодировки данных от кодировки. Я помещаю методы класса class в test names классов, любой класс будет делать.

+ (NSString *)encryptCleartextString:(NSString *)cleartextString keyString:(NSString *)keyString ivString:(NSString *)ivString { 
    NSData *cleartext = [cleartextString dataUsingEncoding:NSUTF8StringEncoding]; 
    NSData *key  = [keyString  dataUsingEncoding:NSUTF8StringEncoding]; 
    NSData *iv  = [ivString  dataUsingEncoding:NSUTF8StringEncoding]; 

    NSData *ciphertext = [Test phpEncryptCleartext:cleartext key:key iv:iv]; 
    NSLog(@"ciphertext: %@", ciphertext); 
    NSString *ciphertextString = [ciphertext base64EncodedStringWithOptions:0]; 
    NSLog(@"ciphertextString: %@", ciphertextString); 

    return ciphertextString; 
} 

+ (NSData *)phpEncryptCleartext:(NSData *)cleartext key:(NSData *)key iv:(NSData *)iv { 
    NSLog(@"phpEncryptCleartext"); 
    NSLog(@"cleartext: %@", cleartext); 
    NSLog(@"key:  %@", key); 
    NSLog(@"iv:  %@", iv); 

    NSData *cleartextPadded = [self phpPadData:cleartext]; 
    NSLog(@"cleartextPadded: %@", cleartextPadded); 

    CCCryptorStatus ccStatus = kCCSuccess; 
    size_t   cryptBytes = 0; // Number of bytes moved to buffer. 
    NSMutableData *ciphertext = [NSMutableData dataWithLength:cleartextPadded.length]; 

    ccStatus = CCCrypt(kCCEncrypt, 
         kCCAlgorithmAES128, 
         0, 
         key.bytes, 
         kCCKeySizeAES128, 
         iv.bytes, 
         cleartextPadded.bytes, 
         cleartextPadded.length, 
         ciphertext.mutableBytes, 
         ciphertext.length, 
         &cryptBytes); 

    if (ccStatus == kCCSuccess) { 
     ciphertext.length = cryptBytes; 
    } 
    else { 
     NSLog(@"kEncryptionError code: %d", ccStatus); // Add error handling 
     ciphertext = nil; 
    } 
    NSLog(@"ciphertext:    %@", ciphertext); 

    return ciphertext; 
} 

// Test code 
NSString *cleartextString = @"encryption is tricky"; 
NSString *ivString = @"hkPDfznq1t1UpKrW"; 
NSString *keyString = @"T8ZvJba0HHsmiVSD"; 

NSString *encryptedTextBase64 = [Test encryptCleartextString:cleartextString keyString:keyString ivString:ivString]; 
NSLog(@"encryptedTextBase64:  %@", encryptedTextBase64); 
+0

Хорошо. Благодарю. Похоже, это может быть сложно. – Alex

+0

Не слишком сложно, он применим к последнему блоку. mcrypt с нулями, PKCS # 7 с прописной длиной и добавляет дополнительный блок, если входной сигнал является точно размером блока. См. [Извлеченные уроки, реализующие AES в PHP с использованием Mcrypt] (http://www.leaseweblabs.com/2014/02/aes-php-mcrypt-key-padding/). Также см .: [PKCS # 7 padding] (http://en.wikipedia.org/wiki/Padding_ (криптография)) – zaph

+0

Большое спасибо за совет.Если я понимаю, что вы говорите, я должен вручную добавить свое собственное дополнение в Objective-C * до того, как * я отправлю строку в 'CCCrypt'. Например, если у меня есть 12-байтная строка открытого текста, я должен добавить '04 04 04 04' в конец строки, а затем отправить это значение в 'CCCrypt'. Затем 'CCCrypt' будет добавлять свои 16 байтов заполнения до того, как он зашифрует. Затем, после того, как я расшифрую строку в PHP, я могу удалить эти последние 20 байтов и сделать вывод, что оставшаяся строка является открытым текстом. Это верно? – Alex

Смежные вопросы