如何将此加密函数从 Swift 转换为 Objective-C?

问题描述

我正在为 iOS Objective-C 应用程序编写一个辅助函数,该函数应该使用 AES-128 算法使用提供的密钥加密字符串。

我已经在 Swift 中实现了这个函数,但我正在努力翻译Objective-C。该函数不考虑任何初始化向量,它被硬编码为空字符串:这是有意的。

这是按预期工作的 Swift 代码

@objc func AES128(_ string: String,withKey: String) -> String {
    let data = string.data(using: String.Encoding.utf8)!
    let keyData = withKey.data(using: String.Encoding.utf8)!
    let ivData = "".data(using: String.Encoding.utf8)!
    
    let cryptLength = size_t(data.count + kCCBlockSizeAES128)
    var cryptData = Data(count: cryptLength)
    let keyLength = size_t(kCCKeySizeAES128)
    let options = Ccoptions(kCcoptionPKCS7Padding)
    var numBytesEncrypted: size_t = 0

    let cryptStatus = cryptData.withUnsafeMutableBytes { cryptBytes in
        data.withUnsafeBytes { dataBytes in
            ivData.withUnsafeBytes { ivBytes in
                keyData.withUnsafeBytes { keyBytes in
                    CCCrypt(Ccoperation(kCCEncrypt),CCAlgorithm(kCCAlgorithmAES),options,keyBytes,keyLength,ivBytes,dataBytes,data.count,cryptBytes,cryptLength,&numBytesEncrypted)
                }
            }
        }
    }

    if UInt32(cryptStatus) == UInt32(kCCSuccess) {
        cryptData.removeSubrange(numBytesEncrypted..<cryptData.count)
    }

    return cryptData.base64EncodedString();
}

这个函数就像一个魅力,用密钥 Hello World 加密字符串 ABCDEFGHIJKLMnop 会产生 3G4OU62dM9zXhkbXy8pmuA== 的 base64 编码字符串。

我将其翻译成Objective-C

+(Nsstring *)AES128:(Nsstring *)string withKey:(Nsstring *)key {
    NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
    NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding];
    NSData *ivData = [@"" dataUsingEncoding:NSUTF8StringEncoding];
    
    size_t cryptLength  = data.length + kCCBlockSizeAES128;
    NSMutableData *cryptData = [NSMutableData dataWithLength:cryptLength];
    size_t keyLength = kCCKeySizeAES128;
    Ccoptions options = kCcoptionPKCS7Padding;
    size_t numBytesEncrypted = 0;
    
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,kCCAlgorithmAES,keyData.bytes,ivData.bytes,data.bytes,data.length,cryptData.mutableBytes,&numBytesEncrypted);
    
    if (cryptStatus == kCCSuccess) {
        [cryptData replaceBytesInRange:NSMakeRange(numBytesEncrypted,cryptData.length - numBytesEncrypted) withBytes:NULL length:0];
    }

    return [cryptData base64EncodedStringWithOptions:0];
}

问题是 Objective-C 版本提供了错误的结果,我会说是随机结果,产生 Hello World + ABCDEFGHIJKLMnop 不同加密值的相同组合。 7m+HH5NusyA1VAfZ78KYCw==NY5p8XtYLoAE/4VbCCrPIg== 是其中一些错误值。 如果您解密这些字符串,您将分别得到 Hello WoldHello Worod

翻译时我做错了什么?

感谢您的帮助!

解决方法

这似乎是您处理初始化向量的问题。如果您注意到,CCCrypt 函数接受一个指向您的 IV 的字节缓冲区内容的指针,但您没有提供长度参数。这意味着缓冲区的长度是已知的。

根据维基百科 (https://en.wikipedia.org/wiki/Initialization_vector),IV 的长度“通常是密码的块大小”。

所以基本上,当你在做

NSData *ivData = [@"" dataUsingEncoding:NSUTF8StringEncoding];

ivData.bytes 属性现在将指向某个地方,在最好的情况下,它的全长为 0x00,但很可能不会。 CCCrypt() 函数将从您传递的内存地址(指向空数组的指针)开始,并使用它在那里找到的任何随机数据继续读取下一个 [IV 长度] 字节。

要解决此问题,您需要以某种方式确保您将指针传递到包含所有零的预期内容的缓冲区。

我只是用

void* ivBuffer = calloc(1,kCCBlockSizeAES128);

它分配一个 128 位(16 字节)的单个块并用全零初始化它。

你对 CCCrypt() 的调用就变成了

CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,kCCAlgorithmAES,options,keyData.bytes,keyLength,ivBuffer,data.bytes,data.length,cryptData.mutableBytes,cryptLength,&numBytesEncrypted);

并且您只需要记住调用 free(ivBuffer) 来释放为此缓冲区分配的内存。

另外,当你处理成功状态时,你可以这样做

if (cryptStatus == kCCSuccess)
{
    cryptData.length = numBytesEncrypted;
}

它会将数据截断为您想要使用的部分。由您决定,因为结果与您已有的结果相同。