Swift中SLIP-0010格式P256公钥解码及SecKey转换问题
P256公钥转换「参数大小错误(incorrectParameterSize)」问题解决
已实现基于HMAC-Sha256的密钥派生,可成功将SLIP-0010格式私钥转换为P256.Signing.PrivateKey并生成SecKey。但处理33字节SLIP-0010公钥(示例:02c9e16154474b3ed5b38218bb0463e008f89ee03e62d22fdcc8014beab25b48fa)时,转换为P256.Signing.PublicKey再生成SecKey触发参数大小错误(incorrectParameterSize),需解决公钥解码及转换问题,该子密钥用于密钥交换、签名和加密消息。
当前私钥处理流程
- 通过
hexData扩展将十六进制字符串转为32字节Data
extension String { var hexData: Data? { var data = Data(capacity: count / 2) let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive) regex.enumerateMatches(in: self, range: NSRange(startIndex..., in: self)) { match, _, _ in let byteString = (self as NSString).substring(with: match!.range) let num = UInt8(byteString, radix: 16)! data.append(num) } guard data.count > 0 else { return nil } return data } }
- 初始化
P256.Signing.PrivateKey
let key = try P256.Signing.PrivateKey.init(rawRepresentation: hexData)
- 生成
SecKey
let attributes = [ kSecAttrKeyType: kSecAttrKeyTypeEC, kSecAttrKeyClass: kSecAttrKeyClassPrivate, kSecAttrKeySizeInBits: 256, kSecAttrIsPermanent: false ] as [CFString: Any] var error: Unmanaged<CFError>? = nil let key = SecKeyCreateWithData(curveKeyData.x963Representation as CFData, attributes as CFDictionary, &error)
报错的公钥处理代码
// hexData - 33字节公钥 do { let key = try P256.Signing.PublicKey.init(rawRepresentation: hexData) let attributes = [ kSecAttrKeyType: kSecAttrKeyTypeEC, kSecAttrKeyClass: kSecAttrKeyClassPublic, kSecAttrKeySizeInBits: 256, kSecAttrIsPermanent: false ] as [CFString: Any] let b = SecKeyCreateWithData(key.x963Representation as CFData, attributes as CFDictionary, nil)! } catch { print(error) }
问题根源
SLIP-0010的33字节公钥属于压缩格式(前缀02/03+32字节X坐标),而P256.Signing.PublicKey.init(rawRepresentation:)默认要求的是65字节未压缩格式(前缀04+32字节X+32字节Y,符合X9.63标准),格式不匹配导致参数大小错误。
解决方案
方案一:直接用压缩格式初始化(iOS15+/macOS12+)
苹果在iOS15及以上版本的CryptoKit中提供了压缩格式公钥的初始化方法,替换rawRepresentation为compressedRepresentation即可:
// hexData - 33字节压缩格式公钥 do { let key = try P256.Signing.PublicKey(compressedRepresentation: hexData) let attributes = [ kSecAttrKeyType: kSecAttrKeyTypeEC, kSecAttrKeyClass: kSecAttrKeyClassPublic, kSecAttrKeySizeInBits: 256, kSecAttrIsPermanent: false ] as [CFString: Any] var error: Unmanaged<CFError>? = nil guard let secKey = SecKeyCreateWithData(key.x963Representation as CFData, attributes as CFDictionary, &error) else { if let err = error?.takeRetainedValue() { print("生成SecKey失败:\(err)") } return } // 后续使用secKey } catch { print("公钥初始化失败:\(error)") }
方案二:手动转换压缩公钥为未压缩格式(兼容旧系统)
若需支持iOS14及以下,需自行实现压缩公钥转未压缩格式的逻辑:
import Security extension Data { func uncompressedP256PublicKey() -> Data? { guard count == 33 else { return nil } let prefix = self[0] guard prefix == 0x02 || prefix == 0x03 else { return nil } // 提取X坐标数据 let xData = self[1...32] var xBytes = [UInt8](repeating: 0, count: 32) xData.copyBytes(to: &xBytes, count: 32) // 基于P256曲线计算Y坐标 var yBytes = [UInt8](repeating: 0, count: 32) let result = secp256r1_ec_Y_from_X(&yBytes, xBytes, prefix == 0x03) guard result == 1 else { return nil } // 拼接未压缩格式:04 + X + Y var uncompressedData = Data() uncompressedData.append(0x04) uncompressedData.append(xData) uncompressedData.append(yBytes) return uncompressedData } }
使用示例:
// hexData - 33字节压缩格式公钥 do { guard let uncompressedData = hexData.uncompressedP256PublicKey() else { print("压缩公钥转未压缩格式失败") return } let key = try P256.Signing.PublicKey(rawRepresentation: uncompressedData) let attributes = [ kSecAttrKeyType: kSecAttrKeyTypeEC, kSecAttrKeyClass: kSecAttrKeyClassPublic, kSecAttrKeySizeInBits: 256, kSecAttrIsPermanent: false ] as [CFString: Any] var error: Unmanaged<CFError>? = nil guard let secKey = SecKeyCreateWithData(key.x963Representation as CFData, attributes as CFDictionary, &error) else { if let err = error?.takeRetainedValue() { print("生成SecKey失败:\(err)") } return } // 后续使用secKey } catch { print("公钥初始化失败:\(error)") }
内容的提问来源于stack exchange,提问作者Alina
相关产品推荐
相关产品推荐

