2012-04-23 195 views
3

我發現AES加密和解密的樣品從http://pastie.org/pastes/297563/textiPhone:解密錯誤「*****與加密ccStatus == -4301」

它的工作原理很細,除了在解密的情況。當我加密用戶電子郵件字符串有一個很長的字符串([email protected]),加密和解密沒有問題。加密後我存儲在一個plist文件中,然後在解密時讀取它。但是,當我加密電子郵件字符串有很短的字符串(例如:[email protected]),加密是好的,但是當我嘗試解密字符串並將其放置在標籤中時,我發現解密是給出錯誤,如「問題與加密ccStatus == -4301」

UDPATE我發現這個問題解密正在發生的事情,只有當字符串長度爲「16」,否則它工作始終正常。請幫忙嗎?

請在下面找到該代碼。 這是加密和解密的代碼。

import "CryptoHelper.h" 

#define LOGGING_FACILITY(X, Y) \ 
if(!(X)) {   \ 
    NSLog(Y);  \ 
}     

#define LOGGING_FACILITY1(X, Y, Z) \ 
if(!(X)) {    \ 
NSLog(Y, Z);  \ 
} 

@interface CryptoHelper(Private) 
- (NSData *)doCipher:(NSData *)plainText key:(NSData *)theSymmetricKey context:(CCOperation)encryptOrDecrypt padding:(CCOptions *)pkcs7; 
- (NSString *)base64EncodeData:(NSData*)dataToConvert; 
- (NSData*)base64DecodeString:(NSString *)string; 
@end 
@implementation CryptoHelper 

static CryptoHelper *MyCryptoHelper = nil; 

const uint8_t kKeyBytes[] = "abcdefgh"; // Must be 16 bytes 
static CCOptions pad = 0; 
static const char encodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/"; 

- (NSString*)encryptString:(NSString*)string 
{ 
    NSRange fullRange; 
    fullRange.length = [string length]; 
    fullRange.location = 0; 

    uint8_t buffer[[string length]]; 

    [string getBytes:&buffer maxLength:[string length] usedLength:NULL encoding:NSUTF8StringEncoding options:0 range:fullRange remainingRange:NULL]; 

    NSData *plainText = [NSData dataWithBytes:buffer length:[string length]]; 

    NSData *encryptedResponse = [self doCipher:plainText key:symmetricKey context:kCCEncrypt padding:&pad]; 

    return [self base64EncodeData:encryptedResponse]; 
} 

- (NSString*)decryptString:(NSString*)string 
{ 
    NSLog(@"string: %@", string); 

    NSData *decryptedResponse = [self doCipher:[self base64DecodeString:string] key:symmetricKey context:kCCDecrypt padding:&pad]; 

    NSString *result = [NSString stringWithFormat:@"decryptedResponse: %@", decryptedResponse]; 
    NSLog(@"decryptedResponse: %@", result); 

    return [NSString stringWithCString:[decryptedResponse bytes] length:[decryptedResponse length]]; 
} 

- (NSData *)doCipher:(NSData *)plainText key:(NSData *)theSymmetricKey context:(CCOperation)encryptOrDecrypt padding:(CCOptions *)pkcs7 
{ 
    CCCryptorStatus ccStatus = kCCSuccess; 
    // Symmetric crypto reference. 
    CCCryptorRef thisEncipher = NULL; 
    // Cipher Text container. 
    NSData * cipherOrPlainText = nil; 
    // Pointer to output buffer. 
    uint8_t * bufferPtr = NULL; 
    // Total size of the buffer. 
    size_t bufferPtrSize = 0; 
    // Remaining bytes to be performed on. 
    size_t remainingBytes = 0; 
    // Number of bytes moved to buffer. 
    size_t movedBytes = 0; 
    // Length of plainText buffer. 
    size_t plainTextBufferSize = 0; 
    // Placeholder for total written. 
    size_t totalBytesWritten = 0; 
    // A friendly helper pointer. 
    uint8_t * ptr; 

    // Initialization vector; dummy in this case 0's. 
    uint8_t iv[kCCBlockSizeAES128]; 
    memset((void *) iv, 0x0, (size_t) sizeof(iv)); 

    LOGGING_FACILITY(plainText != nil, @"PlainText object cannot be nil."); 
    LOGGING_FACILITY(theSymmetricKey != nil, @"Symmetric key object cannot be nil."); 
    LOGGING_FACILITY(pkcs7 != NULL, @"CCOptions * pkcs7 cannot be NULL."); 
    LOGGING_FACILITY([theSymmetricKey length] == kCCKeySizeAES128, @"Disjoint choices for key size."); 

    plainTextBufferSize = [plainText length]; 

    LOGGING_FACILITY(plainTextBufferSize > 0, @"Empty plaintext passed in."); 

    // We don't want to toss padding on if we don't need to 
    if(encryptOrDecrypt == kCCEncrypt) 
    { 
     if(*pkcs7 != kCCOptionECBMode) 
     { 
      if((plainTextBufferSize % kCCBlockSizeAES128) == 0) 
      { 
       *pkcs7 = 0x0000; 
      } 
      else 
      { 
       *pkcs7 = kCCOptionPKCS7Padding; 
      } 
     } 
    } 
    else if(encryptOrDecrypt != kCCDecrypt) 
    { 
     LOGGING_FACILITY1(0, @"Invalid CCOperation parameter [%d] for cipher context.", *pkcs7); 
    } 

    // Create and Initialize the crypto reference. 
    ccStatus = CCCryptorCreate(encryptOrDecrypt, 
           kCCAlgorithmAES128, 
           *pkcs7, 
           (const void *)[theSymmetricKey bytes], 
           kCCKeySizeAES128, 
           (const void *)iv, 
           &thisEncipher 
           ); 

    LOGGING_FACILITY1(ccStatus == kCCSuccess, @"Problem creating the context, ccStatus == %d.", ccStatus); 

    // Calculate byte block alignment for all calls through to and including final. 
    bufferPtrSize = CCCryptorGetOutputLength(thisEncipher, plainTextBufferSize, true); 

    // Allocate buffer. 
    bufferPtr = malloc(bufferPtrSize * sizeof(uint8_t)); 

    // Zero out buffer. 
    memset((void *)bufferPtr, 0x0, bufferPtrSize); 

    // Initialize some necessary book keeping. 

    ptr = bufferPtr; 

    // Set up initial size. 
    remainingBytes = bufferPtrSize; 

    // Actually perform the encryption or decryption. 
    ccStatus = CCCryptorUpdate(thisEncipher, 
           (const void *) [plainText bytes], 
           plainTextBufferSize, 
           ptr, 
           remainingBytes, 
           &movedBytes 
           ); 

    LOGGING_FACILITY1(ccStatus == kCCSuccess, @"Problem with CCCryptorUpdate, ccStatus == %d.", ccStatus); 

    // Handle book keeping. 
    ptr += movedBytes; 
    remainingBytes -= movedBytes; 
    totalBytesWritten += movedBytes; 

    // Finalize everything to the output buffer. 
    ccStatus = CCCryptorFinal( thisEncipher, 
           ptr, 
           remainingBytes, 
           &movedBytes 
          ); 

    totalBytesWritten += movedBytes; 

    if(thisEncipher) 
    { 
     (void) CCCryptorRelease(thisEncipher); 
     thisEncipher = NULL; 
    } 

    LOGGING_FACILITY1(ccStatus == kCCSuccess, @"Problem with encipherment ccStatus == %d", ccStatus); 

    cipherOrPlainText = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)totalBytesWritten]; 

    if(bufferPtr) free(bufferPtr); 

    return cipherOrPlainText; 


} 

#pragma mark - 
#pragma mark Base64 Encode/Decoder 
- (NSString *)base64EncodeData:(NSData*)dataToConvert 
{ 
    if ([dataToConvert length] == 0) 
     return @""; 

    char *characters = malloc((([dataToConvert length] + 2)/3) * 4); 
    if (characters == NULL) 
     return nil; 

    NSUInteger length = 0; 

    NSUInteger i = 0; 
    while (i < [dataToConvert length]) 
    { 
     char buffer[3] = {0,0,0}; 
     short bufferLength = 0; 
     while (bufferLength < 3 && i < [dataToConvert length]) 
      buffer[bufferLength++] = ((char *)[dataToConvert bytes])[i++]; 

     // Encode the bytes in the buffer to four characters, including padding "=" characters if necessary. 
     characters[length++] = encodingTable[(buffer[0] & 0xFC) >> 2]; 
     characters[length++] = encodingTable[((buffer[0] & 0x03) << 4) | ((buffer[1] & 0xF0) >> 4)]; 
     if (bufferLength > 1) 
      characters[length++] = encodingTable[((buffer[1] & 0x0F) << 2) | ((buffer[2] & 0xC0) >> 6)]; 
     else characters[length++] = '='; 
     if (bufferLength > 2) 
      characters[length++] = encodingTable[buffer[2] & 0x3F]; 
     else characters[length++] = '=';  
    } 

    return [[[NSString alloc] initWithBytesNoCopy:characters length:length encoding:NSASCIIStringEncoding freeWhenDone:YES] autorelease]; 
} 

- (NSData*)base64DecodeString:(NSString *)string 
{ 
    if (string == nil) 
     [NSException raise:NSInvalidArgumentException format:nil]; 
    if ([string length] == 0) 
     return [NSData data]; 

    static char *decodingTable = NULL; 
    if (decodingTable == NULL) 
    { 
     decodingTable = malloc(256); 
     if (decodingTable == NULL) 
      return nil; 
     memset(decodingTable, CHAR_MAX, 256); 
     NSUInteger i; 
     for (i = 0; i < 64; i++) 
      decodingTable[(short)encodingTable[i]] = i; 
    } 

    const char *characters = [string cStringUsingEncoding:NSASCIIStringEncoding]; 
    if (characters == NULL)  // Not an ASCII string! 
     return nil; 
    char *bytes = malloc((([string length] + 3)/4) * 3); 
    if (bytes == NULL) 
     return nil; 
    NSUInteger length = 0; 

    NSUInteger i = 0; 
    while (YES) 
    { 
     char buffer[4]; 
     short bufferLength; 
     for (bufferLength = 0; bufferLength < 4; i++) 
     { 
      if (characters[i] == '\0') 
       break; 
      if (isspace(characters[i]) || characters[i] == '=') 
       continue; 
      buffer[bufferLength] = decodingTable[(short)characters[i]]; 
      if (buffer[bufferLength++] == CHAR_MAX)  // Illegal character! 
      { 
       free(bytes); 
       return nil; 
      } 
     } 

     if (bufferLength == 0) 
      break; 
     if (bufferLength == 1)  // At least two characters are needed to produce one byte! 
     { 
      free(bytes); 
      return nil; 
     } 

     // Decode the characters in the buffer to bytes. 
     bytes[length++] = (buffer[0] << 2) | (buffer[1] >> 4); 
     if (bufferLength > 2) 
      bytes[length++] = (buffer[1] << 4) | (buffer[2] >> 2); 
     if (bufferLength > 3) 
      bytes[length++] = (buffer[2] << 6) | buffer[3]; 
    } 

    realloc(bytes, length); 

    return [NSData dataWithBytesNoCopy:bytes length:length]; 
} 

#pragma mark - 
#pragma mark Singleton methods 
- (id)init 
{ 
    if(self = [super init]) 
    { 
     symmetricKey = [[NSData dataWithBytes:kKeyBytes length:sizeof(kKeyBytes)] retain]; 
    } 
    return self; 
} 

+ (CryptoHelper*)sharedInstance 
{ 
    @synchronized(self) 
    { 
     if (MyCryptoHelper == nil) 
     { 
      [[self alloc] init]; 
     } 
    } 
    return MyCryptoHelper; 
} 

+ (id)allocWithZone:(NSZone *)zone 
{ 
    @synchronized(self) 
    { 
     if (MyCryptoHelper == nil) 
     { 
      MyCryptoHelper = [super allocWithZone:zone]; 
      return MyCryptoHelper; // assignment and return on first allocation 
     } 
    } 
    return nil; // on subsequent allocation attempts return nil 
} 

- (id)copyWithZone:(NSZone *)zone 
{ 
    return self; 
} 

- (id)retain 
{ 
    return self; 
} 

- (unsigned)retainCount 
{ 
    return UINT_MAX; // denotes an object that cannot be released 
} 

- (void)release 
{ 
    //do nothing 
} 

- (id)autorelease 
{ 
    return self; 
} 

@end 

下面的代碼是上面的來電,

-(NSString *) getUsername 
    { 
     NSString *usernameString = NULL; 

     // Data.plist code 
     // get paths from root direcory 
     NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); 
     // get documents path 
     NSString *documentsPath = [paths objectAtIndex:0]; 
     // get the path to our Data/plist file 
     NSString *plistPath = [documentsPath stringByAppendingPathComponent:LOGIN_PLIST]; 

     // check to see if Data.plist exists in documents 
     if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) 
     { 
      // if not in documents, get property list from main bundle 
      plistPath = [[NSBundle mainBundle] pathForResource:@"LoginInfo" ofType:@"plist"]; 
     } 

     // read property list into memory as an NSData object 
     NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath]; 
     NSString *errorDesc = nil; 
     NSPropertyListFormat format; 
     // convert static property liost into dictionary object 
     NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc]; 
     if (!temp) 
     { 
      //NSLog(@"Error reading plist: %@, format: %lu", errorDesc, format); 
     } 
     else 
     { 
      NSString *nameStr=[temp objectForKey:@"Username"];  

      NSLog(@"nameStr: %@",nameStr); 

      // do AES128 decryption 
      CryptoHelper *pCrypto = [CryptoHelper sharedInstance]; 
      usernameString = [pCrypto decryptString:nameStr]; 
// usernameString becomes empty after when encryption and trying to decrypt. 
      NSLog(@"usernameString: %@: usernameString length: %ld", usernameString, [usernameString length]); 

     } 

     return usernameString; 
    } 

回答

1

你的代碼是基於蘋果的'CryptoExercise' exampleSecKeyWrapper類。 使用doCipher方法時,我剛碰到相同的錯誤信息。

-4301」在這種情況下,可變ccStatus的CCCryptoFinal呼叫,後者返回類型CCryptorStatus的值後的值。它在CommonCryptor.h定義如下:

enum { 
    kCCSuccess   = 0, 
    kCCParamError  = -4300, 
    kCCBufferTooSmall = -4301, 
    kCCMemoryFailure = -4302, 
    kCCAlignmentError = -4303, 
    kCCDecodeError  = -4304, 
    kCCUnimplemented = -4305 
}; 

所以不知何故在doCipher方法創建的緩衝區太小,這是奇怪的,因爲它的大小是通過CCCryptorGetOutputLength確定,就如同文檔CCCryptorFinal提示:

@result kCCBufferTooSmall indicates insufficent space in the dataOut buffer. 
     The caller can use CCCryptorGetOutputLength() to determine the required 
     output buffer size in this case. The operation can be retried; no state is 
     lost when this is returned. 

我有預感,從蘋果例如的SecKeyWrapper類是不是免費的錯誤。我會看看我能否解決問題或找到另一種在iPhone上進行AES加密的方法。 Rob Napier和Mugunth Kumar的書'iOS 5 programming - Pushing the limits'中有一個解釋和示例代碼,我會嘗試。此外,作者還推薦了兩個WWDC會話,地址分別爲developer.apple.com

  • WWDC 2010 「專場204:創建安全的應用程序」
  • WWDC 2011 「專場208:保護的iOS應用程序」

編輯:

我剛剛發現的錯誤。它在doCipher方法中。可以替換以下行:

// We don't want to toss padding on if we don't need to 
    if(encryptOrDecrypt == kCCEncrypt) 
    { 
     if(*pkcs7 != kCCOptionECBMode) 
     { 
      if((plainTextBufferSize % kCCBlockSizeAES128) == 0) 
      { 
       *pkcs7 = 0x0000; 
      } 
      else 
      { 
       *pkcs7 = kCCOptionPKCS7Padding; 
      } 
     } 
    } 
    else if(encryptOrDecrypt != kCCDecrypt) 
    { 
     LOGGING_FACILITY1(0, @"Invalid CCOperation parameter [%d] for cipher context.", *pkcs7); 
    } 

與下面幾行:

// check for valid context parameter 
if (encryptOrDecrypt != kCCEncrypt && encryptOrDecrypt != kCCDecrypt) { 
    LOGGING_FACILITY1(0, @"Invalid CCOperation parameter [%d] for cipher context.", encryptOrDecrypt); 
} 

由於奧爾特溫低於所指出的,你還必須更換第三個參數在隨後的CCCryptorCreate調用總是kCCOptionPKCS7Padding:

// Create and Initialize the crypto reference. 
ccStatus = CCCryptorCreate(encryptOrDecrypt, 
          kCCAlgorithmAES128, 
          kCCOptionPKCS7Padding, 
          (const void *)[theSymmetricKey bytes], 
          kCCKeySizeAES128, 
          (const void *)iv, 
          &thisEncipher 
          ); 
+0

因此,基本上你會刪除完整的第一個if case,因爲它會導致錯誤。由於刪除了第一個if,case else變成了一個簡單的if,所以這個條件還需要保留前一個else,即encryptOrDecrypt!= kCCEncrypt。 – Tafkadasoh 2012-08-13 18:15:50

+0

您的解決方案對我無效。當加密一個34字節長的字符串並再次解密時,結果只有32字節長,最後2個字符被截斷。 – 2013-05-17 20:36:01

0

我發現,它爲我工作,當我更換

// We don't want to toss padding on if we don't need to 
if(encryptOrDecrypt == kCCEncrypt) 
{ 
    if(*pkcs7 != kCCOptionECBMode) 
    { 
     if((plainTextBufferSize % kCCBlockSizeAES128) == 0) 
     { 
      *pkcs7 = 0x0000; 
     } 
     else 
     { 
      *pkcs7 = kCCOptionPKCS7Padding; 
     } 
    } 
} 
else if(encryptOrDecrypt != kCCDecrypt) 
{ 
    LOGGING_FACILITY1(0, @"Invalid CCOperation parameter [%d] for cipher context.", *pkcs7); 
} 

// Create and Initialize the crypto reference. 
ccStatus = CCCryptorCreate(encryptOrDecrypt, 
          kCCAlgorithmAES128, 
          *pkcs7, 
          (const void *)[theSymmetricKey bytes], 
          kCCKeySizeAES128, 
          (const void *)iv, 
          &thisEncipher 
          ); 

通過

// We don't want to toss padding on if we don't need to 
if (encryptOrDecrypt != kCCEncrypt && encryptOrDecrypt != kCCDecrypt) { 
    LOGGING_FACILITY1(0, @"Invalid CCOperation parameter [%d] for cipher context.", *pkcs7); 
} 

// Create and Initialize the crypto reference. 
ccStatus = CCCryptorCreate(encryptOrDecrypt, 
          kCCAlgorithmAES128, 
          kCCOptionPKCS7Padding, 
          (const void *)[theSymmetricKey bytes], 
          kCCKeySizeAES128, 
          (const void *)iv, 
          &thisEncipher 
          ); 

從本質上講,我總是CCCryptorCreatekCCOptionPKCS7Padding選項設置無論加密或解密,並不管文本長度。

請注意,我的情況是有限的,我沒有做任何測試,這是否適用於所有情況! 沒有保證!

+0

感謝您爲我的解決方案的50%提供信貸...不。 – Tafkadasoh 2013-06-17 11:03:37

+0

您的解決方案與我的相反。你完全刪除填充,而我總是使用它。這兩種解決方案都源自Apple示例代碼。我不知道我應該讚揚你。此外,工作解決方案應提供upvotes(您的解決方案不適用於我),不承認某人投入答案的時間。 – 2013-06-17 15:42:28