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

无法解密iPhone上通过OpenSSL编码的数据,iphone,ios,encryption,cryptography,openssl,Iphone,Ios,Encryption,Cryptography,Openssl,这是我上一个问题的后续问题: 我正在使用OpenSSL命令行实用程序对字符串进行加密,然后尝试在iPhone上使用对字符串进行解密。使用Dropbox SDK,将一个带有加密字符串的xml文件加载到iPhone上,我的应用程序将尝试解析和解密该文件中的字符串 以下是openssl命令的一个示例: printf %s "Hello" | openssl enc -aes-128-cbc -K 00ff349830193845af43984758690213 -iv 0 -base64 上面的b

这是我上一个问题的后续问题:

我正在使用
OpenSSL
命令行实用程序对字符串进行加密,然后尝试在iPhone上使用
对字符串进行解密。使用Dropbox SDK,将一个带有加密字符串的xml文件加载到iPhone上,我的应用程序将尝试解析和解密该文件中的字符串

以下是openssl命令的一个示例:

printf %s "Hello" | openssl enc -aes-128-cbc -K 00ff349830193845af43984758690213 -iv 0 -base64
上面的base64字符串放在一个XML文件中,然后由应用程序解析

我使用Matt Gallagher的NSData加法来解码base64文本。我假设这是正确的;我还没有找到一个好方法来测试它。(来源:)

以下是解码加密字符串的方法。
在本例中,键是一个NSString,等于“00FF349830193845AF439844758690213”

下面是进行实际解密的方法:(此方法的功劳归a。)

出现错误,错误代码
-4304
,这是
kCCDecodeError
,因为
ccStatus
不等于
KCCSucces

我觉得密钥和iv没有正确设置为NSData对象。OpenSSL要求key和iv是十六进制值,我已经这样做了,并小心地将它们精确地设置为128位。但是,我认为我在将那些字符串转换为
doCipher
方法的NSData时缺少了一些东西


非常感谢您的帮助!我整天都在玩这个

在我看来,您的
00ff349830193845af4398458690213
看起来更像十六进制数字,而不是Base64。16字节的十六进制值这一事实使我们很有可能这是用十六进制表示的IV,而不是用Base64表示的。

当IV处理不正确时,这是最小的问题

解码错误听起来像是不正确的参数长度,因为任何随机iv、键和数据都应该是有效的输入。(我妻子同意,她做这件事很专业。)在将密钥和数据转换为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=[@“00ff349830193845af439844758690213”数据使用编码:NSUTF8StringEncoding];
NSData*testData=[NSData DATA WITH CONTENTS OFFILE:@“yourDirectoryPath/file_aes.txt”];
NSData*clearData=[NSData DATA WITH CONTENTS OFFILE:@“yourDirectoryPath/file_orig.txt”];
NSLog(@“keyHexData:%@”,keyHexData);
NSLog(@“testData:%@”,testData);
NSLog(@“clearData:%@”,clearData);
无符号字符密钥字节[16];
无符号字符*十六进制=(uint8_t*)keyHexData.bytes;
字符字节_chars[3]={'\0','\0','\0'};

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

例如,让我们以所有零字节的IV为例

您为OpenSSL提供了IV
“00000000000000000000000000”
。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有一个名为的函数,用于将字符串转换为字节(在C中,字节保存在
无符号字符
数组中)

#定义BIO_printf fprintf
#定义生物错误标准
int set_hex(字符*输入,无符号字符*输出,整数大小)
{
inti,n;
无符号字符j;
n=strlen(英寸);
如果(n>(尺寸*2))
{
BIO_printf(BIO_err,“十六进制字符串太长\n”);
返回(0);
}
memset(输出,0,大小);

对于(i=0;i='0')&&(j='A')&&(j='A')&&(j是的!这正是正在发生的事情。那么我该如何将十六进制字符串转换为正确的字节呢?@galacticfury:我添加了OpenSSL使用的函数。它们的实现非常陈旧,但如果你想自己实现,算法很简单。对于字符串中的每个十六进制字符,将字符转换为十进制(0-15)。如果十六进制字符是上半字节,则将其乘以16。如果是下半字节,则将该值添加到上半字节,则得到一个完整的字节。不幸的是,此例程无法工作,它将收到一个信号“EXE_BAD_ACCESS”。在CocoaFu示例中,有五行代码可以转换密钥。@CoCoCoaFu:例程可以工作。我修复了我的错误在编辑中的“如何使用”示例,使其不再传递不可变的输入字符串(const char*)。因为OpenSSL代码在转换后会清除输入数组,以便不会在内存中留下密钥和IV的纯文本副本。因此,如果要获得EXC坏访问,例如
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  (This is an array of 16 zero bytes)
// fyi, I plan to replace this later with a random iv
NSData *ivData = [@"00000000000000000000000000000000" dataUsingEncoding:NSUTF8StringEncoding];
30 30 30 30 30 30 30 30 ...
#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");