对我上一个问题的后续跟进: How do I get the initialization vector (iv) from OpenSSL encrypted data
我正在使用OpenSSL
命令行实用程序来加密字符串,然后尝试使用<CommonCrypto/CommonCryptor.h>
来解密iPhone上的字符串。使用Dropbox SDK,将带有加密字符串的xml文件加载到iPhone上,我的应用程序尝试解析和解密此文件中的字符串。
以下是openssl命令的示例:
printf %s "Hello" | openssl enc -aes-128-cbc -K 00ff349830193845af43984758690213 -iv 0 -base64
上面的base 64字符串放在一个XML文件中,然后由app解析。
我正在使用Matt Gallagher的NSData来解码base64文本。我假设它正常工作;我还没有找到一个测试它的好方法。 (来源:http://cocoawithlove.com/2009/06/base64-encoding-options-on-mac-and.html)。
这是解码加密字符串的方法
在这种情况下,关键是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];
}
以下是执行实际解密的方法:(此方法的信用转到fellow stackoverflow user。)
+ (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
。
我觉得键和iv没有正确设置为NSData对象。 OpenSSL要求key和iv为十六进制值,我已经完成并小心地将它们设置为128位。但是,我认为我在将这些字符串转换为doCipher
方法的NSData时遗漏了一些东西。
非常感谢任何帮助!一整天都在玩弄这一天。
答案 0 :(得分:8)
虽然iv处理不正确,但问题最少。
解码错误听起来像是不正确的参数长度,因为任何随机的iv,key和data应该是有效的输入。 (我的妻子同意并且她专业地做这件事。)在将它们转换为NSData之后检查密钥和数据长度等内容。请注意,使用不正确或不兼容的填充传递加密数据也会导致解码错误。
为Base64编写测试,你的iOS代码与openssl。
从更简单的测试中解决问题。
例如,删除base64,直到获得加密顶级工作。尝试简单数据,比如一个块长度为0,填充可能是个问题。尝试更简单的密钥,例如全0。您可以在Mac终端命令行上使用OPENSSL。
基本加密工作完成后,将添加回所需的功能。
对于来自命令行的openssl使用输入和输出文件,它们将处理二进制文件,因此您至少最初不会遇到这个障碍。这是一个示例:
(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创建的文件的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进行加密并使用CommonCrypto进行解密。
需要克服的问题:
1)需要添加Base64
这是完成所需加密的起点。
答案 1 :(得分:4)
当函数需要原始字节时,您正在为CCCrypt提供密钥和IV的UTF8表示。如果以字符串形式存储密钥和IV,其中字符串是字节的十六进制表示,则需要将此十六进制数字字符串转换回原始字节。
例如,让我们将所有零字节的IV。
您为OpenSSL提供了IV "00000000000000000000000000000000"
。 OpenSSL接受该字符串并将每两个十六进制数字转换为它们各自的字节,并提供这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)
然而,您接受IV字符串并获取它的UTF8表示。
// 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
。因此,将十六进制表示转换为字节,而不是UTF8字符,您的密钥和IV将与OpenSSL匹配。
OpenSSL有一个名为set_hex的函数,它执行从字符串到字节的转换(在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");
答案 2 :(得分:2)
对我而言,00ff349830193845af43984758690213
看起来更像十六进制数字而不是Base64。事实上,有16个字节的十六进制使得这是一个很好的选择,这是以十六进制表示的IV,而不是在Base64中。