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
相关产品推荐
相关产品推荐

