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

Swift RSA密钥生成与Java/JS平台是否存在兼容性问题?

对接外部API的RSA密钥生成问题(Swift端兼容故障)

我们正在对接某外部API,需实现RSA密钥生成逻辑:将RSA公钥提交给API,由API加密文本后返回,再由我方解密。目前Java(Android)、JavaScript(Web)版本均能正常完成对接,但Swift(iOS)版本始终失败。经测试,将其他平台生成的密钥硬编码到Swift代码中可正常使用,因此问题根源大概率在Swift的密钥生成环节。

核心疑问:Apple的RSA实现是否与Java、JavaScript的RSA实现存在兼容性问题?还是我们在Swift代码中遗漏了关键配置?

API提供商明确要求:公钥需为至少4096位的PKCS1格式,采用Base64 UTF-8纯编码(非MIME格式)。

以下是各平台的代码实现示例:

Swift代码

func generateKeyPair() throws -> (publicKey: SecKey, privateKey: SecKey) {

    let attributes: [CFString: Any] = [
        kSecAttrKeyType: kSecAttrKeyTypeRSA,
        kSecAttrKeySizeInBits: 4096,
    ]

    var error: Unmanaged<CFError>?
    guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
        throw error!.takeRetainedValue() as Error
    }
    
    guard let publicKey = SecKeyCopyPublicKey(privateKey) else {
        throw NSError(domain: NSOSStatusErrorDomain, code: Int(errSecParam), userInfo: nil)
    }
    
    return (publicKey: publicKey, privateKey: privateKey)
}


func encodePublicKey(publicKey: SecKey) -> String? {
    var error: Unmanaged<CFError>?
    guard let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? else {
        print("Error getting external representation of public key")
        return nil
    }

    return publicKeyData.base64EncodedString()
}

Java代码(Android)

public KeyPair generateKeyPair() throws GeneralSecurityException {
  KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
  keyPairGenerator.initialize(4096);
  return keyPairGenerator.generateKeyPair();
}


public String encodePublicKey(KeyPair keyPair) throws GeneralSecurityException {
  return new String(Base64.getEncoder().encode(keyPair.getPublic().getEncoded()), StandardCharsets.UTF_8);
}


public String decryptAccessToken(KeyPair keyPair, String initialisationVector, String encryptedToken, String encryptedSymmetricKey) throws GeneralSecurityException {
  PrivateKey privateKey = keyPair.getPrivate();
  Cipher rsaCipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
  OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT);
  rsaCipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParams);
  byte[] decryptedSymmetricKey = rsaCipher.doFinal(Base64.getDecoder().decode(encryptedSymmetricKey));

  byte[] iv = Base64.getDecoder().decode(initialisationVector.getBytes(StandardCharsets.UTF_8));
  SecretKey aesKey = new SecretKeySpec(decryptedSymmetricKey, "AES");
  Cipher aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
  aesCipher.init(Cipher.DECRYPT_MODE, aesKey, new GCMParameterSpec(128, iv));
  byte[] decryptedToken = aesCipher.doFinal(Base64.getDecoder().decode(encryptedToken.getBytes(StandardCharsets.UTF_8)));

  return new String(decryptedToken, StandardCharsets.UTF_8); 
}

JavaScript代码(Web)

usePublicKey = new Promise((resolve, reject) => {
  crypto.subtle.generateKey({
    name: "RSA-OAEP",
    modulusLength: 4096,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: "SHA-256"
  }, true, ["encrypt", "decrypt"])
   .then((keyPair) => {
    crypto.subtle.exportKey("spki", keyPair.publicKey).then((exported) => {
      let encodedPublicKey = btoa(String.fromCharCode.apply(null, new Uint8Array(exported)));
      resolve(encodedPublicKey);
    });
    useAccessToken = (serviceResponse, targetCallback) => {
      let encryptedSymmetricKey = serviceResponse.encryptedSymmetricKey;
      let encodedIv = serviceResponse.initialisationVector;
      let encryptedToken = serviceResponse.token;
      decryptAccessToken(encryptedToken, encryptedSymmetricKey, encodedIv, keyPair.privateKey).then((decryptedToken) => {
        targetCallback(decryptedToken);
      })
    }
  });

});



async function decryptAccessToken(encryptedToken, encryptedSymmetricKey, encodedIv, privateKey) {
  let decodedEncryptedSymmetricKey = Uint8Array.from(atob(encryptedSymmetricKey), c => c.charCodeAt(0))
  let decryptedSymmetricKey = await crypto.subtle.decrypt({
    name: "RSA-OAEP",
    hash: {
      name: "SHA-256"
    }
  }, privateKey, decodedEncryptedSymmetricKey);
  let aesKey = await crypto.subtle.importKey("raw", decryptedSymmetricKey, "AES-GCM", true, ["encrypt", "decrypt"]);
  let decodedIv = Uint8Array.from(atob(encodedIv), c => c.charCodeAt(0));
  let decodedEncryptedToken = Uint8Array.from(atob(encryptedToken), c => c.charCodeAt(0))
  let rawDecryptedToken = await crypto.subtle.decrypt({
    name: "AES-GCM",
    iv: decodedIv
  }, aesKey, decodedEncryptedToken);
  return new TextDecoder('utf-8').decode(new DataView(rawDecryptedToken));
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 03:40:10