最近在項(xiàng)目中網(wǎng)絡(luò)請求項(xiàng)目需要用到ECDH算法(橢圓曲線選擇P-384),計(jì)算出協(xié)商秘鑰后導(dǎo)出密碼使用HKDF進(jìn)行密鑰擴(kuò)展犁享,這里將算法遇到的幾個問題記錄下來
1乐设、生成公鑰和私鑰
iOS 10.0之后有個類SecKey晨缴,專門用于生成公鑰和私鑰。生成代碼如下圖
var error: Unmanaged<CFError>?
let attributes: [String: Any] = [kSecAttrKeySizeInBits as String: 384,
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
kSecPrivateKeyAttrs as String: [kSecAttrIsPermanent as String: false],
kSecPublicKeyAttrs as String:[kSecAttrIsPermanent as String: false]]
self.privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error)
if let privateKey = self.privateKey {
publicKey = SecKeyCopyPublicKey(privateKey)
}
其中384表示的是 P-384橢圓曲線两残。另外還有P-224, P-256, P-521這些橢圓曲線
2、密鑰字符串和SecKey的互相轉(zhuǎn)換
使用ECDH加密把跨,必然涉及到服務(wù)端傳給APP公鑰磕昼,APP傳給服務(wù)端公鑰,就這需要密鑰字符串和SecKey的互相轉(zhuǎn)換节猿。在這里就不能不說到一個
SecKey的一個坑:在將密鑰字符串轉(zhuǎn)化為SecKey時票从,會自動的去掉密鑰的ASN.1,所以在轉(zhuǎn)化時得先加上
// MARK: -
public extension SecKey {
func publicSeckeyToString() -> String? {
var error:Unmanaged<CFError>?
if let cfdata = SecKeyCopyExternalRepresentation(self, &error) {
// 添加secp384r1的asn.1
let pemPrefixBuffer :[UInt8] = SecKey.getPKCS1SHA384ASN1()
var finalPemData = Data(bytes: pemPrefixBuffer as [UInt8], count: pemPrefixBuffer.count)
finalPemData.append(cfdata as Data)
let finalPemString = finalPemData.base64EncodedString(options: .lineLength64Characters)
return finalPemString
}
return nil
}
// 根據(jù)公鑰字符串生成seckey
class func initPubkeyString(pubkeyString: String) -> SecKey {
let pubKeyByte:Array<UInt8> = Data.init(base64Encoded: pubkeyString)!.bytes
let asn1Byte:Array<UInt8> = SecKey.getPKCS1SHA384ASN1()
let array = pubKeyByte[asn1Byte.count...]
var error: Unmanaged<CFError>?
let attributes = [kSecAttrKeySizeInBits as String: 384,
kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeyClass: kSecAttrKeyClassPublic] as NSDictionary
let pubkey = SecKeyCreateWithData(Data(array) as NSData, attributes, &error)
return pubkey!
}
class func getPKCS1SHA384ASN1() -> Array<UInt8> {
return [
0x30, 0x76,
0x30, 0x10,
0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05,
0x2b, 0x81, 0x04,
0x00, 0x22, 0x03, 0x62, 0x00
]
}
}
這里我偷了懶滨嘱,沒有繼續(xù)翻蘋果的文檔看有沒有關(guān)于asn.1的類峰鄙,我是直接用的ASN.1 JavaScript decoder 這個網(wǎng)址去查看了我這所需要的P-384曲線的ASN.1
3、之后就是計(jì)算出協(xié)商秘鑰了太雨,這里代碼很簡單
let exchangeData = exchangeKey(privateKey: privateKey, pubkey: pubkey)!
后面用HKDF算法對協(xié)商密鑰擴(kuò)展我這用的第三方庫CryptoSwift吟榴,代碼
let key = try! HKDF(password: [UInt8](exchangeData), variant: .sha256).calculate()