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

