Swift中实现与Java兼容的AES文件加密方案问询
Swift 实现与 Android Java 兼容的 AES 加密(PBKDF2+HMAC-SHA1 密钥派生)
以下是完全匹配你提供的 Java 加密逻辑的 Swift 实现,确保两端加密结果一致,可在 Java 服务器端正常解密:
完整实现代码
首先需要在项目桥接文件(如YourProject-Bridging-Header.h)中添加 CommonCrypto 依赖:
#import <CommonCrypto/CommonCrypto.h>
密钥派生与加密核心函数
import Foundation /// 基于 PBKDF2+HMAC-SHA1 派生 AES 密钥(对应 Java 的 PBKDF2WithHmacSHA1) func deriveKey(from password: String, salt: String, iterations: UInt32, keyLength: Int) throws -> Data { guard let passwordData = password.data(using: .utf8), let saltData = salt.data(using: .utf8) else { throw NSError(domain: "EncryptionError", code: -1, userInfo: [NSLocalizedDescriptionKey: "字符串编码失败"]) } var derivedKeyData = Data(repeating: 0, count: keyLength) let status = derivedKeyData.withUnsafeMutableBytes { derivedKeyBytes in saltData.withUnsafeBytes { saltBytes in passwordData.withUnsafeBytes { passwordBytes in CCKeyDerivationPBKDF( CCPBKDFAlgorithm(kCCPBKDF2), passwordBytes.baseAddress, passwordData.count, saltBytes.baseAddress, saltData.count, CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1), iterations, derivedKeyBytes.baseAddress, derivedKeyData.count ) } } } guard status == kCCSuccess else { throw NSError(domain: "EncryptionError", code: Int(status), userInfo: [NSLocalizedDescriptionKey: "密钥派生失败"]) } return derivedKeyData } /// AES-CBC-PKCS7 加密(对应 Java 的 AES/CBC/PKCS5Padding) func encryptData(_ data: Data, key: Data, iv: [UInt8]) throws -> Data { guard iv.count == kCCBlockSizeAES128 else { throw NSError(domain: "EncryptionError", code: -2, userInfo: [NSLocalizedDescriptionKey: "IV 长度必须为16字节"]) } let ivData = Data(iv) var encryptedData = Data(repeating: 0, count: data.count + kCCBlockSizeAES128) var numBytesEncrypted = 0 let status = CCCrypt( CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionPKCS7Padding), key.bytes, key.count, ivData.bytes, data.bytes, data.count, encryptedData.mutableBytes, encryptedData.count, &numBytesEncrypted ) guard status == kCCSuccess else { throw NSError(domain: "EncryptionError", code: Int(status), userInfo: [NSLocalizedDescriptionKey: "加密失败"]) } encryptedData.removeSubrange(numBytesEncrypted..<encryptedData.count) return encryptedData }
补全文件选择后的加密逻辑
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { guard let inputURL = urls.first else { return } // 输出文件路径:Documents 目录下的 temp.enc let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let outputURL = documentDirectory.appendingPathComponent("temp.enc") // 与 Java 端完全一致的配置参数 let secretKeyString = "MY_SECRETE_KEY" let saltString = "MY_SALT_KEY" let iterations: UInt32 = 65536 let keyLength = 32 // 256位密钥 let iv: [UInt8] = Array(repeating: 0, count: 16) // 16字节全0 IV do { // 1. 读取选中的文件数据 let fileData = try Data(contentsOf: inputURL) // 2. 派生 AES 密钥 let aesKey = try deriveKey(from: secretKeyString, salt: saltString, iterations: iterations, keyLength: keyLength) // 3. 执行加密 let encryptedData = try encryptData(fileData, key: aesKey, iv: iv) // 4. 写入加密后的文件 try encryptedData.write(to: outputURL) print("加密完成,文件路径:\(outputURL.path)") } catch { print("加密出错:\(error.localizedDescription)") } }
代码对应关系说明
密钥派生逻辑
- Java 中的
SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")和PBEKeySpec,对应 Swift 的CCKeyDerivationPBKDF函数,参数完全匹配:- 伪随机算法:
kCCPRFHmacAlgSHA1(对应 HmacSHA1) - 迭代次数:65536(与 Java 一致)
- 密钥长度:32字节(256位,对应 Java 的256参数)
- 编码:密码和盐均使用 UTF-8,与 Java 的
getBytes()默认行为保持一致
- 伪随机算法:
- Java 中的
AES加密逻辑
- Java 的
AES/CBC/PKCS5Padding对应 Swift 配置:- 算法:
kCCAlgorithmAES(AES) - 模式:CBC(CCCrypt 默认模式为 CBC,无需额外指定)
- 填充:
kCCOptionPKCS7Padding(AES 块大小为16字节,PKCS5与PKCS7填充在此场景下等价)
- 算法:
- IV 使用16字节全0数组,与 Java 的
IvParameterSpec完全一致
- Java 的
文件读写
- Swift 的
Data(contentsOf:)对应 Java 的Files.readAllBytes - Swift 的
Data.write(to:)对应 Java 的FileOutputStream写入逻辑
- Swift 的
注意事项
- 确保 Android 端的
SALT.getBytes()和SECRET_KEY.toCharArray()使用 UTF-8 编码,若 Java 端指定了其他编码,Swift 需同步调整(如.ascii) - 实际项目中建议优化错误处理逻辑,避免直接打印错误,可向用户展示友好提示
- 密钥和盐不应硬编码在代码中,建议通过安全渠道传递
内容的提问来源于stack exchange,提问作者Hritik Gupta
相关产品推荐
相关产品推荐

