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

iOS中使用ECDSA-SHA384算法验证JWT签名失败求助

iOS ES384 JWT签名验证失败的解决方案

1. 确认公钥导入的核心参数

虽然你已经解决了SecKey创建问题,但需再次确认导入公钥时的属性配置:

  • 必须明确指定P-384曲线,不能依赖系统默认值:
    let keyAttributes: [CFString: Any] = [
        kSecAttrKeyType: kSecAttrKeyTypeEC,
        kSecAttrKeyClass: kSecAttrKeyClassPublic,
        kSecAttrKeySizeInBits: 384,
        kSecAttrKeyCurve: kSecP384KeyCurve // 关键参数,缺失会导致算法不匹配
    ]
    
  • 确保公钥是SPKI或PKCS#8格式的DER/PEM,避免使用PKCS#1格式(EC公钥不支持该格式)。PEM格式需先去掉头尾部标记、删除换行,再转成Data导入。

2. 修复JWT签名的格式不兼容问题

JWT的ES384签名是r+s原始拼接格式(各48字节,共96字节),但iOS的SecKeyVerifySignature要求的是ASN.1 DER编码格式,这是ccerr -7错误的核心原因,需做格式转换:

func convertJWTSignatureToASN1(_ rawSignature: Data) -> Data? {
    guard rawSignature.count == 96 else { return nil }
    let rData = rawSignature[0..<48]
    let sData = rawSignature[48..<96]
    
    // 编码单个整数为ASN.1格式
    func encodeInteger(_ data: Data) -> Data {
        var intData = data
        // 移除前导零,避免无效编码
        while intData.first == 0x00 {
            intData.removeFirst()
        }
        // 若最高位为1,添加0x00前缀防止被解析为负数
        if (intData.first ?? 0) & 0x80 != 0 {
            intData.insert(0x00, at: 0)
        }
        var encoded = Data()
        encoded.append(0x02) // INTEGER标签
        encoded.append(UInt8(intData.count)) // 长度
        encoded.append(intData)
        return encoded
    }
    
    let rEncoded = encodeInteger(rData)
    let sEncoded = encodeInteger(sData)
    
    // 组装SEQUENCE结构
    var asn1Data = Data()
    asn1Data.append(0x30) // SEQUENCE标签
    asn1Data.append(UInt8(rEncoded.count + sEncoded.count)) // 内容长度
    asn1Data.append(rEncoded)
    asn1Data.append(sEncoded)
    
    return asn1Data
}

验证时使用转换后的ASN.1格式签名,而非JWT直接解码的原始数据。

3. 确保签名验证的算法匹配

调用SecKeyVerifySignature时,必须指定ES384对应的算法标识符:

let verifyAlgorithm = SecKeyAlgorithm.ecDSASignatureDigestSHA384
// 先校验算法是否被公钥支持
guard SecKeyIsAlgorithmSupported(publicKey, .verify, verifyAlgorithm) else {
    print("algorithm not supported by the key")
    return false
}

// 准备待验证的数据:JWT的header.payload部分的UTF8原始数据
let jwtSigningInput = "\(headerBase64Url).\(payloadBase64Url)".data(using: .utf8)!
// 转换签名格式
guard let convertedSignature = convertJWTSignatureToASN1(rawSignatureData) else {
    return false
}

// 执行验证
let verifyResult = SecKeyVerifySignature(
    publicKey,
    verifyAlgorithm,
    jwtSigningInput as CFData,
    convertedSignature as CFData,
    nil
)
return verifyResult == errSecSuccess

4. 排查jwt.io测试数据的问题

  • 生成测试JWT时,确保jwt.io的Algorithm选择ES384,密钥对是P-384曲线生成的,不要混用P-256等其他曲线的密钥。
  • 复制公钥时,完整复制PEM格式的所有内容,包括头尾标记,避免遗漏字符导致导入的公钥无效。

内容的提问来源于stack exchange,提问作者Stefo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 00:21:26