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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 21:10:07