2011-10-19 42 views
5

Tôi đang triển khai cho iOS một số mã giải mã cho thư bắt nguồn trên máy chủ mà tôi không có quyền kiểm soát. Vì vậy, các yêu cầu giải mã là:Mã hóa AES cho một NSString?

Cipher Method : AES256 
Cipher Mode: ECB 
Padding: PKCS5Padding 

Vì các thử nghiệm ban đầu của tôi không thể giải mã được. Vì vậy, tôi đã chơi xung quanh với some test vectors để xem mã mà tôi sử dụng là đúng,

Đây là mã mã hóa dữ liệu:

NSString+AESCrypt.h 
------------------- 
#import <Foundation/Foundation.h> 
#import "NSData+AESCrypt.h" 

@interface NSString (AESCrypt) 

- (NSString *)AES256EncryptWithKey:(NSString *)key; 
- (NSString *)AES256DecryptWithKey:(NSString *)key; 

@end 


NSString+AESCrypt.m 
------------------- 
#import "NSString+AESCrypt.h" 

@implementation NSString (AESCrypt) 

- (NSString *)AES256EncryptWithKey:(NSString *)key 
{ 
    NSData *plainData = [self dataUsingEncoding:NSUTF8StringEncoding]; 
    NSData *encryptedData = [plainData AES256EncryptWithKey:key]; 

    NSString *encryptedString = [encryptedData base64Encoding]; 

    return encryptedString; 
} 

- (NSString *)AES256DecryptWithKey:(NSString *)key 
{ 
    NSData *encryptedData = [NSData dataWithBase64EncodedString:self]; 
    NSData *plainData = [encryptedData AES256DecryptWithKey:key]; 

    NSString *plainString = [[NSString alloc] initWithData:plainData encoding:NSUTF8StringEncoding]; 

    return [plainString autorelease]; 
} 

@end 


NSData+AESCrypt.h 
------------------- 
#import <Foundation/Foundation.h> 

@interface NSData (AESCrypt) 

- (NSData *)AES256EncryptWithKey:(NSString *)key; 
- (NSData *)AES256DecryptWithKey:(NSString *)key; 

+ (NSData *)dataWithBase64EncodedString:(NSString *)string; 
- (id)initWithBase64EncodedString:(NSString *)string; 

- (NSString *)base64Encoding; 
- (NSString *)base64EncodingWithLineLength:(NSUInteger)lineLength; 

- (BOOL)hasPrefixBytes:(const void *)prefix length:(NSUInteger)length; 
- (BOOL)hasSuffixBytes:(const void *)suffix length:(NSUInteger)length; 

@end 

NSData+AESCrypt.m 
------------------- 
#import "NSData+AESCrypt.h" 
#import <CommonCrypto/CommonCryptor.h> 

static char encodingTable[64] = 
{ 
    'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 
    'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', 
    'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', 
    'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' 
}; 

@implementation NSData (AESCrypt) 

- (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, kCCOptionECBMode + 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, kCCOptionECBMode + 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; 
} 

#pragma mark - 

+ (NSData *)dataWithBase64EncodedString:(NSString *)string 
{ 
    return [[[NSData allocWithZone:nil] initWithBase64EncodedString:string] autorelease]; 
} 

- (id)initWithBase64EncodedString:(NSString *)string 
{ 
    NSMutableData *mutableData = nil; 

    if(string) 
    { 
     unsigned long ixtext = 0; 
     unsigned long lentext = 0; 
     unsigned char ch = 0; 
     unsigned char inbuf[4], outbuf[3]; 
     short i = 0, ixinbuf = 0; 
     BOOL flignore = NO; 
     BOOL flendtext = NO; 
     NSData *base64Data = nil; 
     const unsigned char *base64Bytes = nil; 

     // Convert the string to ASCII data. 
     base64Data = [string dataUsingEncoding:NSASCIIStringEncoding]; 
     base64Bytes = [base64Data bytes]; 
     mutableData = [NSMutableData dataWithCapacity:base64Data.length]; 
     lentext = base64Data.length; 

     while(YES) 
     { 
     if(ixtext >= lentext) break; 
     ch = base64Bytes[ixtext++]; 
     flignore = NO; 

     if((ch >= 'A') && (ch <= 'Z')) ch = ch - 'A'; 
     else if((ch >= 'a') && (ch <= 'z')) ch = ch - 'a' + 26; 
     else if((ch >= '0') && (ch <= '9')) ch = ch - '0' + 52; 
     else if(ch == '+') ch = 62; 
     else if(ch == '=') flendtext = YES; 
     else if(ch == '/') ch = 63; 
     else flignore = YES; 

     if(! flignore) 
     { 
      short ctcharsinbuf = 3; 
      BOOL flbreak = NO; 

      if(flendtext) 
      { 
       if(! ixinbuf) break; 
       if((ixinbuf == 1) || (ixinbuf == 2)) ctcharsinbuf = 1; 
       else ctcharsinbuf = 2; 
       ixinbuf = 3; 
       flbreak = YES; 
      } 

      inbuf [ixinbuf++] = ch; 

      if(ixinbuf == 4) 
      { 
       ixinbuf = 0; 
       outbuf [0] = (inbuf[0] << 2) | ((inbuf[1] & 0x30) >> 4); 
       outbuf [1] = ((inbuf[1] & 0x0F) << 4) | ((inbuf[2] & 0x3C) >> 2); 
       outbuf [2] = ((inbuf[2] & 0x03) << 6) | (inbuf[3] & 0x3F); 

       for(i = 0; i < ctcharsinbuf; i++) 
        [mutableData appendBytes:&outbuf[i] length:1]; 
      } 

      if(flbreak) break; 
     } 
     } 
    } 

    self = [self initWithData:mutableData]; 
    return self; 
} 

#pragma mark - 

- (NSString *)base64Encoding 
{ 
    return [self base64EncodingWithLineLength:0]; 
} 

- (NSString *)base64EncodingWithLineLength:(NSUInteger)lineLength 
{ 
    const unsigned char *bytes = [self bytes]; 
    NSMutableString *result = [NSMutableString stringWithCapacity:self.length]; 
    unsigned long ixtext = 0; 
    unsigned long lentext = self.length; 
    long ctremaining = 0; 
    unsigned char inbuf[3], outbuf[4]; 
    unsigned short i = 0; 
    unsigned short charsonline = 0, ctcopy = 0; 
    unsigned long ix = 0; 

    while(YES) 
    { 
     ctremaining = lentext - ixtext; 
     if(ctremaining <= 0) break; 

     for(i = 0; i < 3; i++) 
     { 
     ix = ixtext + i; 
     if(ix < lentext) inbuf[i] = bytes[ix]; 
     else inbuf [i] = 0; 
     } 

     outbuf [0] = (inbuf [0] & 0xFC) >> 2; 
     outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4); 
     outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6); 
     outbuf [3] = inbuf [2] & 0x3F; 
     ctcopy = 4; 

     switch(ctremaining) 
     { 
     case 1: 
      ctcopy = 2; 
      break; 
     case 2: 
      ctcopy = 3; 
      break; 
     } 

     for(i = 0; i < ctcopy; i++) 
     [result appendFormat:@"%c", encodingTable[outbuf[i]]]; 

     for(i = ctcopy; i < 4; i++) 
     [result appendString:@"="]; 

     ixtext += 3; 
     charsonline += 4; 

     if(lineLength > 0) 
     { 
     if(charsonline >= lineLength) 
     { 
      charsonline = 0; 
      [result appendString:@"\n"]; 
     } 
     } 
    } 

    return [NSString stringWithString:result]; 
} 

#pragma mark - 

- (BOOL)hasPrefixBytes:(const void *)prefix length:(NSUInteger)length 
{ 
    if(! prefix || ! length || self.length < length) return NO; 
    return (memcmp([self bytes], prefix, length) == 0); 
} 

- (BOOL)hasSuffixBytes:(const void *)suffix length:(NSUInteger)length 
{ 
    if(! suffix || ! length || self.length < length) return NO; 
    return (memcmp(((const char *)[self bytes] + (self.length - length)), suffix, length) == 0); 
} 

@end 

tôi thực hiện trên chức năng và ghi dữ liệu dẫn đến các bản ghi với điều này mã:

NSString * _secret = @"6bc1bee22e409f96e93d7e117393172a"; 
NSString * _key = @"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"; 

NSString *encryptedString = [_secret AES256EncryptWithKey:_key]; 
NSLog(@"Encrypted ID : %@", encryptedString); 

NSString *decryptedString = [encryptedString AES256DecryptWithKey:_key]; 
NSLog(@"Decrypted ID : %@", decryptedString); 

Kể từ vector kiểm tra, các mật mã được mã hóa phải được điều này:

f3eed1bdb5d2a03c064b5a7e3db181f8

Kết quả ghi:

2011-10-19 13:32:41.640 Ticket[2215:707] Encrypted ID : XWLsnTQvocXNkAqVisEgWTCPdYR6KPoIojezjN3fn/wuytQkpUZnNbzUoT4peeTK 
2011-10-19 13:32:41.641 Ticket[2215:707] Decrypted ID : 6bc1bee22e409f96e93d7e117393172a 

Tôi biết rằng ID mã hóa này là trong Base64, nhưng vẫn còn ngay cả khi tôi chuyển nó sang HEX, sản lượng thực tế thay đổi từ kết quả.

Tôi có thể quên tùy chọn nào? Mã hóa của NSData có trả về cái gì khác không ...?

Vì vậy, nếu ai đó có thể hướng dẫn tôi trên con đường phù hợp sẽ tuyệt vời, Chúc mừng.

+0

Chào mừng bạn đến SO; chúc mừng cho câu hỏi đầu tiên của bạn, rất rõ ràng và được viết tốt. – Cyrille

Trả lời

0

Tôi cố vấn đề là trang thử nghiệm-vector giả định rằng các giá trị hex được in này là nhị phân chứ không phải văn bản: Chuỗi "6b" trông giống như mã hóa utf-8 như sau: 0x3662.

để nhận chuỗi kiểm tra chính xác, trước tiên bạn phải mã hóa nó. chuỗi của bạn nên bắt đầu bằng: @ "kÁ¾â ....". Chuỗi đó sẽ dẫn đến biểu diễn hexa bên phải nếu nó sẽ được mã hóa với utf-8.

Bạn nên kiểm tra một trong hai aes-mã hóa của bạn với NSDatainitialized with an hex-string
hoặc bạn phải giải mã utf-8 đầu tiên để đóng gói tất cả những gì thành một chuỗi. Nhưng hãy cẩn thận: thường có những biểu tượng không thể được biểu diễn/in và thậm chí tệ hơn: nếu tesdata của bạn hoặc cypher chứa Zero-Byte thì điều này sẽ gây ra một số vấn đề bởi vì nó thường được sử dụng như một biểu tượng chấm dứt trong chuỗi thường chứa chỉ có thể đọc được charachters. (Không biết làm thế nào này tương tác với NSString)
hoặc sử dụng các biến thể đầu tiên để tạo ra một NSData ra khỏi một hex-chuỗi và sau đó chuyển đổi dữ liệu đến một NSString

Các vấn đề liên quan