Swift中使用AES.GCM.open解密AES-256-GCM数据失败问题排查
Swift CryptoKit解密AES-256-GCM失败:共享密钥一致但报错503316581
背景说明
我需要实现一套加密流程:服务器使用客户端提供的公钥加密小数据载荷,客户端可解密该数据,且服务器无需存储任何敏感信息。采用ECDH密钥协商+AES-256-GCM加密的方案,该方案认可度较高。目前Ruby客户端、基于SubtleCrypto/WebCrypto的JavaScript客户端都能与服务器正常配合完成解密,但Swift客户端(基于CLI应用与swift-crypto库)解密时出现问题。
实现流程
- 客户端生成ECDH公钥私钥对
- 将公钥通过HTTP请求发送至服务器
- 服务器生成一次性ECDH密钥对,通过ECDH派生共享密钥,再用AES-256-GCM加密数据
- 服务器返回客户端解密所需信息:派生共享密钥用的服务器公钥、IV、密文、认证标签
问题详情
Swift客户端计算共享密钥的步骤完全正常,生成的共享密钥与服务器端完全一致,但执行AES-GCM解密操作时触发错误:
Crypto.CryptoKitError.underlyingCoreCryptoError(error: 503316581)
Swift客户端核心代码
import Foundation #if os(Linux) import FoundationNetworking import Crypto #endif extension Data { public func urlsafeBase64() -> String { return base64EncodedString() .replacingOccurrences(of: "+", with: "-") .replacingOccurrences(of: "/", with: "_") .replacingOccurrences(of: "=", with: "") } public init?(base64urlEncoded input: String) { var base64 = input base64 = base64.replacingOccurrences(of: "-", with: "+") base64 = base64.replacingOccurrences(of: "_", with: "/") while base64.count % 4 != 0 { base64 = base64.appending("=") } self.init(base64Encoded: base64) } } let ourPrivateKey = P256.KeyAgreement.PrivateKey() let ourPublicKey = ourPrivateKey.publicKey let reqData: [String: Any] = ["pem": ourPublicKey.pemRepresentation] // 省略与后端的HTTP交互逻辑,服务器返回以下内容: // iv, ciphertext, tag, 服务器公钥的PEM格式 // 响应内容已解析到`EncryptedData`结构体中 // iv、tag、ciphertext均为URL安全的Base64编码 struct EncryptedData: Codable { var ciphertext: String? var iv: String? var tag: String? var pem: String? } let serverPubKey = try! P256.KeyAgreement.PublicKey(pemRepresentation: ed.pem!) let sharedSecret = try! ourPrivateKey.sharedSecretFromKeyAgreement(with: serverPubKey) // 此处验证通过:sharedSecret与服务器生成的完全一致 // 这一步我不确定是否必要,Ruby和JS实现里没有这一步 let symmetricKey = sharedSecret.hkdfDerivedSymmetricKey( using: SHA256.self, salt: Data(), sharedInfo: Data(), outputByteCount: 32 ) // Ruby和JS里叫IV,Swift里对应nonce let nonceData = Data(base64urlEncoded: ed.iv!)! let ciphertextData = Data(base64urlEncoded: ed.ciphertext!)! let tagData = Data(base64urlEncoded: ed.tag!)! let sealedBox = try! AES.GCM.SealedBox( nonce: AES.GCM.Nonce(data: nonceData), ciphertext: ciphertextData, tag: tagData ) let decrypted = try! AES.GCM.open(sealedBox, using: symmetricKey) // 报错:Crypto.CryptoKitError.underlyingCoreCryptoError(error: 503316581)
Ruby服务器核心代码
def enc_test message = JSON.generate(ts: Time.now.to_s) user_pubkey = OpenSSL::PKey::EC.new(params.require("pem")) render(json: encrypt(message, user_pubkey)) end private ALGO = "aes-256-gcm" def encode64(data) = Base64.urlsafe_encode64(data, padding: false) def encrypt(message, user_pubkey) server_key = OpenSSL::PKey::EC.generate("prime256v1") derived_key = server_key.derive(user_pubkey) ciphertext, tag, iv = OpenSSL::Cipher.new(ALGO).encrypt.then do |c| iv = c.random_iv c.key = derived_key c.iv = iv [c.update(message) + c.final, c.auth_tag, iv] end { ciphertext: encode64(ciphertext), tag: encode64(tag), iv: encode64(iv), pem: server_key.to_public_pem, } end
提问
为什么客户端和服务器生成的共享密钥完全一致,但解密仍触发这个模糊的错误?我遗漏了哪些步骤?
内容的提问来源于stack exchange,提问作者mroach
相关产品推荐
相关产品推荐

