2011-10-19 70 views
5

我正在實施iOS的一些解密代碼,用於源自我無法控制的服務器上的消息。所以解密要求是:對NSString的AES加密?

Cipher Method : AES256 
Cipher Mode: ECB 
Padding: PKCS5Padding 

由於我最初的試驗未能解密。所以我打得四處some test vectors看到我使用是正確的代碼,

這是對數據加密的代碼:

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 

我執行上述功能和產生的數據寫入日誌本代碼:

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

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

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

如從測試向量,加密的密碼應該是這樣的:

個f3eed1bdb5d2a03c064b5a7e3db181f8

結果日誌:

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 

我知道這個加密ID是Base64編碼,但仍即使我將其轉換爲十六進制,實際輸出的結果而變化。

我忘了什麼選項? NSData的編碼是否返回了其他內容...?

所以,如果有人能指引我走上正確的道路,那就太好了,乾杯。

+0

歡迎SO;祝賀你的第一個問題,寫得很清楚,寫得很好。 – Cyrille

回答

0

我討厭的問題是測試向量頁面假定這些打印的十六進制值是二進制文件而不是文本:字符串「6b」看起來像這樣編碼utf-8:0x3662。

爲了獲得正確的測試字符串,您必須先對其進行編碼。你的字符串應該以這個開頭:@「kÁ¾â....」。該字符串將導致正確的六進製表示,如果它將用utf-8編碼。

您應該使用NSDatainitialized with an hex-string
測試您的aes加密,或者您必須先解碼utf-8才能將所有內容打包爲一個字符串。但要注意:通常有些符號不能被表示/打印,甚至更糟:如果你的tesdata或密碼包含零字節,那麼這會引起一些問題,因爲它通常用作通常包含在字符串中的終止符號只有可讀的charachters。 (不知道該如何與NSString交互)
或使用第一種變體創建NSData了一個十六進制的字符串,然後將該數據轉換爲NSString