Swift CryptoKit 加密产生不一致的结果

Swift CryptoKit encryption yields inconsistent results

我有一个简单的加密例程,每次执行都会产生不同的结果。谁能找出问题所在?下面的代码仅显示了三个不同时间的相同调用,正如您所看到的,每次调用的结果都不同。

import SwiftUI
import CryptoKit

struct ContentView: View {
    @State var password = "TopSecret"
    @State var text = "My text"
    
    var body: some View {
        VStack {
            TextField("Password", text: $password)
            TextField("Text", text: $text)
            Text(try! encrypt(password: password, text: text).hexEncodedString())
            Text(try! encrypt(password: password, text: text).hexEncodedString()).foregroundColor(Color.blue)
            Text(try! encrypt(password: password, text: text).hexEncodedString())
        }
    }
}

func encrypt(password: String, text: String) throws -> Data {
    let x = [UInt8](text.utf8)
    return try encrypt(password: password, input: x)
}

func encrypt(password: String, input: [UInt8]) throws -> Data {
    let pwd = password.padding(toLength: 32, withPad: "@", startingAt: 0)
    let key = SymmetricKey(data: pwd.data(using: String.Encoding.utf8)!)
    let sealedBox = try AES.GCM.seal(input, using: key)
    return sealedBox.combined!
}

extension Data {
    struct HexEncodingOptions: OptionSet {
        let rawValue: Int
        static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
    }
    func hexEncodedString(options: HexEncodingOptions = []) -> String {
        let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
        return self.map { String(format: format, [=14=]) }.joined()
    }
}

这是 seal 方法等安全加密方案的一项功能。包含一个随机值(称为“随机数”)以确保每次加密都是唯一的。所有这些都将正确解密。

如果您需要一致的(不安全的)加密结果,例如在单元测试中,您可以通过将 nonce 参数传递给 seal.

来传递静态随机数值

请注意,您的密钥派生非常不安全。您不能只将 ASCII 字符串填充到 AES 密钥中。它破坏了键空间。对于计算机生成的密钥,您应该生成一个随机的 32 字节。对于人工生成的密码,您需要一个 PBKDF(例如 PBKDF2 或 scrypt)来扩展密钥。不幸的是,我认为 CryptoKit 没有提供任何工具来支持基于密码的加密;只有随机密钥加密。您需要使用 CommonCrypto 或 CryptoSwift 之类的工具手动执行密钥扩展,或者您需要使用支持密码的工具,例如 Themis 或 RNCryptor。