Невозможно расшифровать данные, закодированные через OpenSSL на iPhone - PullRequest
4 голосов
/ 14 сентября 2011

Как продолжение моего предыдущего вопроса: Как получить вектор инициализации (iv) из зашифрованных данных OpenSSL

Я использую OpenSSL утилиту командной строки для шифрования строки, а затем пытаюсь использовать <CommonCrypto/CommonCryptor.h> для расшифровки строки на iPhone. Используя Dropbox SDK, на iPhone загружается XML-файл с зашифрованными строками, где мое приложение пытается проанализировать и расшифровать строки в этом файле.

Вот пример команды openssl:

printf %s "Hello" | openssl enc -aes-128-cbc -K 00ff349830193845af43984758690213 -iv 0 -base64

Приведенная выше строка base 64 помещается в файл XML, который затем анализируется приложением.

Я использую дополнение NSData Мэтта Галлахера для декодирования текста base64. Я предполагаю, что это работает правильно; Я действительно не нашел хороший способ проверить это. (Источник: http://cocoawithlove.com/2009/06/base64-encoding-options-on-mac-and.html).

Вот метод декодирования зашифрованной строки.
Ключом является строка NSString в этом случае, равная @"00ff349830193845af43984758690213".

+ (NSString *)string:(NSString *)encryptedString withAES128Key:(NSString *)key {

// decode base64, from Matt Gallagher's NSData category
NSData *b64DecodedData = [NSData dataFromBase64String:encryptedString];

NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];

// fyi, I plan to replace this later with a random iv
NSData *ivData = [@"00000000000000000000000000000000" dataUsingEncoding:NSUTF8StringEncoding];

// decrypt the string
NSData *decodedData = [self doCipher:b64DecodedData iv:ivData key:keyData context:kCCDecrypt];

NSString *unencryptedString = [[NSString alloc] initWithBytes:[decodedData bytes] length:[decodedData length] encoding:NSUTF8StringEncoding];

return [unencryptedString autorelease];
}

Вот метод, который выполняет фактическое дешифрование: (Кредит для этого метода предоставляется товарищу по стеку .)

+ (NSData *)doCipher:(NSData *)dataIn
              iv:(NSData *)iv
             key:(NSData *)symmetricKey
         context:(CCOperation)encryptOrDecrypt
{
CCCryptorStatus ccStatus   = kCCSuccess;
size_t          cryptBytes = 0;    // Number of bytes moved to buffer.
NSMutableData  *dataOut    = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeAES128];

ccStatus = CCCrypt( encryptOrDecrypt,
                   kCCAlgorithmAES128,
                   kCCOptionPKCS7Padding,
                   [symmetricKey bytes], 
                   kCCKeySizeAES128,
                   iv,
                   dataIn.bytes,
                   dataIn.length,
                   dataOut.mutableBytes,
                   dataOut.length,
                   &cryptBytes);

// error occurs here, error -4304 kCCDecodeError
if (ccStatus != kCCSuccess) {
    // Handle error
    NSLog(@"CCCrypt status: %d", ccStatus);
}

dataOut.length = cryptBytes;

return dataOut;
}

Произошла ошибка, код ошибки -4304, который равен kCCDecodeError, поскольку ccStatus не равен kCCSuccess.

Я чувствую, что ключ и iv неправильно установлены как объекты NSData. OpenSSL требует, чтобы ключ и iv были шестнадцатеричными значениями, что я и сделал, и тщательно установил их точно в 128 бит. Тем не менее, я думаю, что я что-то упускаю при преобразовании этих строк в NSData для метода doCipher.

Любая помощь очень ценится! Я играл с этим весь день.

Ответы [ 3 ]

8 голосов
/ 14 сентября 2011

Хотя iv неправильно обрабатывает, это наименьшая из проблем.

Ошибка декодирования звучит как неправильная длина аргумента, поскольку любые случайные значения iv, ключ и данные должны быть допустимыми. (Моя жена соглашается, и она делает это профессионально.) Проверяйте такие вещи, как ключ и длину данных после преобразования их в NSData. Обратите внимание, что передача зашифрованных данных с неправильным или несовместимым заполнением также приведет к ошибке декодирования.

Напишите тест для Base64, ваш код iOS против openssl.

Работайте над решением из более простых тестов.

Например, отбрасывайте base64, пока не добьетесь успеха в шифровании. Попробуйте простые данные, скажем, один блок длиной 0, заполнение может быть проблемой. Попробуйте более простой ключ, такой как все 0. Вы можете использовать OPENSSL в командной строке Mac Terminal.

Как только базовое шифрование заработает, добавьте необходимые функции.

Для openssl из командной строки используйте входные и выходные файлы, они будут обрабатывать двоичные файлы, поэтому у вас не будет этого препятствия по крайней мере на начальном этапе. Вот образец:

(file_orig.txt contains: "1234567890123456")

openssl enc -e -aes-128-cbc -K 00ff349830193845af43984758690213 -p -iv 0 -nosalt -in file_orig.txt -out file_aes.txt

, который печатает ключ, который он сгенерировал, а также используемый им iv:

key=00ff349830193845af43984758690213
iv =00000000000000000000000000000000

Затем вы можете прочитать те же файлы данных в вашем методе iOS.

Вот метод iOS, который использует файлы, которые создает openssl:
(поместить вывод ключа openssl в файл key-hex-openssl.txt)

NSData *keyHexData = [@"00ff349830193845af43984758690213" dataUsingEncoding:NSUTF8StringEncoding];
NSData *testData   = [NSData dataWithContentsOfFile:@"yourDirectoryPath/file_aes.txt"];
NSData *clearData  = [NSData dataWithContentsOfFile:@"yourDirectoryPath/file_orig.txt"];

NSLog(@"keyHexData: %@", keyHexData);
NSLog(@"testData:   %@", testData);
NSLog(@"clearData:  %@", clearData);

unsigned char keyBytes[16];
unsigned char *hex = (uint8_t *)keyHexData.bytes;

char byte_chars[3] = {'\0','\0','\0'};
for (int i=0; i<16; i++) {
    byte_chars[0] = hex[i*2];
    byte_chars[1] = hex[(i*2)+1];
    keyBytes[i] = strtol(byte_chars, NULL, 16);
}
NSData *keyData = [NSData dataWithBytes:keyBytes length:16];
NSLog(@"keyData:    %@", keyData);

NSData *ivData = [NSData dataWithBytes:(char []){0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} length:16];
NSLog(@"ivData:     %@", ivData);

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

ccStatus = CCCrypt(kCCDecrypt,
                   kCCAlgorithmAES128,
                   kCCOptionPKCS7Padding,
                   keyData.bytes, 
                   kCCKeySizeAES128,
                   ivData.bytes,
                   testData.bytes,
                   testData.length,
                   clearOut.mutableBytes,
                   clearOut.length,
                   &cryptBytes);

if (ccStatus != kCCSuccess) {
    NSLog(@"CCCrypt status: %d", ccStatus);
}

clearOut.length = cryptBytes;
NSLog(@"clearOut:   %@", clearOut);
keyHexData: <41393641 34344436 31343245 43463546 33444339 30303038 46453941 34383838>
testData:   <86a8b306 0f33db02 01e77e66 af5bcb3a>
clearData:  <31323334 35363738 39303132 33343536>
keyData:    <a96a44d6 142ecf5f 3dc90008 fe9a4888>
ivData:     <00000000 00000000 00000000 00000000>
clearOut:   <31323334 35363738 39303132 33343536>

Обратите внимание, что clearData была восстановлена ​​в clearOut

Это демонстрирует шифрование с помощью openssl и дешифрование с помощью CommonCrypto.

Проблемы, которые необходимо преодолеть:
1) Необходимо добавить Base64

Это отправная точка для завершения необходимого шифрования.

4 голосов
/ 14 сентября 2011

Вы предоставляете CCCrypt представление вашего ключа в UTF8 и IV, когда функция хочет необработанные байты.Если вы храните свой ключ и IV в строковой форме, где строка представляет собой шестнадцатеричное представление байтов, вам необходимо преобразовать эту строку шестнадцатеричных цифр обратно в необработанные байты.

Например, давайте возьмемIV всех нулевых байтов.

Вы предоставили OpenSSL IV "00000000000000000000000000000000".OpenSSL берет эту строку и преобразует каждые две шестнадцатеричные цифры в их соответствующие байты, и получает следующие 16 байтов:

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  (This is an array of 16 zero bytes)

Однако вы берете строку IV и получаете ее представление в UTF8.

// fyi, I plan to replace this later with a random iv
NSData *ivData = [@"00000000000000000000000000000000" dataUsingEncoding:NSUTF8StringEncoding];

В результате вы получите следующие байты:

30 30 30 30 30 30 30 30 ...

Поскольку символ '0' в UTF8 равен 0x30.Поэтому преобразуйте ваши шестнадцатеричные представления в байты, а не символы UTF8, и ваш ключ и IV будут соответствовать OpenSSL.

OpenSSL имеет функцию с именем set_hex , которая выполняет преобразование из строки в байты (которая вC хранятся в массиве unsigned char.

#define BIO_printf fprintf
#define bio_err stderr
int set_hex(char *in, unsigned char *out, int size)
        {
        int i,n;
        unsigned char j;

        n=strlen(in);
        if (n > (size*2))
                {
                BIO_printf(bio_err,"hex string is too long\n");
                return(0);
                }
        memset(out,0,size);
        for (i=0; i<n; i++)
                {
                j=(unsigned char)*in;
                *(in++)='\0';
                if (j == 0) break;
                if ((j >= '0') && (j <= '9'))
                        j-='0';
                else if ((j >= 'A') && (j <= 'F'))
                        j=j-'A'+10;
                else if ((j >= 'a') && (j <= 'f'))
                        j=j-'a'+10;
                else
                        {
                        BIO_printf(bio_err,"non-hex digit\n");
                        return(0);
                        }
                if (i&1)
                        out[i/2]|=j;
                else
                        out[i/2]=(j<<4);
                }
        return(1);
        }

Например,

    char iv_str[] = "12345678901234567890123456789012";
    unsigned char iv[16];
    if( !set_hex(iv_str, iv, sizeof(iv)) )
    {
        // Handle error where string was not a well-formed IV
    }
    printf("IV: "); for(int i=0;i<sizeof(iv); ++i) { printf("%02x", iv[i]); } printf("\n");
2 голосов
/ 14 сентября 2011

Для меня ваш 00ff349830193845af43984758690213 выглядит больше как шестнадцатеричные цифры, чем Base64. Тот факт, что в шестнадцатеричном байте стоит 16 байтов, делает хорошую ставку на то, что это IV, выраженное в гексах, а не в Base64.

...