Swift调用Google API服务账号认证报Invalid JWT signature错误如何解决
错误原因
- 签名算法不匹配:JWT Header中声明的签名算法为
RS256(即SHA256WithRSA,非对称签名算法,需要使用RSA私钥进行签名),但代码中使用了HMAC<SHA256>(对称消息认证码算法),二者完全不兼容,导致Google端校验签名失败。 - 私钥处理逻辑错误:直接将带PEM格式头尾的私钥字符串转为UTF8数据作为对称密钥使用,没有对PEM格式的私钥做解码处理,没有提取出真正的RSA私钥二进制数据。
- 请求参数格式不符合规范:将
grant_type和assertion参数拼接在URL查询串中,但请求头声明了application/x-www-form-urlencoded格式,这类参数应该放在POST请求的body体中,不符合规范的传参也可能导致校验失败。
修复方案
- 首先处理PEM格式的RSA私钥,去除头尾标识、换行符后做Base64解码,得到原始RSA私钥数据,再通过Security框架生成RSA私钥对象。
- 替换HMAC签名逻辑为RSA-SHA256签名逻辑,使用生成的RSA私钥对JWT header.payload串做签名。
- 调整请求参数的传递方式,将参数放入POST请求的httpBody中。
修复后参考代码如下:
import Security import Foundation extension Data { func urlSafeBase64EncodedString() -> String { return base64EncodedString() .replacingOccurrences(of: "+", with: "-") .replacingOccurrences(of: "/", with: "_") .replacingOccurrences(of: "=", with: "") } } struct Header: Encodable { let alg = "RS256" let typ = "JWT" } struct Payload: Encodable { let iss = "content-uploader@project-id.iam.gserviceaccount.com" let scope = "https://www.googleapis.com/auth/devstorage.full_control" let aud = "https://oauth2.googleapis.com/token" let iat = Date().timeIntervalSince1970 let exp = Date().timeIntervalSince1970 + 3600 // JWT最大有效期为1小时 } // 处理PEM私钥 let pemKey = "-----BEGIN PRIVATE KEY-----你的私钥内容\n-----END PRIVATE KEY-----\n" let cleanedKey = pemKey .replacingOccurrences(of: "-----BEGIN PRIVATE KEY-----", with: "") .replacingOccurrences(of: "-----END PRIVATE KEY-----", with: "") .replacingOccurrences(of: "\n", with: "") .trimmingCharacters(in: .whitespacesAndNewlines) guard let keyData = Data(base64Encoded: cleanedKey) else { fatalError("私钥解码失败") } // 生成RSA私钥对象 let attributes: [CFString: Any] = [ kSecAttrKeyType: kSecAttrKeyTypeRSA, kSecAttrKeyClass: kSecAttrKeyClassPrivate, kSecAttrKeySizeInBits: 2048 ] var error: Unmanaged<CFError>? guard let privateKey = SecKeyCreateWithData(keyData as CFData, attributes as CFDictionary, &error) else { fatalError("RSA私钥创建失败: \(error!.takeRetainedValue() as Error)") } // 生成JWT header和payload let headerJSONData = try! JSONEncoder().encode(Header()) let headerBase64 = headerJSONData.urlSafeBase64EncodedString() let payloadJSONData = try! JSONEncoder().encode(Payload()) let payloadBase64 = payloadJSONData.urlSafeBase64EncodedString() let toSign = "\(headerBase64).\(payloadBase64)".data(using: .utf8)! // RSA-SHA256签名 guard let signature = SecKeyCreateSignature(privateKey, .rsaSignatureMessagePKCS1v15SHA256, toSign as CFData, &error) as Data?, error == nil else { fatalError("签名失败: \(error!.takeRetainedValue() as Error)") } let signatureBase64 = signature.urlSafeBase64EncodedString() let jwt = "\(headerBase64).\(payloadBase64).\(signatureBase64)"
修复后请求代码如下:
guard let url = URL(string: "https://oauth2.googleapis.com/token") else { fatalError("URL无效") } var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") // 参数放入body中 let params = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=\(jwt)" request.httpBody = params.data(using: .utf8) let task = URLSession.shared.dataTask(with: request) { data, response, error in if let data = data, let result = String(data: data, encoding: .utf8) { print("返回结果: \(result)") } } task.resume()
内容的提问来源于stack exchange,提问作者David Reese
相关产品推荐
相关产品推荐

