Шифрование AES для NSString на iPhone

Может ли кто-нибудь указать мне в правильном направлении, чтобы иметь возможность зашифровать строку, возвращая другую строку с зашифрованными данными? (Я пробовал использовать шифрование AES256.) Я хочу написать метод, который принимает два экземпляра NSString, один из которых представляет собой сообщение для шифрования, а другой - «код доступа» для его шифрования - я подозреваю, что мне придется сгенерировать ключ шифрования с паролем, который можно отменить, если код доступа предоставляется вместе с зашифрованными данными. Затем метод должен вернуть NSString, созданный из зашифрованных данных.

Я пробовал метод, подробно описанный в первый комментарий к этому сообщению, но пока мне не повезло. В CryptoExercise от Apple определенно что-то есть, но я не могу в этом разобраться. .. Я видел много ссылок на CCCrypt, но во всех случаях, когда я его использовал, он терпел неудачу.

Мне также нужно было бы расшифровать зашифрованную строку, но я надеюсь, что это так же просто, как kCCEncrypt / kCCDecrypt.


person Boz    schedule 09.09.2009    source источник
comment
Обратите внимание, что я назначил награду за ответ Роба Напьера, который предоставил безопасную версию ответа.   -  person Maarten Bodewes    schedule 28.10.2012


Ответы (5)


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

Более поздний комментарий к этому сообщению включает этот адаптированный код, который работает для меня и кажется немного более простым. Если вы включите их код для категории NSData, вы можете написать что-то вроде этого: (Примечание: вызовы printf() предназначены только для демонстрации состояния данных в различных точках - в реальном приложении не имеет смысла печатать такие ценности.)

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSString *key = @"my password";
    NSString *secret = @"text to encrypt";

    NSData *plain = [secret dataUsingEncoding:NSUTF8StringEncoding];
    NSData *cipher = [plain AES256EncryptWithKey:key];
    printf("%s\n", [[cipher description] UTF8String]);

    plain = [cipher AES256DecryptWithKey:key];
    printf("%s\n", [[plain description] UTF8String]);
    printf("%s\n", [[[NSString alloc] initWithData:plain encoding:NSUTF8StringEncoding] UTF8String]);

    [pool drain];
    return 0;
}

Учитывая этот код и тот факт, что зашифрованные данные не всегда хорошо переводятся в NSString, может быть удобнее написать два метода, которые объединяют нужную вам функциональность в прямом и обратном направлениях ...

- (NSData*) encryptString:(NSString*)plaintext withKey:(NSString*)key {
    return [[plaintext dataUsingEncoding:NSUTF8StringEncoding] AES256EncryptWithKey:key];
}

- (NSString*) decryptData:(NSData*)ciphertext withKey:(NSString*)key {
    return [[[NSString alloc] initWithData:[ciphertext AES256DecryptWithKey:key]
                                  encoding:NSUTF8StringEncoding] autorelease];
}

Это определенно работает в Snow Leopard, и @Boz сообщает, что CommonCrypto является частью Core OS на iPhone. И 10.4, и 10.5 имеют /usr/include/CommonCrypto, хотя в 10.5 есть справочная страница для CCCryptor.3cc, а в 10.4 нет, поэтому YMMV.


РЕДАКТИРОВАТЬ: См. этот дополнительный вопрос об использовании кодировки Base64 для представления байтов зашифрованных данных в виде строки. (при желании) с использованием безопасных преобразований без потерь.

person Quinn Taylor    schedule 09.09.2009
comment
Спасибо. CommonCrypto является частью Core OS на iPhone, и я тоже использую 10.6. - person Boz; 09.09.2009
comment
Я сделал -1, потому что указанный код опасно небезопасен. Вместо этого посмотрите на ответ Роба Напьера. Его запись в блоге robnapier.net/aes-commoncrypto подробно описывает, почему это небезопасно. - person Erik Engheim; 29.05.2014
comment
В моем случае это решение не работает. У меня есть строка, которую я хочу декодировать: U2FsdGVkX1 + MEhsbofUNj58m + 8tu9ifAKRiY / Zf8YIw =, и у меня есть ключ: 3841b8485cd155d932a2d601b8cee2ec. Я не могу расшифровать строку с помощью ключа с вашим решением. Спасибо - person George; 11.06.2014
comment
Это решение не работает в приложении Cocoa на El Capitan с XCode7. ARC запрещает autorelease. - person Volomike; 17.01.2016
comment
@QuinnTaylor Я могу отредактировать этот ответ, но хотел дать вам возможность изменить его по своему усмотрению. Я исправил ваш код здесь. Также вы можете указать, что без этого адаптированного кода он не будет компилироваться. Итак, я заставил его работать над приложением Cocoa на El Capitan с XCode7. Теперь то, что я пытаюсь сделать, - это выяснить, как Base64Encode / Base64Decode эти данные, чтобы их можно было передавать без помех при передаче, а не возвращать необработанные данные. - person Volomike; 17.01.2016
comment
@QuinnTaylor Вот краткий ответ, чтобы вы могли переписать свой ответ. Я заставил его работать в XCode7 и включил все части. Плюс я добавил кодирование / декодирование base64. Работает отлично! Я даже тестировал с японскими строками, и это работает - так что здесь поддерживается UTF-8. - person Volomike; 17.01.2016

Я собрал коллекцию категорий для NSData и NSString, в которых используются решения, найденные на блог Джеффа Ламарче и некоторые подсказки Куинн Тейлор здесь, на Stack Overflow.

Он использует категории для расширения NSData для обеспечения шифрования AES256, а также предлагает расширение NSString для безопасного кодирования зашифрованных данных в строки с помощью BASE64.

Вот пример, показывающий использование для шифрования строк:

NSString *plainString = @"This string will be encrypted";
NSString *key = @"YourEncryptionKey"; // should be provided by a user

NSLog( @"Original String: %@", plainString );

NSString *encryptedString = [plainString AES256EncryptWithKey:key];
NSLog( @"Encrypted String: %@", encryptedString );

NSLog( @"Decrypted String: %@", [encryptedString AES256DecryptWithKey:key] );

Получите полный исходный код здесь:

https://gist.github.com/838614

Спасибо за полезные советы!

-- Майкл

person Michael Thiel    schedule 22.02.2011
comment
NSString * key = @YourEncryptionKey; // должен быть предоставлен пользователем. Можем ли мы сгенерировать случайный безопасный 256-битный ключ вместо ключа, предоставленного пользователем. - person Pranav Jaiswal; 24.04.2012
comment
Ссылка Джеффа Ламарша не работает - person whyoz; 27.02.2018
comment
@michael - не могли бы вы помочь мне в этом stackoverflow.com/questions/63632975/ Спасибо - person Chandni; 31.08.2020

@owlstead, относительно вашего запроса на «криптографически безопасный вариант одного из заданных ответов» см. RNCryptor . Он был разработан, чтобы делать именно то, что вы запрашиваете (и был создан в ответ на проблемы с кодом, перечисленным здесь).

RNCryptor использует PBKDF2 с солью, предоставляет случайный IV и присоединяет HMAC (также сгенерированный из PBKDF2 с его собственной солью. Он поддерживает синхронные и асинхронные операции.

person Rob Napier    schedule 14.08.2012
comment
Интересный код, который, вероятно, стоит того. Какое количество итераций для PBKDF2 и для чего вы рассчитываете HMAC? Я полагаю, это только зашифрованные данные? Я не мог легко найти это в предоставленной документации. - person Maarten Bodewes; 15.08.2012
comment
См. Подробности в разделе «Лучшие практики безопасности». Я рекомендую 10 тыс. Итераций на iOS (~ 80 мс на iPhone 4). И да, шифрование вместо HMAC. Я, вероятно, просмотрю страницу формата данных сегодня вечером, чтобы убедиться, что она актуальна до версии 2.0 (основные документы актуальны, но я не могу вспомнить, редактировал ли я страницу формата данных). - person Rob Napier; 15.08.2012
comment
А, да, нашел в документации количество раундов и посмотрел код. Я вижу там функции очистки и отдельные HMAC и ключи шифрования. Если позволит время, завтра попробую взглянуть повнимательнее. Потом я выставлю баллы. - person Maarten Bodewes; 15.08.2012
comment
Я вижу RNCryptor в github.com/rnapier/RNCryptor, но кажется, что его невозможно зашифровать (и расшифровать ) из NSString в NSString. Меня очень интересует такая возможность, есть какие-нибудь новости? - person ndman; 05.10.2012
comment
Зашифруйте в NSData и используйте один из многих кодировщиков Base64, чтобы преобразовать его в строку. Невозможно зашифровать строку в строку без кодировщика данных в строку. - person Rob Napier; 07.10.2012
comment
Я попытался использовать RNCryptor для расшифровки строки, но безуспешно. У меня есть строка, которую я хочу декодировать: U2FsdGVkX1 + MEhsbofUNj58m + 8tu9ifAKRiY / Zf8YIw = и у меня есть ключ: 3841b8485cd155d932a2d601b8cee2ec. Я не могу расшифровать строку с помощью RNDecrypt - person George; 11.06.2014
comment
Вы должны использовать один и тот же формат AES для шифрования и дешифрования. Стандартного формата AES нет. Если вы хотите использовать RNCryptor для дешифрования, вам необходимо использовать RNCryptor для шифрования. - person Rob Napier; 11.06.2014
comment
@RobNapier. Если я использую RNCryptor, должен ли я использовать информацию о соответствии экспорта (ДА) в iTunes-Connect? - person Jack; 27.07.2018
comment
@Jack По совету моего юриста (который очень красочно описал мою нехватку знаний в области экспортного законодательства ...) я больше не даю советов по экспортному законодательству. Вам нужно будет обсудить это со своим юристом. - person Rob Napier; 27.07.2018
comment
@RobNapier Я пробовал ваше шифрование, но возможно ли, что мы сгенерируем одну и ту же зашифрованную строку в объекте c, Java и в .Net. Я использовал один и тот же ключ для всех, но для получения другой зашифрованной строки в iOS. Не могли бы вы направить меня. спасибо Вот мой вопрос по этому поводу. stackoverflow.com/questions/63632975/ - person Chandni; 31.08.2020
comment
@Chandni. Безопасная схема шифрования будет давать вам новое шифрование каждый раз, когда вы ее запускаете. Это сделано намеренно. В случае RNCryptor он включает 3 больших случайных числа специально для этого, но все схемы безопасного шифрования делают это каким-то образом (путем введения некоторого фрагмента случайных данных). Есть несколько очень специализированных исключений, когда каждое шифрование создает уникальный результат, но, как правило, этого следует ожидать. Вы можете найти совместимые реализации RNCryptor на ObjC, Java и C # здесь: github.com/RNCryptor - person Rob Napier; 31.08.2020

Я немного подождал, пока @QuinnTaylor обновит его ответ, но, поскольку он этого не сделал, вот ответ немного более четко и таким образом, чтобы он загрузился в XCode7 (и, возможно, больше). Я использовал это в приложении Какао, но он, вероятно, будет работать и с приложением iOS. Нет ошибок ARC.

Вставьте перед любым разделом @implementation в файле AppDelegate.m или AppDelegate.mm.

#import <CommonCrypto/CommonCryptor.h>

@implementation NSData (AES256)

- (NSData *)AES256EncryptWithKey:(NSString *)key {
    // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
    bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)

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

    NSUInteger dataLength = [self length];

    //See the doc: For block ciphers, the output size will always be less than or 
    //equal to the input size plus the size of one block.
    //That's why we need to add the size of one block here
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);

    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                     keyPtr, kCCKeySizeAES256,
                                     NULL /* initialization vector (optional) */,
                                     [self bytes], dataLength, /* input */
                                     buffer, bufferSize, /* output */
                                     &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        //the returned NSData takes ownership of the buffer and will free it on deallocation
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }

    free(buffer); //free the buffer;
    return nil;
}

- (NSData *)AES256DecryptWithKey:(NSString *)key {
    // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
    bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)

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

    NSUInteger dataLength = [self length];

    //See the doc: For block ciphers, the output size will always be less than or 
    //equal to the input size plus the size of one block.
    //That's why we need to add the size of one block here
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);

    size_t numBytesDecrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                     keyPtr, kCCKeySizeAES256,
                                     NULL /* initialization vector (optional) */,
                                     [self bytes], dataLength, /* input */
                                     buffer, bufferSize, /* output */
                                     &numBytesDecrypted);

    if (cryptStatus == kCCSuccess) {
        //the returned NSData takes ownership of the buffer and will free it on deallocation
        return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
    }

    free(buffer); //free the buffer;
    return nil;
}

@end

Вставьте эти две функции в нужный вам класс @implementation. В моем случае я выбрал @implementation AppDelegate в моем файле AppDelegate.mm или AppDelegate.m.

- (NSString *) encryptString:(NSString*)plaintext withKey:(NSString*)key {
    NSData *data = [[plaintext dataUsingEncoding:NSUTF8StringEncoding] AES256EncryptWithKey:key];
    return [data base64EncodedStringWithOptions:kNilOptions];
}

- (NSString *) decryptString:(NSString *)ciphertext withKey:(NSString*)key {
    NSData *data = [[NSData alloc] initWithBase64EncodedString:ciphertext options:kNilOptions];
    return [[NSString alloc] initWithData:[data AES256DecryptWithKey:key] encoding:NSUTF8StringEncoding];
}
person Volomike    schedule 18.01.2016
comment
Примечание: 1. При расшифровке размер вывода будет меньше размера ввода, если есть заполнение (PKCS # 7). Нет причин увеличивать bufferSize, просто используйте размер зашифрованных данных. 2. Вместо того, чтобы распределять буфер и затем dataWithBytesNoCopy, просто выделите NSMutableData с помощью dataWithLength и используйте свойство mutableBytes для байтового указателя, а затем просто измените размер, установив его свойство length. 3. Использование строки напрямую для шифрования очень небезопасно, следует использовать производный ключ, такой как созданный PBKDF2. - person zaph; 19.01.2016
comment
@zaph, можно где-нибудь вставить pastebin / pastie, чтобы я мог видеть изменения? Кстати, в приведенном выше коде я просто адаптировал код, который я видел от Куинна Тейлора, чтобы заставить его работать. Я все еще изучаю это дело по ходу дела, и ваш вклад будет мне очень полезен. - person Volomike; 19.01.2016
comment
См. Этот ответ SO, и он даже имеет минимальную обработку ошибок и обрабатывает как шифрование, так и дешифрование. Нет необходимости увеличивать буфер при расшифровке, это просто меньше кода, не специализирующегося на дополнительном, если мало что можно получить. В случае, если требуется расширение ключа нулями (этого делать не следует), просто создайте изменяемую версию ключа и установите длину: keyData.length = kCCKeySizeAES256;. - person zaph; 19.01.2016
comment
См. Этот ответ SO для использования PBKDF2 для создания ключа из строки. - person zaph; 19.01.2016
comment
@Volomike Если я использую это, следует ли мне выбрать Экспорт информации о соответствии (ДА) в iTunes-Connect? - person Jack; 27.07.2018

Please use the below mentioned URL to encrypt string using AES excryption with 
key and IV values.

https://github.com/muneebahmad/AESiOSObjC

person SURESH SANKE    schedule 21.01.2020