无法解密通过iPhone上的OpenSSL编码的数据
对我之前的问题的一种跟进: 如何从OpenSSLencryption数据获得初始化向量(iv)
我正在使用OpenSSL
命令行工具来encryption一个string,然后尝试使用<CommonCrypto/CommonCryptor.h>
来解密iPhone上的string。 使用Dropbox SDK,将带有encryptionstring的xml文件加载到iPhone上,在那里我的应用程序试图parsing和解密此文件中的string。
这是openssl命令的一个例子:
printf %s "Hello" | openssl enc -aes-128-cbc -K 00ff349830193845af43984758690213 -iv 0 -base64
上面的base 64string放在一个XML文件中,然后由应用程序parsing。
我正在使用Matt Gallagher的NSData来解码base64文本。 我假设这工作正常; 我还没有真正find一个testing它的好方法。 (来源: http : //cocoawithlove.com/2009/06/base64-encoding-options-on-mac-and.html )。
以下是解密encryptionstring的方法。
关键是在这种情况下的NSString等于@"00ff349830193845af43984758690213"
。
+ (NSString *)string:(NSString *)encryptedString withAES128Key:(NSString *)key { // decode base64, from Matt Gallagher's NSData category NSData *b64DecodedData = [NSData dataFromBase64String:encryptedString]; NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding]; // fyi, I plan to replace this later with a random iv NSData *ivData = [@"00000000000000000000000000000000" dataUsingEncoding:NSUTF8StringEncoding]; // decrypt the string NSData *decodedData = [self doCipher:b64DecodedData iv:ivData key:keyData context:kCCDecrypt]; NSString *unencryptedString = [[NSString alloc] initWithBytes:[decodedData bytes] length:[decodedData length] encoding:NSUTF8StringEncoding]; return [unencryptedString autorelease]; }
下面是实际解密的方法:(这个方法的信用转到一个同伴的计算器用户 。)
+ (NSData *)doCipher:(NSData *)dataIn iv:(NSData *)iv key:(NSData *)symmetricKey context:(CCOperation)encryptOrDecrypt { CCCryptorStatus ccStatus = kCCSuccess; size_t cryptBytes = 0; // Number of bytes moved to buffer. NSMutableData *dataOut = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeAES128]; ccStatus = CCCrypt( encryptOrDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, [symmetricKey bytes], kCCKeySizeAES128, iv, dataIn.bytes, dataIn.length, dataOut.mutableBytes, dataOut.length, &cryptBytes); // error occurs here, error -4304 kCCDecodeError if (ccStatus != kCCSuccess) { // Handle error NSLog(@"CCCrypt status: %d", ccStatus); } dataOut.length = cryptBytes; return dataOut; }
发生错误,错误代码-4304
是kCCDecodeError
因为ccStatus
不等于kCCSuccess
。
我觉得关键和四是没有被正确设置为NSData对象。 OpenSSL要求key和iv是hex值,我已经完成并仔细地将它们设置为128位。 但是,我认为我错过了将这些string转换为doCipher
方法的NSData的doCipher
。
任何帮助是极大的赞赏! 一整天都在玩这个。
而iv是处理不当,这是最less的问题。
解码错误听起来像不正确的参数长度,因为任何随机的iv,键和数据应该是有效的input。 (我的妻子同意,她专业地做这个东西。)把它们转换成NSData之后,检查键和数据长度。 请注意,传递带有不正确或不兼容填充的encryption数据也会导致解码错误。
写一个testingBase64,你的iOS代码vs openssl。
从简单的testing开始解决问题。
例如,删除base64,直到获得encryption顶级工作。 尝试简单的数据,比如说一个块长度为0的填充可能是一个问题。 尝试一个更简单的键,如全0。 您可以在Macterminal命令行上使用OPENSSL。
一旦基本encryption正在工作,则需要添加所需的function。
对于从命令行使用input和输出文件的openssl,他们将处理二进制文件,所以至less在初始阶段你不会有这个障碍。 这是一个示例:
(file_orig.txt contains: "1234567890123456") openssl enc -e -aes-128-cbc -K 00ff349830193845af43984758690213 -p -iv 0 -nosalt -in file_orig.txt -out file_aes.txt
打印出它生成的密钥以及它使用的iv:
key=00ff349830193845af43984758690213 iv =00000000000000000000000000000000
然后你可以在你的iOS方法中读取相同的数据文件。
这是一个使用openssl创build的文件的iOS方法:
(把键openssl输出到文件key-hex-openssl.txt中)
NSData *keyHexData = [@"00ff349830193845af43984758690213" dataUsingEncoding:NSUTF8StringEncoding]; NSData *testData = [NSData dataWithContentsOfFile:@"yourDirectoryPath/file_aes.txt"]; NSData *clearData = [NSData dataWithContentsOfFile:@"yourDirectoryPath/file_orig.txt"]; NSLog(@"keyHexData: %@", keyHexData); NSLog(@"testData: %@", testData); NSLog(@"clearData: %@", clearData); unsigned char keyBytes[16]; unsigned char *hex = (uint8_t *)keyHexData.bytes; char byte_chars[3] = {'\0','\0','\0'}; for (int i=0; i<16; i++) { byte_chars[0] = hex[i*2]; byte_chars[1] = hex[(i*2)+1]; keyBytes[i] = strtol(byte_chars, NULL, 16); } NSData *keyData = [NSData dataWithBytes:keyBytes length:16]; NSLog(@"keyData: %@", keyData); NSData *ivData = [NSData dataWithBytes:(char []){0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} length:16]; NSLog(@"ivData: %@", ivData); CCCryptorStatus ccStatus = kCCSuccess; size_t cryptBytes = 0; // Number of bytes moved to buffer. NSMutableData *clearOut = [NSMutableData dataWithLength:testData.length]; ccStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, keyData.bytes, kCCKeySizeAES128, ivData.bytes, testData.bytes, testData.length, clearOut.mutableBytes, clearOut.length, &cryptBytes); if (ccStatus != kCCSuccess) { NSLog(@"CCCrypt status: %d", ccStatus); } clearOut.length = cryptBytes; NSLog(@"clearOut: %@", clearOut); keyHexData: <41393641 34344436 31343245 43463546 33444339 30303038 46453941 34383838> testData: <86a8b306 0f33db02 01e77e66 af5bcb3a> clearData: <31323334 35363738 39303132 33343536> keyData: <a96a44d6 142ecf5f 3dc90008 fe9a4888> ivData: <00000000 00000000 00000000 00000000> clearOut: <31323334 35363738 39303132 33343536>
请注意,clearData已恢复为clearOut
这演示用openssl进行encryption并用CommonCrypto进行解密。
需要克服的问题:
1)需要添加Base64
这是完成所需encryption的起点。
当函数需要原始字节时,您提供了CCCrypt密钥和IV的UTF8表示。 如果以stringforms存储密钥和IV,其中string是字节的hex表示,则需要将此hex数字string转换回原始字节。
例如,让我们把你的IV的所有零字节。
你提供了IV "00000000000000000000000000000000"
OpenSSL。 OpenSSL接受该string并将每两个hex数字转换为它们各自的字节,然后出现这16个字节:
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (This is an array of 16 zero bytes)
但是,您可以使用IVstring并获取它的UTF8表示forms。
// fyi, I plan to replace this later with a random iv NSData *ivData = [@"00000000000000000000000000000000" dataUsingEncoding:NSUTF8StringEncoding];
结果是这些字节:
30 30 30 30 30 30 30 30 ...
因为UTF8中的字符'0'
是0x30
。 所以把你的hex表示转换成字节,而不是UTF8字符,你的密钥和IV将匹配OpenSSL的。
OpenSSL有一个名为set_hex的函数, 它将string转换为字节(C中的数据保存在unsigned char
数组中)。
#define BIO_printf fprintf #define bio_err stderr int set_hex(char *in, unsigned char *out, int size) { int i,n; unsigned char j; n=strlen(in); if (n > (size*2)) { BIO_printf(bio_err,"hex string is too long\n"); return(0); } memset(out,0,size); for (i=0; i<n; i++) { j=(unsigned char)*in; *(in++)='\0'; if (j == 0) break; if ((j >= '0') && (j <= '9')) j-='0'; else if ((j >= 'A') && (j <= 'F')) j=j-'A'+10; else if ((j >= 'a') && (j <= 'f')) j=j-'a'+10; else { BIO_printf(bio_err,"non-hex digit\n"); return(0); } if (i&1) out[i/2]|=j; else out[i/2]=(j<<4); } return(1); }
例如,
char iv_str[] = "12345678901234567890123456789012"; unsigned char iv[16]; if( !set_hex(iv_str, iv, sizeof(iv)) ) { // Handle error where string was not a well-formed IV } printf("IV: "); for(int i=0;i<sizeof(iv); ++i) { printf("%02x", iv[i]); } printf("\n");
对我来说,你的00ff349830193845af43984758690213
看起来更像hex数字比Base64。 事实上,有16个字节的hex值,这是一个很好的select,这是四进制表示的hex,而不是Base64。