2009-09-09 12 views
118

Ai có thể chỉ cho tôi đúng hướng để có thể mã hóa một chuỗi, trả về một chuỗi khác với dữ liệu được mã hóa không? (Tôi đã thử với mã hóa AES256). Tôi muốn viết một phương thức nhận hai trường hợp NSString, một là thông điệp mã hóa và một là mật mã để mã hóa nó - Tôi nghi ngờ tôi phải tạo ra khóa mã hóa bằng mật mã, theo cách có thể đảo ngược nếu mật mã được cung cấp cùng với dữ liệu được mã hóa. Phương thức sau đó sẽ trả về một NSString được tạo từ dữ liệu được mã hóa.Mã hóa AES cho một NSString trên iPhone

Tôi đã thử kỹ thuật chi tiết trong the first comment on this post, nhưng tôi đã không có may mắn cho đến nay. Của Apple CryptoExercise chắc chắn có một cái gì đó, nhưng tôi không thể làm cho tinh thần của nó ... Tôi đã nhìn thấy rất nhiều tài liệu tham khảo để CCCrypt, nhưng nó không thành công trong mọi trường hợp tôi đã sử dụng nó.

Tôi cũng sẽ phải có khả năng giải mã một chuỗi được mã hóa, nhưng tôi hy vọng điều đó đơn giản như kCCEncrypt/kCCDecrypt.

+1

Xin lưu ý rằng tôi đã trả tiền thưởng cho câu trả lời của Rob Napier, người đã cung cấp phiên bản ** bảo mật **. –

Trả lời

124

Vì bạn chưa đăng bất kỳ mã nào, thật khó để biết chính xác bạn đang gặp sự cố nào. Tuy nhiên, bài đăng trên blog bạn liên kết dường như hoạt động khá rõ ràng ... ngoài dấu phẩy thừa trong mỗi cuộc gọi đến CCCrypt() đã gây ra lỗi biên dịch.

Nhận xét sau về bài đăng đó bao gồm this adapted code, phù hợp với tôi và có vẻ đơn giản hơn một chút. Nếu bạn bao gồm mã của họ cho thể loại NSData, bạn có thể viết một cái gì đó như thế này: (Lưu ý: Các cuộc gọi printf() chỉ để minh họa trạng thái của dữ liệu tại các điểm khác nhau - trong một ứng dụng thực tế, sẽ không có ý nghĩa để in như vậy giá trị.)

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; 
} 

với mã này, và thực tế là dữ liệu được mã hóa sẽ không luôn luôn dịch độc đáo vào một NSString, nó có thể là thuận tiện hơn để viết hai phương pháp quấn các chức năng bạn cần, ở phía trước và ngược lại. ..

- (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]; 
} 

này chắc chắn hoạt động trên Snow Leopard, và @Boz báo cáo rằng CommonCrypto là một phần của hệ điều hành lõi trên iPhone. Cả 10.4 và 10.5 có /usr/include/CommonCrypto, mặc dù 10.5 có trang người dùng cho CCCryptor.3cc và 10.4 thì không, vì vậy YMMV.


EDIT: Xem this follow-up question về việc sử dụng mã hóa Base64 cho đại diện byte dữ liệu được mã hóa như là một chuỗi (nếu muốn) sử dụng an toàn, chuyển đổi lossless.

+1

Cảm ơn. CommonCrypto là một phần của hệ điều hành lõi trên iPhone và tôi cũng chạy 10.6. – Boz

+0

Câu trả lời hay là Quinn. – GSD

+0

Tôi đã làm -1, bởi vì mã được tham chiếu là không an toàn một cách nguy hiểm. Hãy nhìn vào câu trả lời của Rob Napier. Bài viết blog của ông "http://robnapier.net/aes-commoncrypto chi tiết chính xác tại sao điều này là không an toàn. –

45

Tôi đã tập hợp các danh mục cho NSData và NSString sử dụng các giải pháp được tìm thấy trên Jeff LaMarche's blogsome hints bởi Quinn Taylor tại đây trên Stack Overflow.

Nó sử dụng các danh mục để mở rộng NSData để cung cấp mã hóa AES256 và cũng cung cấp phần mở rộng NSString cho dữ liệu được mã hóa BASE64 an toàn cho chuỗi.

Dưới đây là một ví dụ cho thấy việc sử dụng cho các chuỗi mã hóa:

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]); 

Lấy mã nguồn đầy đủ ở đây:

https://gist.github.com/838614

Cảm ơn tất cả các gợi ý hữu ích!

- Michael

+4

mã tuyệt vời !! Cảm ơn các gợi ý ... – Nick

+0

NSString * key = @ "YourEncryptionKey"; // nên được cung cấp bởi người dùng Chúng tôi có thể tạo khóa 256 bit bảo mật ngẫu nhiên, thay vì khóa được cung cấp bởi người dùng. –

+0

Liên kết Jeff LaMarche bị hỏng – whyoz

31

@owlstead, về yêu cầu của bạn cho "một biến thể mã hóa an toàn của một trong những câu trả lời được đưa ra," xin vui lòng xem RNCryptor. Nó được thiết kế để làm chính xác những gì bạn đang yêu cầu (và được xây dựng để đáp ứng các vấn đề với mã được liệt kê ở đây).

RNCryptor sử dụng PBKDF2 với muối, cung cấp một IV ngẫu nhiên, và gắn HMAC (còn được tạo ra từ PBKDF2 với muối riêng của mình. Nó hỗ trợ hoạt động đồng bộ và không đồng bộ.

+0

Mã thú vị và có thể đáng giá. Số lần lặp lại cho PBKDF2 là gì và bạn tính HMAC như thế nào? Tôi đoán chỉ là dữ liệu được mã hóa? Tôi không thể tìm thấy dễ dàng trong tài liệu được cung cấp. –

+0

Xem "Bảo mật thực hành tốt nhất" để biết chi tiết. Tôi khuyên bạn nên lặp lại 10k trên iOS (~ 80ms trên iPhone 4). Và có, mã hóa-hơn-HMAC. Có lẽ tôi sẽ xem qua trang "Định dạng dữ liệu" tối nay để đảm bảo nó được cập nhật trên v2.0 (các tài liệu chính được cập nhật, nhưng tôi không thể nhớ nếu tôi sửa đổi trang định dạng dữ liệu). –

+0

Ah, vâng, đã tìm thấy số vòng trong tài liệu và tìm mã. Tôi thấy các chức năng dọn dẹp và các khóa mã hóa và HMAC riêng biệt trong đó. Nếu thời gian cho phép tôi sẽ cố gắng và xem xét kỹ hơn vào ngày mai. Sau đó, tôi sẽ chỉ định các điểm. –

7

Tôi đợi một chút trên @QuinnTaylor để cập nhật câu trả lời của mình nhưng kể từ khi anh ta không, đây là câu trả lời rõ ràng hơn một chút và theo cách nó sẽ tải trên XCode7 (và có lẽ lớn hơn) .Tôi đã sử dụng nó trong ứng dụng Cocoa, nhưng có khả năng nó sẽ hoạt động tốt với ứng dụng iOS tốt, không có lỗi ARC.

Dán trước bất kỳ phần @thực hiện nào trong tệp AppDelegate.m hoặc AppDelegate.mm của bạn

#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 

Dán hai hàm này vào lớp @implementation bạn muốn. Trong trường hợp của tôi, tôi đã chọn @implementation AppDelegate trong tệp AppDelegate.mm hoặc AppDelegate.m của mình.

- (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]; 
} 
+0

Lưu ý: 1. Khi giải mã kích thước đầu ra sẽ nhỏ hơn kích thước đầu vào khi có đệm (PKCS # 7). Không có lý do để tăng bufferSize, chỉ cần sử dụng kích thước dữ liệu được mã hóa. 2. Thay vì malloc'ing một bộ đệm và sau đó 'dataWithBytesNoCopy' chỉ phân bổ một' NSMutableData' với 'dataWithLength' và sử dụng thuộc tính' mutableBytes' cho con trỏ byte và sau đó chỉ thay đổi kích thước bằng cách thiết lập thuộc tính 'length'. 3. Sử dụng chuỗi trực tiếp cho một mã hóa là rất không an toàn, một khóa có nguồn gốc nên được sử dụng như được tạo ra bởi PBKDF2. – zaph

+0

@zaph, bạn có thể làm một cây pastebin/pastie ở đâu đó để tôi có thể thấy những thay đổi không? BTW, trên đoạn mã trên, tôi chỉ thích nghi với mã tôi thấy từ Quinn Taylor để làm cho nó hoạt động. Tôi vẫn đang học kinh doanh này khi tôi đi, và đầu vào của bạn sẽ rất hữu ích cho tôi. – Volomike

+0

Xem [SO trả lời] này (http://stackoverflow.com/a/23641521/451475) và thậm chí nó có xử lý lỗi tối thiểu và xử lý cả mã hóa và giải mã. Không cần phải mở rộng bộ đệm trên giải mã, nó chỉ là ít mã không chuyên với một bổ sung nếu khi có rất ít để đạt được. Trong trường hợp mở rộng khóa bằng null là mong muốn (điều đó không nên được thực hiện) chỉ cần tạo một phiên bản có thể thay đổi của khóa và thiết lập độ dài: 'keyData.length = kCCKeySizeAES256;'. – zaph