在iOS上使用AES-256加密与openssl不产生相同的结果

4
我已经花了几个小时看这个问题,我正在拼命地尝试使用AES-256加密iOS上的一小段文本,然后可以通过openssl进行解密。
看起来很简单?不是的。
我找到的iOS代码与openssl的密钥和IV不兼容,因此我不得不对其进行调整,但它显然没有起作用。
所以这就是我使用的加密代码...传入要加密的字符串(dataString),一个字符串密钥(key)和一个字符串初始化向量(iv)...
- (NSData *)AES256Encrypt:(NSString *)dataString WithKey:(NSString *)key iv:(NSString *)iv {

    // 'key' should be 32 bytes for AES256, will be null-padded otherwise
    //char keyPtr[kCCKeySizeAES256+1]; // room for terminator (unused)
    //bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)

    // fetch key data
    //[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];

    //NSLog(@"keyPtr: '%s'", keyPtr);

    NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
    NSLog(@"keyPtr: '%s'", keyData.bytes);
    NSData *dataToEncrypt = [dataString dataUsingEncoding:NSUTF8StringEncoding];
    NSData *ivData = [iv dataUsingEncoding:NSUTF8StringEncoding];

    NSUInteger dataLength = [dataToEncrypt length];

    //See the doc: For block ciphers, the output size will always be less than or 
    //equal to the input size plus the size of one block.
    //That's why we need to add the size of one block here
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);

    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                          keyData.bytes, kCCKeySizeAES256,
                                          ivData.bytes, // initialisation vector
                                          dataToEncrypt.bytes, 
                                          dataToEncrypt.length, /* input */
                                          buffer, bufferSize, /* output */
                                          &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        //the returned NSData takes ownership of the buffer and will free it on deallocation
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }

    free(buffer); //free the buffer;
    return nil;
}

对于相同的字符串进行编码,使用此方法与使用相同的密钥和向量在openssl中不会产生相同的值...例如,以下命令行:

openssl enc -aes-256-cbc -e -in secrets.txt -a -iv 0000 -K 0000 -p

secrets.txt 是一个文本文件,包含需要加密的字符串。

这将输出类似于以下内容:

salt=3C66000000000000
key=0000000000000000000000000000000000000000000000000000000000000000
iv =00000000000000000000000000000000
qTMfgtAxbF8Yyh27ZDrcIQ==

解密时,执行相反的操作(假设上述加密的最后一行数据在test.secrets.out中)

openssl enc -aes-256-cbc -d -in test.secrets.out -a -iv 0000 -K 0000 -p 
salt=3C66000000000000
key=0000000000000000000000000000000000000000000000000000000000000000
iv =00000000000000000000000000000000
< text of the secrets.txt file >

现在,如果我使用4个字符的密钥和iv,这在iOS中无法正确编码。如果我使用完整长度的密钥和iv,这也无法正确编码。
基本上,这是一个检查,以确定如果我发送了一段加密数据,它是否是正确的数据。
我错过了什么?
我已经查看了一些代码,试图找到答案...

http://robnapier.net/blog/aes-commoncrypto-564

https://github.com/rnapier/RNCryptor

http://pastie.org/426530

我在这里进行了广泛搜索,但仍找不到答案。

非常感谢您的帮助。


看看能否让IOS解密它所加密的内容。 - Hot Licks
同意Hot Licks的说法,但如果他正在尝试使用iOS加速程序在本地进行加密以通过SSL连接到其他内容,那么......我注意到SSL正在使用AES的CBC模式。您确定您已经将iOS设置为CBC模式吗?AES以128位块处理块,也许值得研究一下iOS是以小端还是大端解释输入数据。这也适用于键和IV。 - trumpetlicks
还有一点需要注意,如果数据没有对齐到128位边界,则您可能还需要查看如何对最后一个128位块进行填充。 - trumpetlicks
热门提示:是的 - 我已经检查过它可以解密我加密的内容。这似乎不是问题所在...尽管在这里不需要解密数据。 - padajo
trumpetlicks:已经研究了填充和小/大端...虽然我不确定自己是否足够了解如何详细地查看这一位。该代码纯粹是为了提供一个加密数据包以及使用共享私钥的HTTP POST操作。这样,另一端可以检查数据是否被篡改。因此不需要解密或任何其他操作。 - padajo
已经将API中的AES加密移除,因为我们无法确定它使用的加密密钥,所以我们无法保证在其他任何地方进行解密或加密时会得到相同的结果。叹气 - padajo
1个回答

2
OpenSSL有一种独特的(读作“与任何标准都不接近,也不是特别安全”)将密码转换为IV和密钥的方法。如果您查看RNOpenSSLCryptor,您会看到使用的算法:
// For aes-128:
//
// key = MD5(password + salt)
// IV = MD5(Key + password + salt)

//
// For aes-256:
//
// Hash0 = ''
// Hash1 = MD5(Hash0 + Password + Salt)
// Hash2 = MD5(Hash1 + Password + Salt)
// Hash3 = MD5(Hash2 + Password + Salt)
// Hash4 = MD5(Hash3 + Password + Salt)
//
// Key = Hash1 + Hash2
// IV = Hash3 + Hash4
//

// File Format:
//
// |Salted___|<salt>|<ciphertext>|
//

使用RNOpenSSLCryptor可以使RNCryptor支持OpenSSL格式。我目前正在重构这个代码,在async分支上支持异步操作,但该分支尚不支持OpenSSL,但我计划在不久的将来进行重构(到2012年7月中旬)。
如果您想要实现自己使用的代码,请查看keyForPassword:salt:IVForKey:password:salt:
请注意,OpenSSL文件格式存在几个安全问题,如果可以避免使用,我不建议使用它。它不使用非常好的KDF生成其密钥,IV不够随机,也没有提供HMAC进行身份验证。这些安全问题是我设计不同的文件格式的原因,尽管我讨厌创建“另一个不兼容的容器”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接