iOS 关于使用ECDH算法遇到的几个问题

最近在项目中网络请求项目需要用到ECDH算法(椭圆曲线选择P-384),计算出协商秘钥后导出密码使用HKDF进行密钥扩展,这里将算法遇到的几个问题记录下来

1、生成公钥和私钥

iOS 10.0之后有个类SecKey,专门用于生成公钥和私钥。生成代码如下图


var error: Unmanaged?
        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的互相转换

使用ECDH加密,必然涉及到服务端传给APP公钥,APP传给服务端公钥,就这需要密钥字符串和SecKey的互相转换。在这里就不能不说到一个

SecKey的一个坑:在将密钥字符串转化为SecKey时,会自动的去掉密钥的ASN.1,所以在转化时得先加上


// MARK: -
public extension SecKey {
    func publicSeckeyToString() -> String? {
        var error:Unmanaged?
        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
    }
   
    
    // 根据公钥字符串生成seckey
    class func initPubkeyString(pubkeyString: String) -> SecKey {
        let pubKeyByte:Array = Data.init(base64Encoded: pubkeyString)!.bytes
        let asn1Byte:Array = SecKey.getPKCS1SHA384ASN1()
        let array = pubKeyByte[asn1Byte.count...]
        
        var error: Unmanaged?
        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 {
        return [
            0x30, 0x76,
            0x30, 0x10,
            0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05,
            0x2b, 0x81, 0x04,
            0x00, 0x22, 0x03, 0x62, 0x00
        ]
    }

}

这里我偷了懒,没有继续翻苹果的文档看有没有关于asn.1的类,我是直接用的ASN.1 JavaScript decoder 这个网址去查看了我这所需要的P-384曲线的ASN.1

3、之后就是计算出协商秘钥了,这里代码很简单


let exchangeData = exchangeKey(privateKey: privateKey, pubkey: pubkey)!

后面用HKDF算法对协商密钥扩展我这用的第三方库CryptoSwift,代码


let key = try! HKDF(password: [UInt8](exchangeData), variant: .sha256).calculate()

你可能感兴趣的:(iOS 关于使用ECDH算法遇到的几个问题)