2012-04-23 182 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