You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中SLIP-0010格式P256公钥解码及SecKey转换问题

P256公钥转换「参数大小错误(incorrectParameterSize)」问题解决

已实现基于HMAC-Sha256的密钥派生,可成功将SLIP-0010格式私钥转换为P256.Signing.PrivateKey并生成SecKey。但处理33字节SLIP-0010公钥(示例:02c9e16154474b3ed5b38218bb0463e008f89ee03e62d22fdcc8014beab25b48fa)时,转换为P256.Signing.PublicKey再生成SecKey触发参数大小错误(incorrectParameterSize),需解决公钥解码及转换问题,该子密钥用于密钥交换、签名和加密消息。


当前私钥处理流程

  1. 通过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
   }
}
  1. 初始化P256.Signing.PrivateKey
let key = try P256.Signing.PrivateKey.init(rawRepresentation: hexData) 
  1. 生成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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 21:05:25