iOS 是否公开用于密钥生成和使用 ECDH 派生密钥的 API?
据我所知,苹果正在内部使用它(特别是 x25519),但我没有看到它通过通用加密或其他方式公开为公共 API。
谢谢,
Z
使用 Xcode 8.3.3 在 Playground 中完成,使用 EC 为 Alice、Bob 生成私钥/公钥,然后使用 Alice 的私钥和 Bob 的公钥计算 Alice 的共享秘密,并使用 Bob 的私钥和 Alice 的公钥为 Bob 共享秘密,最后断言他们是平等的。
import Security
import UIKit
let attributes: [String: Any] =
[kSecAttrKeySizeInBits as String: 256,
kSecAttrKeyType as String: kSecAttrKeyTypeEC,
kSecPrivateKeyAttrs as String:
[kSecAttrIsPermanent as String: false]
]
var error: Unmanaged<CFError>?
if #available(iOS 10.0, *) {
// generate a key for alice
guard let privateKey1 = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
let publicKey1 = SecKeyCopyPublicKey(privateKey1)
// generate a key for bob
guard let privateKey2 = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
let publicKey2 = SecKeyCopyPublicKey(privateKey2)
let dict: [String: Any] = [:]
// alice is calculating the shared secret
guard let shared1 = SecKeyCopyKeyExchangeResult(privateKey1, SecKeyAlgorithm.ecdhKeyExchangeStandardX963SHA256, publicKey2!, dict as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
// bob is calculating the shared secret
guard let shared2 = SecKeyCopyKeyExchangeResult(privateKey2, SecKeyAlgorithm.ecdhKeyExchangeStandardX963SHA256, publicKey1!, dict as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
print(shared1==shared2)
} else {
// Fallback on earlier versions
print("unsupported")
}
感谢@Mats 为我指明了正确的方向..3
这是 swift 5 的最新代码和参数的变化。
import Security
import UIKit
var error: Unmanaged<CFError>?
let keyPairAttr:[String : Any] = [kSecAttrKeySizeInBits as String: 256,
SecKeyKeyExchangeParameter.requestedSize.rawValue as String: 32,
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecPrivateKeyAttrs as String: [kSecAttrIsPermanent as String: false],
kSecPublicKeyAttrs as String:[kSecAttrIsPermanent as String: false]]
let algorithm:SecKeyAlgorithm = SecKeyAlgorithm.ecdhKeyExchangeStandardX963SHA256//ecdhKeyExchangeStandardX963SHA256
do {
guard let privateKey = SecKeyCreateRandomKey(keyPairAttr as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
let publicKey = SecKeyCopyPublicKey(privateKey)
print("public ky1: \(String(describing: publicKey)),\n private key: \(privateKey)\n\n")
guard let privateKey2 = SecKeyCreateRandomKey(keyPairAttr as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
let publicKey2 = SecKeyCopyPublicKey(privateKey2)
print("public ky2: \(String(describing: publicKey2)),\n private key2: \(privateKey2)\n\n")
let shared:CFData? = SecKeyCopyKeyExchangeResult(privateKey, algorithm, publicKey2!, keyPairAttr as CFDictionary, &error)
let sharedData:Data = shared! as Data
print("shared Secret key: \(sharedData.hexEncodedString())\n\n")
let shared2:CFData? = SecKeyCopyKeyExchangeResult(privateKey2, algorithm, publicKey!, keyPairAttr as CFDictionary, &error)
let sharedData2:Data = shared2! as Data
print("shared Secret key 2: \(sharedData2.hexEncodedString())\n\n")
// shared secret key and shared secret key 2 should be same
} catch let error as NSError {
print("error: \(error)")
} catch {
print("unknown error")
}
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, $0) }.joined()
}
}
大约 - 2020 和 iOS13。在下面 Zohar 的代码片段中,在尝试获取共享秘密之前,还要在字典中指定密钥大小。
let dict: [String: Any] = [SecKeyKeyExchangeParameter.requestedSize.rawValue as String: 32]
否则会出现错误。
不见了kSecKeyKeyExchangeParameterRequestedSize
您可以使用 CryptoKit (iOS13+) 实现同样的效果
import CryptoKit
import Foundation
var protocolSalt = "Hello, playground".data(using: .utf8)!
// generate key pairs
let sPrivateKey = Curve25519.KeyAgreement.PrivateKey()
let sPublicKey = sPrivateKey.publicKey
let rPrivateKey = Curve25519.KeyAgreement.PrivateKey()
let rPublicKey = rPrivateKey.publicKey
// sender derives symmetric key
let sSharedSecret = try! sPrivateKey.sharedSecretFromKeyAgreement(with: rPublicKey)
let sSymmetricKey = sSharedSecret.hkdfDerivedSymmetricKey(using: SHA256.self,
salt: protocolSalt,
sharedInfo: Data(),
outputByteCount: 32)
let sSensitiveMessage = "The result of your test is positive".data(using: .utf8)!
// sender encrypts data
let encryptedData = try! ChaChaPoly.seal(sSensitiveMessage, using: sSymmetricKey).combined
// receiver derives same symmetric key
let rSharedSecret = try! rPrivateKey.sharedSecretFromKeyAgreement(with: sPublicKey)
let rSymmetricKey = rSharedSecret.hkdfDerivedSymmetricKey(using: SHA256.self,
salt: protocolSalt,
sharedInfo: Data(),
outputByteCount: 32)
// receiver decrypts data
let sealedBox = try! ChaChaPoly.SealedBox(combined: encryptedData)
let decryptedData = try! ChaChaPoly.open(sealedBox, using: rSymmetricKey)
let rSensitiveMessage = String(data: decryptedData, encoding: .utf8)!
// assertions
sSymmetricKey == rSymmetricKey
sSensitiveMessage == decryptedData