【问题标题】:AES-256 encryption on iOS not producing same result as openssliOS 上的 AES-256 加密不会产生与 openssl 相同的结果
【发布时间】:2025-03-19 11:20:02
【问题描述】:

我已经看了好几个小时了。我正在拼命尝试让 iOS 使用 AES-256 加密来加密一小段文本,然后可以通过 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;
}

对于相同的字符串进行编码,这不会产生与使用具有相同键和 iv 的 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,但是如果他尝试使用 iOS 加速例程在本地加密以通过 SSL 连接到其他东西,那么.....我注意到 SSL 使用的是 AES 的 CBC 模式.您确定您已将 iOS 设置为 CBC 模式。 AES 以 128 位块的形式处理块,或许值得研究一下 iOS 是以小端还是大端来解释输入数据。这也适用于密钥和 IV。
  • 另外请注意,您可能还需要查看如何为最后一个 128 位块填充数据(如果数据未在 128 位边界上对齐)。
  • Hot Licks:是的 - 已经检查过它解密了我加密的内容。这似乎不是问题......虽然这里不需要解密数据。
  • trumpetlicks: 看过 padding 和 little/big endian...虽然我不确定我是否足够了解如何详细了解这一点。该代码纯粹是为了提供一个加密数据包以及一个带有共享私钥的 HTTP POST 操作。这样另一端可以检查数据是否被篡改。所以不需要解密或类似的东西。

标签: iphone ios openssl aes commoncrypto


【解决方案1】:

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。这些安全问题是我设计different file format 的原因,就像我讨厌创建“又一个不兼容的容器”一样。

【讨论】:

    最近更新 更多