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