iOS Swift实现与Android一致的RSA密钥对生成及SHA256RSA签名
iOS Swift 与Android端一致的RSA密钥生成+SHA256RSA签名解决方案
原有代码核心问题
你的iOS代码存在3个核心问题导致和Java后端不兼容:
- 冗余调用了两次
SecKeyGeneratePair,属于无效逻辑,会导致密钥状态异常 - iOS系统默认导出的RSA公钥是无封装的裸公钥格式,缺少Java
X509EncodedKeySpec要求的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
相关产品推荐
相关产品推荐

