无法解密通过iPhone上的OpenSSL编码的数据

对我之前的问题的一种跟进:如何从OpenSSL加密数据获得初始化向量(iv)

我使用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文件中,然后由应用程序解析。

我正在使用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];
}

下面是实际解密的方法:(这个方法的信用转到一个同伴的stackoverflow用户。)

+ (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;
}

发生错误,错误代码-4304kCCDecodeError因为ccStatus不等于kCCSuccess

我觉得key和iv没有被正确设置为NSData对象。 OpenSSL要求key和iv是十六进制值,我已经完成并仔细地将它们设置为128位。 但是,我认为我错过了将这些字符串转换为doCipher方法的NSData。

任何帮助是极大的赞赏! 一整天都在玩这个。


虽然iv处理不当,这是最少的问题。

解码错误听起来像不正确的参数长度,因为任何随机的iv,键和数据应该是有效的输入。 (我的妻子同意,并且她专业地做这些东西。)在将它们转换为NSData之后,检查关键字和数据长度等事情。 请注意,传递带有不正确或不兼容填充的加密数据也会导致解码错误。

为Base64编写一个测试,你的iOS代码vs openssl。

从简单的测试开始解决问题。

例如,删除base64,直到获得加密顶级工作。 尝试简单的数据,比如一个块的长度为0,填充可能是一个问题。 尝试一个更简单的键,比如全0。 您可以在Mac Terminal命令行上使用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] = {'','',''};
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

这是完成所需加密的起点。


当函数需要原始字节时,您提供CCCrypt密钥和IV的UTF8表示。 如果以字符串形式存储密钥和IV,其中字符串是字节的十六进制表示,则需要将此十六进制数字字符串转换回原始字节。

例如,让我们把你的IV全部零字节。

您向IV提供了OpenSSL "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 longn");
                return(0);
                }
        memset(out,0,size);
        for (i=0; i<n; i++)
                {
                j=(unsigned char)*in;
                *(in++)='';
                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 digitn");
                        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看起来更像十六进制数字比Base64。 事实上,有16个字节的十六进制值可以很好地证明这是以十六进制表示的IV,而不是Base64。

链接地址: http://www.djcxy.com/p/68329.html

上一篇: Unable to decrypt data encoded via OpenSSL on iPhone

下一篇: Encrypted NSData to NSString in obj