You need to enable JavaScript to run this app. iOS Swift实现与Android一致的RSA密钥对生成及SHA256RSA签名
优惠活动
大模型
产品
解决方案
定价
更多

iOS Swift实现与Android一致的RSA密钥对生成及SHA256RSA签名

iOS Swift 与Android端一致的RSA密钥生成+SHA256RSA签名解决方案

原有代码核心问题

你的iOS代码存在3个核心问题导致和Java后端不兼容:

  • 冗余调用了两次SecKeyGeneratePair,属于无效逻辑,会导致密钥状态异常
  • iOS系统默认导出的RSA公钥是无封装的裸公钥格式,缺少JavaX509EncodedKeySpec要求的X.509标准头;私钥默认是PKCS#1格式,不是Java要求的PKCS8格式(如果需要导出私钥字符串存储需要做格式转换)
  • Base64编码格式和Android默认配置不匹配,Android的Base64.DEFAULT会每76字符插入换行,iOS默认Base64输出无换行

前置配置

需要先在项目的桥接头文件中加入以下代码,否则无法调用SHA256相关方法:

#import <CommonCrypto/CommonCrypto.h>

完整实现代码

import Security

@available(iOS 10.0, *)
class RSAGenerator {
    private var publicKey: SecKey?
    private var privateKey: SecKey?
    
    // 1024位RSA公钥X.509标准固定头,无需修改
    private let rsa1024PublicKeyHeader: [UInt8] = [
        0x30, 0x81, 0x9F, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
        0x05, 0x00, 0x03, 0x81, 0x8D, 0x00
    ]
    
    /// 生成RSA密钥对,返回符合Java规范的公钥、私钥字符串
    func generateKeyPair() -> (publicKey: String?, privateKey: String?) {
        let parameters: [String: Any] = [
            kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
            kSecAttrKeySizeInBits as String: 1024,
            kSecAttrIsPermanent as String: false // 不需要持久化到钥匙串可保持false
        ]
        
        var pubKey: SecKey?
        var privKey: SecKey?
        let status = SecKeyGeneratePair(parameters as CFDictionary, &pubKey, &privKey)
        
        guard status == errSecSuccess, let publicKey = pubKey, let privateKey = privKey else {
            return (nil, nil)
        }
        self.publicKey = publicKey
        self.privateKey = privateKey
        
        // 转换公钥为带X.509头的Java兼容格式
        var pubKeyStr: String?
        if let pubData = SecKeyCopyExternalRepresentation(publicKey, nil) as Data? {
            var fullPubData = Data(rsa1024PublicKeyHeader)
            fullPubData.append(pubData)
            // 对齐Android Base64.DEFAULT格式:每76字符插入换行
            pubKeyStr = fullPubData.base64EncodedString(options: .lineLength76Characters)
        }
        
        // 转换私钥为PKCS8格式(仅需要导出私钥字符串存储时保留)
        var privKeyStr: String?
        if let privData = SecKeyCopyExternalRepresentation(privateKey, nil) as Data? {
            let pkcs8Header: [UInt8] = [
                0x30, 0x82, 0x02, 0x76, 0x02, 0x01, 0x00, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
                0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x60
            ]
            var fullPrivData = Data(pkcs8Header)
            fullPrivData.append(privData)
            privKeyStr = fullPrivData.base64EncodedString(options: .lineLength76Characters)
        }
        
        return (pubKeyStr, privKeyStr)
    }
    
    /// 生成SHA256withRSA签名,和Android实现逻辑完全对齐
    func generateSignature(for plainText: String) -> String? {
        guard let privateKey = privateKey else { return nil }
        let plainData = Data(plainText.utf8)
        
        // 计算原文SHA256摘要
        let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
        let hashBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: digestLength)
        defer { hashBuffer.deallocate() }
        _ = plainData.withUnsafeBytes { buffer in
            CC_SHA256(buffer.baseAddress, CC_LONG(plainData.count), hashBuffer)
        }
        
        // 私钥签名,使用PKCS1_SHA256填充,对应Java的SHA256withRSA算法
        let blockSize = SecKeyGetBlockSize(privateKey)
        var signatureBuffer = [UInt8](repeating: 0, count: blockSize)
        var signatureLength = blockSize
        let signStatus = SecKeyRawSign(
            privateKey,
            .PKCS1SHA256,
            hashBuffer,
            digestLength,
            &signatureBuffer,
            &signatureLength
        )
        
        guard signStatus == errSecSuccess else { return nil }
        let signatureData = Data(bytes: signatureBuffer, count: signatureLength)
        // 对齐Android Base64.DEFAULT编码格式
        return signatureData.base64EncodedString(options: .lineLength76Characters)
    }
}

使用说明

  • 生成密钥对返回的公钥可直接传给Java后端,后端使用X509EncodedKeySpec即可正常解析
  • 生成的签名直接传给后端,后端使用SHA256withRSA算法验证即可,和Android端生成的签名完全兼容
  • 如果不需要导出私钥字符串存储,可以省略私钥转PKCS8格式的逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.11 07:03:03