Шифрование AES для NSString на iPhone - программирование

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

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

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

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

4b9b3361

Ответ 1

Поскольку вы не размещали какой-либо код, трудно точно знать, с какими проблемами вы сталкиваетесь. Тем не менее, сообщение в блоге, на которое вы ссылаетесь, похоже, работает довольно прилично... кроме дополнительной запятой в каждом вызове 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 входит в основную ОС на iPhone. И 10.4, и 10.5 имеют /usr/include/CommonCrypto, хотя 10.5 имеет справочную страницу для CCCryptor.3cc, а 10.4 - нет, поэтому YMMV.


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

Ответ 2

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

Он использует категории для расширения 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

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

- Майкл

Ответ 3

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

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

Ответ 4

Я немного подождал @QuinnTaylor, чтобы обновить его ответ, но так как он этого не сделал, вот ответ немного более четко и так, что он будет загружаться на XCode7 (и, возможно, больше). Я использовал это в приложении Cocoa, но, скорее всего, это будет хорошо работать с приложением 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 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 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];
}