swift 中的 AES 加密
AES encryption in swift
我正在尝试在 swift 中实施 AES 加密。 Android 和 C# 的加密解密工作正常。我需要在 swift 中实现它。它是 current code for android 并且 C# 后面跟着这个。
我尝试使用
但是 none 有效。当我在服务器上发送加密字符串时,它没有被解密。
任何帮助将不胜感激
一定要使用相同的参数,这些参数似乎是 AES 和 CBC 模式,带有 iv、PKCS5Padding
(实际上是 PKCS#7)填充和 16 字节(128 位)密钥。
PKCS#5 填充和 PKCS#7 填充本质上是相同的,有时由于历史原因 PKCS#5 填充被指定用于 AES 但实际填充是 PKCS#7。
确保密钥、iv 和加密数据的编码完全匹配。十六进制将它们转储到两个平台上以确保它们相同。加密函数并不难用,只要输入参数正确输出就正确。
为了使这更安全,iv 应该是随机字节并添加到加密数据之前,以便在解密期间使用。
跨平台 AES 加密使用 256 位密钥,因此无法按原样工作。
示例:
Swift 2
// operation: kCCEncrypt or kCCDecrypt
func testCrypt(data data:[UInt8], keyData:[UInt8], ivData:[UInt8], operation:Int) -> [UInt8]? {
let cryptLength = size_t(data.count+kCCBlockSizeAES128)
var cryptData = [UInt8](count:cryptLength, repeatedValue:0)
let keyLength = size_t(kCCKeySizeAES128)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(CCOperation(operation),
algoritm,
options,
keyData, keyLength,
ivData,
data, data.count,
&cryptData, cryptLength,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.removeRange(numBytesEncrypted..<cryptData.count)
} else {
print("Error: \(cryptStatus)")
}
return cryptData;
}
let message = "Don´t try to read this text. Top Secret Stuff"
let messageData = Array(message.utf8)
let keyData = Array("12345678901234567890123456789012".utf8)
let ivData = Array("abcdefghijklmnop".utf8)
let encryptedData = testCrypt(data:messageData, keyData:keyData, ivData:ivData, operation:kCCEncrypt)!
let decryptedData = testCrypt(data:encryptedData, keyData:keyData, ivData:ivData, operation:kCCDecrypt)!
var decrypted = String(bytes:decryptedData, encoding:NSUTF8StringEncoding)!
print("message: \(message)");
print("messageData: \(NSData(bytes:messageData, length:messageData.count))");
print("keyData: \(NSData(bytes:keyData, length:keyData.count))");
print("ivData: \(NSData(bytes:ivData, length:ivData.count))");
print("encryptedData: \(NSData(bytes:encryptedData, length:encryptedData.count))");
print("decryptedData: \(NSData(bytes:decryptedData, length:decryptedData.count))");
print("decrypted: \(String(bytes:decryptedData,encoding:NSUTF8StringEncoding)!)");
输出:
message: Don´t try to read this text. Top Secret Stuff
messageData: 446f6ec2 b4742074 72792074 6f207265 61642074 68697320 74657874 2e20546f 70205365 63726574 20537475 6666
keyData: 31323334 35363738 39303132 33343536 37383930 31323334 35363738 39303132
ivData: 61626364 65666768 696a6b6c 6d6e6f70
encryptedData: b1b6dc17 62eaf3f8 baa1cb87 21ddc35c dee803ed fb320020 85794848 21206943 a85feb5b c8ee58fc d6fb664b 96b81114
decryptedData: 446f6ec2 b4742074 72792074 6f207265 61642074 68697320 74657874 2e20546f 70205365 63726574 20537475 6666
decrypted: Don´t try to read this text. Top Secret Stuff
Swift 3 with [UInt8]
type
func testCrypt(data:[UInt8], keyData:[UInt8], ivData:[UInt8], operation:Int) -> [UInt8]? {
let cryptLength = size_t(data.count+kCCBlockSizeAES128)
var cryptData = [UInt8](repeating:0, count:cryptLength)
let keyLength = size_t(kCCKeySizeAES128)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(CCOperation(operation),
algoritm,
options,
keyData, keyLength,
ivData,
data, data.count,
&cryptData, cryptLength,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.removeSubrange(numBytesEncrypted..<cryptData.count)
} else {
print("Error: \(cryptStatus)")
}
return cryptData;
}
Swift 3 & 4 with Data
type
func testCrypt(data:Data, keyData:Data, ivData:Data, operation:Int) -> Data {
let cryptLength = size_t(data.count + kCCBlockSizeAES128)
var cryptData = Data(count:cryptLength)
let keyLength = size_t(kCCKeySizeAES128)
let options = CCOptions(kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
let cryptStatus = cryptData.withUnsafeMutableBytes {cryptBytes in
data.withUnsafeBytes {dataBytes in
ivData.withUnsafeBytes {ivBytes in
keyData.withUnsafeBytes {keyBytes in
CCCrypt(CCOperation(operation),
CCAlgorithm(kCCAlgorithmAES),
options,
keyBytes, keyLength,
ivBytes,
dataBytes, data.count,
cryptBytes, cryptLength,
&numBytesEncrypted)
}
}
}
}
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.removeSubrange(numBytesEncrypted..<cryptData.count)
} else {
print("Error: \(cryptStatus)")
}
return cryptData;
}
let message = "Don´t try to read this text. Top Secret Stuff"
let messageData = message.data(using:String.Encoding.utf8)!
let keyData = "12345678901234567890123456789012".data(using:String.Encoding.utf8)!
let ivData = "abcdefghijklmnop".data(using:String.Encoding.utf8)!
let encryptedData = testCrypt(data:messageData, keyData:keyData, ivData:ivData, operation:kCCEncrypt)
let decryptedData = testCrypt(data:encryptedData, keyData:keyData, ivData:ivData, operation:kCCDecrypt)
var decrypted = String(bytes:decryptedData, encoding:String.Encoding.utf8)!
已停用文档部分的示例:
CBC 模式下的 AES 加密,随机 IV (Swift 3+)
iv 是加密数据的前缀
aesCBC128Encrypt
将创建一个随机 IV 并作为加密代码的前缀。
aesCBC128Decrypt
将在解密期间使用带前缀的 IV。
输入是数据,键是数据对象。如果需要从调用方法中转换为 and/or 等编码形式,例如 Base64。
密钥的长度应恰好为 128 位(16 字节)、192 位(24 字节)或 256 位(32 字节)。如果使用另一个密钥大小,则会抛出错误。
PKCS#7 padding 默认设置。
此示例需要通用加密
必须有项目的桥接头:
#import <CommonCrypto/CommonCrypto.h>
将 Security.framework
添加到项目中。
这是示例,不是生产代码。
enum AESError: Error {
case KeyError((String, Int))
case IVError((String, Int))
case CryptorError((String, Int))
}
// The iv is prefixed to the encrypted data
func aesCBCEncrypt(data:Data, keyData:Data) throws -> Data {
let keyLength = keyData.count
let validKeyLengths = [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256]
if (validKeyLengths.contains(keyLength) == false) {
throw AESError.KeyError(("Invalid key length", keyLength))
}
let ivSize = kCCBlockSizeAES128;
let cryptLength = size_t(ivSize + data.count + kCCBlockSizeAES128)
var cryptData = Data(count:cryptLength)
let status = cryptData.withUnsafeMutableBytes {ivBytes in
SecRandomCopyBytes(kSecRandomDefault, kCCBlockSizeAES128, ivBytes)
}
if (status != 0) {
throw AESError.IVError(("IV generation failed", Int(status)))
}
var numBytesEncrypted :size_t = 0
let options = CCOptions(kCCOptionPKCS7Padding)
let cryptStatus = cryptData.withUnsafeMutableBytes {cryptBytes in
data.withUnsafeBytes {dataBytes in
keyData.withUnsafeBytes {keyBytes in
CCCrypt(CCOperation(kCCEncrypt),
CCAlgorithm(kCCAlgorithmAES),
options,
keyBytes, keyLength,
cryptBytes,
dataBytes, data.count,
cryptBytes+kCCBlockSizeAES128, cryptLength,
&numBytesEncrypted)
}
}
}
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.count = numBytesEncrypted + ivSize
}
else {
throw AESError.CryptorError(("Encryption failed", Int(cryptStatus)))
}
return cryptData;
}
// The iv is prefixed to the encrypted data
func aesCBCDecrypt(data:Data, keyData:Data) throws -> Data? {
let keyLength = keyData.count
let validKeyLengths = [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256]
if (validKeyLengths.contains(keyLength) == false) {
throw AESError.KeyError(("Invalid key length", keyLength))
}
let ivSize = kCCBlockSizeAES128;
let clearLength = size_t(data.count - ivSize)
var clearData = Data(count:clearLength)
var numBytesDecrypted :size_t = 0
let options = CCOptions(kCCOptionPKCS7Padding)
let cryptStatus = clearData.withUnsafeMutableBytes {cryptBytes in
data.withUnsafeBytes {dataBytes in
keyData.withUnsafeBytes {keyBytes in
CCCrypt(CCOperation(kCCDecrypt),
CCAlgorithm(kCCAlgorithmAES128),
options,
keyBytes, keyLength,
dataBytes,
dataBytes+kCCBlockSizeAES128, clearLength,
cryptBytes, clearLength,
&numBytesDecrypted)
}
}
}
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
clearData.count = numBytesDecrypted
}
else {
throw AESError.CryptorError(("Decryption failed", Int(cryptStatus)))
}
return clearData;
}
用法示例:
let clearData = "clearData0123456".data(using:String.Encoding.utf8)!
let keyData = "keyData890123456".data(using:String.Encoding.utf8)!
print("clearData: \(clearData as NSData)")
print("keyData: \(keyData as NSData)")
var cryptData :Data?
do {
cryptData = try aesCBCEncrypt(data:clearData, keyData:keyData)
print("cryptData: \(cryptData! as NSData)")
}
catch (let status) {
print("Error aesCBCEncrypt: \(status)")
}
let decryptData :Data?
do {
let decryptData = try aesCBCDecrypt(data:cryptData!, keyData:keyData)
print("decryptData: \(decryptData! as NSData)")
}
catch (let status) {
print("Error aesCBCDecrypt: \(status)")
}
示例输出:
clearData: <636c6561 72446174 61303132 33343536>
keyData: <6b657944 61746138 39303132 33343536>
cryptData: <92c57393 f454d959 5a4d158f 6e1cd3e7 77986ee9 b2970f49 2bafcf1a 8ee9d51a bde49c31 d7780256 71837a61 60fa4be0>
decryptData: <636c6561 72446174 61303132 33343536>
备注:
CBC 模式示例代码的一个典型问题是它将随机 IV 的创建和共享留给了用户。此示例包括 IV 的生成、为加密数据添加前缀并在解密期间使用带前缀的 IV。这将临时用户从 CBC mode.
所需的详细信息中解放出来
为了安全起见,加密的数据也应该有身份验证,这个示例代码没有提供,为了体积小并允许与其他平台更好的互操作性。
还缺少从密码导出密钥的密钥,建议使用 PBKDF2 文本密码用作密钥 material。
对于稳健的生产就绪多平台加密代码,请参阅 RNCryptor。
我的两分钱:
swift 4 / xcode 9 扩展数据:
extension Data{
func aesEncrypt( keyData: Data, ivData: Data, operation: Int) -> Data {
let dataLength = self.count
let cryptLength = size_t(dataLength + kCCBlockSizeAES128)
var cryptData = Data(count:cryptLength)
let keyLength = size_t(kCCKeySizeAES128)
let options = CCOptions(kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
let cryptStatus = cryptData.withUnsafeMutableBytes {cryptBytes in
self.withUnsafeBytes {dataBytes in
ivData.withUnsafeBytes {ivBytes in
keyData.withUnsafeBytes {keyBytes in
CCCrypt(CCOperation(operation),
CCAlgorithm(kCCAlgorithmAES),
options,
keyBytes, keyLength,
ivBytes,
dataBytes, dataLength,
cryptBytes, cryptLength,
&numBytesEncrypted)
}
}
}
}
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.removeSubrange(numBytesEncrypted..<cryptData.count)
} else {
print("Error: \(cryptStatus)")
}
return cryptData;
}
}
func testAES() -> Bool {
let message = "secret message"
let key = "key890123456"
let ivString = "abcdefghijklmnop" // 16 bytes for AES128
let messageData = message.data(using:String.Encoding.utf8)!
let keyData = key.data(using: .utf8)!
let ivData = ivString.data(using: .utf8)!
let encryptedData = messageData.aesEncrypt( keyData:keyData, ivData:ivData, operation:kCCEncrypt)
let decryptedData = encryptedData.aesEncrypt( keyData:keyData, ivData:ivData, operation:kCCDecrypt)
let decrypted = String(bytes:decryptedData, encoding:String.Encoding.utf8)!
return message == decrypted
}
找到一个名为 RNCryptor 的库,用 swift 语言实现,用于 AES 加密/解密。
可以使用 Cocoapods 或 Carthage 完成安装。
这是加密和解密的示例代码。
// Encryption
let data = "sample data string".data(using: String.Encoding.utf8)
let password = "Secret password"
let encryptedData = RNCryptor.encrypt(data: data, withPassword: password)
// Decryption
do {
let originalData = try RNCryptor.decrypt(data: encryptedData, withPassword: password)
// ...
} catch {
print(error)
}
我用过 CryptoSwift。
首先,我在 pod 文件中安装了 cryptoSwift。然后在我的视图控制器中我导入了 CryptoSwift。
这是我用过的代码:
let value = "xyzzy". // This is the value that we want to encrypt
let key = "abc". // This is the key
let EncryptedValue = try! value.aesEncrypt(key: key)
let DecryptedValue = try! EncryptedValue.aesDecrypt(key: key)
然后,使用字符串扩展:
extension String {
func aesEncrypt(key: String) throws -> String {
var result = ""
do {
let key: [UInt8] = Array(key.utf8) as [UInt8]
let aes = try! AES(key: key, blockMode: .ECB, padding: .pkcs5) // AES128 .ECB pkcs7
let encrypted = try aes.encrypt(Array(self.utf8))
result = encrypted.toBase64()!
print("AES Encryption Result: \(result)")
} catch {
print("Error: \(error)")
}
return result
}
func aesDecrypt(key: String) throws -> String {
var result = ""
do {
let encrypted = self
let key: [UInt8] = Array(key.utf8) as [UInt8]
let aes = try! AES(key: key, blockMode: .ECB, padding: .pkcs5) // AES128 .ECB pkcs7
let decrypted = try aes.decrypt(Array(base64: encrypted))
result = String(data: Data(decrypted), encoding: .utf8) ?? ""
print("AES Decryption Result: \(result)")
} catch {
print("Error: \(error)")
}
return result
}
}
在此我没有使用 iv 和 encrypted.toBase64() 来加密 result = encrypted.toBase64()!
代替加密 result = encrypted.toStringHex()!
中的
和解密类似 let decrypted = try aes.decrypt(Array(base64: encrypted))
代替 let decrypted = try aes.decrypt(Array(Hex: encrypted))
对于无法将字节数组转换为字符串的任何人
String(data: Data(decrypted), encoding: .utf8)
这是我的示例字符串扩展
extension String {
func decryptAES(key: String, iv: String) -> String {
do {
let encrypted = self
let key = Array(key.utf8)
let iv = Array(iv.utf8)
let aes = try AES(key: key, blockMode: CTR(iv: iv), padding: .noPadding)
let decrypted = try aes.decrypt(Array(hex: encrypted))
return String(data: Data(decrypted), encoding: .utf8) ?? ""
} catch {
return "Error: \(error)"
}
}
}
Swift 5
我重构了@ingconti 的代码。
import Foundation
import CommonCrypto
struct AES {
// MARK: - Value
// MARK: Private
private let key: Data
private let iv: Data
// MARK: - Initialzier
init?(key: String, iv: String) {
guard key.count == kCCKeySizeAES128 || key.count == kCCKeySizeAES256, let keyData = key.data(using: .utf8) else {
debugPrint("Error: Failed to set a key.")
return nil
}
guard iv.count == kCCBlockSizeAES128, let ivData = iv.data(using: .utf8) else {
debugPrint("Error: Failed to set an initial vector.")
return nil
}
self.key = keyData
self.iv = ivData
}
// MARK: - Function
// MARK: Public
func encrypt(string: String) -> Data? {
return crypt(data: string.data(using: .utf8), option: CCOperation(kCCEncrypt))
}
func decrypt(data: Data?) -> String? {
guard let decryptedData = crypt(data: data, option: CCOperation(kCCDecrypt)) else { return nil }
return String(bytes: decryptedData, encoding: .utf8)
}
func crypt(data: Data?, option: CCOperation) -> Data? {
guard let data = data else { return nil }
let cryptLength = data.count + key.count
var cryptData = Data(count: cryptLength)
var bytesLength = Int(0)
let status = cryptData.withUnsafeMutableBytes { cryptBytes in
data.withUnsafeBytes { dataBytes in
iv.withUnsafeBytes { ivBytes in
key.withUnsafeBytes { keyBytes in
CCCrypt(option, CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionPKCS7Padding), keyBytes.baseAddress, key.count, ivBytes.baseAddress, dataBytes.baseAddress, data.count, cryptBytes.baseAddress, cryptLength, &bytesLength)
}
}
}
}
guard Int32(status) == Int32(kCCSuccess) else {
debugPrint("Error: Failed to crypt data. Status \(status)")
return nil
}
cryptData.removeSubrange(bytesLength..<cryptData.count)
return cryptData
}
}
这样使用
let password = "UserPassword1!"
let key128 = "1234567890123456" // 16 bytes for AES128
let key256 = "12345678901234561234567890123456" // 32 bytes for AES256
let iv = "abcdefghijklmnop" // 16 bytes for AES128
let aes128 = AES(key: key128, iv: iv)
let aes256 = AES(key: key256, iv: iv)
let encryptedPassword128 = aes128?.encrypt(string: password)
aes128?.decrypt(data: encryptedPassword128)
let encryptedPassword256 = aes256?.encrypt(string: password)
aes256?.decrypt(data: encryptedPassword256)
结果
基于@zaph 的出色回答,我创建了这个 Playground 用于:
Swift 5
import Foundation
import CommonCrypto
protocol Cryptable {
func encrypt(_ string: String) throws -> Data
func decrypt(_ data: Data) throws -> String
}
struct AES {
private let key: Data
private let ivSize: Int = kCCBlockSizeAES128
private let options: CCOptions = CCOptions(kCCOptionPKCS7Padding)
init(keyString: String) throws {
guard keyString.count == kCCKeySizeAES256 else {
throw Error.invalidKeySize
}
self.key = Data(keyString.utf8)
}
}
extension AES {
enum Error: Swift.Error {
case invalidKeySize
case generateRandomIVFailed
case encryptionFailed
case decryptionFailed
case dataToStringFailed
}
}
private extension AES {
func generateRandomIV(for data: inout Data) throws {
try data.withUnsafeMutableBytes { dataBytes in
guard let dataBytesBaseAddress = dataBytes.baseAddress else {
throw Error.generateRandomIVFailed
}
let status: Int32 = SecRandomCopyBytes(
kSecRandomDefault,
kCCBlockSizeAES128,
dataBytesBaseAddress
)
guard status == 0 else {
throw Error.generateRandomIVFailed
}
}
}
}
extension AES: Cryptable {
func encrypt(_ string: String) throws -> Data {
let dataToEncrypt = Data(string.utf8)
let bufferSize: Int = ivSize + dataToEncrypt.count + kCCBlockSizeAES128
var buffer = Data(count: bufferSize)
try generateRandomIV(for: &buffer)
var numberBytesEncrypted: Int = 0
do {
try key.withUnsafeBytes { keyBytes in
try dataToEncrypt.withUnsafeBytes { dataToEncryptBytes in
try buffer.withUnsafeMutableBytes { bufferBytes in
guard let keyBytesBaseAddress = keyBytes.baseAddress,
let dataToEncryptBytesBaseAddress = dataToEncryptBytes.baseAddress,
let bufferBytesBaseAddress = bufferBytes.baseAddress else {
throw Error.encryptionFailed
}
let cryptStatus: CCCryptorStatus = CCCrypt( // Stateless, one-shot encrypt operation
CCOperation(kCCEncrypt), // op: CCOperation
CCAlgorithm(kCCAlgorithmAES), // alg: CCAlgorithm
options, // options: CCOptions
keyBytesBaseAddress, // key: the "password"
key.count, // keyLength: the "password" size
bufferBytesBaseAddress, // iv: Initialization Vector
dataToEncryptBytesBaseAddress, // dataIn: Data to encrypt bytes
dataToEncryptBytes.count, // dataInLength: Data to encrypt size
bufferBytesBaseAddress + ivSize, // dataOut: encrypted Data buffer
bufferSize, // dataOutAvailable: encrypted Data buffer size
&numberBytesEncrypted // dataOutMoved: the number of bytes written
)
guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
throw Error.encryptionFailed
}
}
}
}
} catch {
throw Error.encryptionFailed
}
let encryptedData: Data = buffer[..<(numberBytesEncrypted + ivSize)]
return encryptedData
}
func decrypt(_ data: Data) throws -> String {
let bufferSize: Int = data.count - ivSize
var buffer = Data(count: bufferSize)
var numberBytesDecrypted: Int = 0
do {
try key.withUnsafeBytes { keyBytes in
try data.withUnsafeBytes { dataToDecryptBytes in
try buffer.withUnsafeMutableBytes { bufferBytes in
guard let keyBytesBaseAddress = keyBytes.baseAddress,
let dataToDecryptBytesBaseAddress = dataToDecryptBytes.baseAddress,
let bufferBytesBaseAddress = bufferBytes.baseAddress else {
throw Error.encryptionFailed
}
let cryptStatus: CCCryptorStatus = CCCrypt( // Stateless, one-shot encrypt operation
CCOperation(kCCDecrypt), // op: CCOperation
CCAlgorithm(kCCAlgorithmAES128), // alg: CCAlgorithm
options, // options: CCOptions
keyBytesBaseAddress, // key: the "password"
key.count, // keyLength: the "password" size
dataToDecryptBytesBaseAddress, // iv: Initialization Vector
dataToDecryptBytesBaseAddress + ivSize, // dataIn: Data to decrypt bytes
bufferSize, // dataInLength: Data to decrypt size
bufferBytesBaseAddress, // dataOut: decrypted Data buffer
bufferSize, // dataOutAvailable: decrypted Data buffer size
&numberBytesDecrypted // dataOutMoved: the number of bytes written
)
guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
throw Error.decryptionFailed
}
}
}
}
} catch {
throw Error.encryptionFailed
}
let decryptedData: Data = buffer[..<numberBytesDecrypted]
guard let decryptedString = String(data: decryptedData, encoding: .utf8) else {
throw Error.dataToStringFailed
}
return decryptedString
}
}
do {
let aes = try AES(keyString: "FiugQTgPNwCWUY,VhfmM4cKXTLVFvHFe")
let stringToEncrypt: String = "please encrypt meeee"
print("String to encrypt:\t\t\t\(stringToEncrypt)")
let encryptedData: Data = try aes.encrypt(stringToEncrypt)
print("String encrypted (base64):\t\(encryptedData.base64EncodedString())")
let decryptedData: String = try aes.decrypt(encryptedData)
print("String decrypted:\t\t\t\(decryptedData)")
} catch {
print("Something went wrong: \(error)")
}
输出:
我还基于它创建了一个Swift包:
我知道这是一个老问题。
自 2019 年起您可以使用 Apple 的 CryptoKit
。它介绍了 AES.GCM
import CryptoKit
let key = SymmetricKey(size: .bits256)
let data = Data(...)
do {
let enc = try AES.GCM.seal(data, using: key).combined!
let dec = try AES.GCM.open(try AES.GCM.SealedBox(combined: data), using: key))
} catch {...}
此外,我还制作了一个有用的 swift 包,扩展了 CryptoKit 以允许 AES.CBC
加密 (https://github.com/gal-yedidovich/CryptoExtensions)
那么,就import CBC
import CryptoKit
import CBC
let data: Data = ... //some data to encrypt
let iv: Data = ... //an initial vector
let key: SymmetricKey = ... //encryption key
//one shot crypto operation
do {
let encrypted = try AES.CBC.encrypt(data, using: key, iv: iv)
let decrypted = try AES.CBC.decrypt(encrypted, using: key, iv: iv)
} catch {...}
//using cipher
do {
let cipher = try AES.CBC.Cipher(.encrypt, using: key, iv: iv)
var enc = Data()
enc += try cipher.update(...)
enc += try cipher.update(...)
enc += try cipher.finalize()
} catch {...}
我正在寻找使用 PKC5 填充的 AES 加密 ECB 模式而不使用任何 pod。通过收集不同的信息,我找到了解决问题的正确方法。也许对其他人有帮助。
注意:PKCS5 和 PKCS7 填充没有区别。
import CommonCrypto
func encryptionAESModeECB(messageData data: Data, key: String) -> Data? {
guard let keyData = key.data(using: String.Encoding.utf8) else { return nil }
guard let cryptData = NSMutableData(length: Int((data.count)) + kCCBlockSizeAES128) else { return nil }
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
let iv: String = ""
var numBytesEncrypted: size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
(keyData as NSData).bytes, keyLength,
iv,
(data as NSData).bytes, data.count,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
let encryptedString = cryptData.base64EncodedString(options: .lineLength64Characters)
return encryptedString.data(using: .utf8)
} else {
return nil
}
}
func decryptionAESModeECB(messageData: Data, key: String) -> Data? {
guard let messageString = String(data: messageData, encoding: .utf8) else { return nil }
guard let data = Data(base64Encoded: messageString, options: .ignoreUnknownCharacters) else { return nil }
guard let keyData = key.data(using: String.Encoding.utf8) else { return nil }
guard let cryptData = NSMutableData(length: Int((data.count)) + kCCBlockSizeAES128) else { return nil }
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCDecrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
let iv: String = ""
var numBytesEncrypted: size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
(keyData as NSData).bytes, keyLength,
iv,
(data as NSData).bytes, data.count,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
return cryptData as Data
} else {
return nil
}
}
这样使用:
let encryptedData = encryptionAESModeECB(messageData: data, key: "keyString")
let decryptedData = decryptionAESModeECB(messageData: data, key: "keyString")
这个扩展很容易使用,也支持所有平台的解密。
import CryptoSwift
extension String {
func aesEncrypt() throws -> String {
do {
let encrypted = try AES(key: "your_encryption_key", iv: "your_iv", padding: .pkcs7).encrypt([UInt8](self.data(using: .utf8)!))
return Data(encrypted).base64EncodedString()
} catch {
}
return ""
}
func aesDecrypt() throws -> String {
do {
guard let data = Data(base64Encoded: self) else { return "" }
let decrypted = try AES(key: "your_encryption_key", iv: "your_iv", padding: .pkcs7).decrypt([UInt8](data))
return decrypted
} catch {
}
return ""
}
}
我正在尝试在 swift 中实施 AES 加密。 Android 和 C# 的加密解密工作正常。我需要在 swift 中实现它。它是 current code for android 并且 C# 后面跟着这个。
我尝试使用
但是 none 有效。当我在服务器上发送加密字符串时,它没有被解密。
任何帮助将不胜感激
一定要使用相同的参数,这些参数似乎是 AES 和 CBC 模式,带有 iv、PKCS5Padding
(实际上是 PKCS#7)填充和 16 字节(128 位)密钥。
PKCS#5 填充和 PKCS#7 填充本质上是相同的,有时由于历史原因 PKCS#5 填充被指定用于 AES 但实际填充是 PKCS#7。
确保密钥、iv 和加密数据的编码完全匹配。十六进制将它们转储到两个平台上以确保它们相同。加密函数并不难用,只要输入参数正确输出就正确。
为了使这更安全,iv 应该是随机字节并添加到加密数据之前,以便在解密期间使用。
跨平台 AES 加密使用 256 位密钥,因此无法按原样工作。
示例:
Swift 2
// operation: kCCEncrypt or kCCDecrypt
func testCrypt(data data:[UInt8], keyData:[UInt8], ivData:[UInt8], operation:Int) -> [UInt8]? {
let cryptLength = size_t(data.count+kCCBlockSizeAES128)
var cryptData = [UInt8](count:cryptLength, repeatedValue:0)
let keyLength = size_t(kCCKeySizeAES128)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(CCOperation(operation),
algoritm,
options,
keyData, keyLength,
ivData,
data, data.count,
&cryptData, cryptLength,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.removeRange(numBytesEncrypted..<cryptData.count)
} else {
print("Error: \(cryptStatus)")
}
return cryptData;
}
let message = "Don´t try to read this text. Top Secret Stuff"
let messageData = Array(message.utf8)
let keyData = Array("12345678901234567890123456789012".utf8)
let ivData = Array("abcdefghijklmnop".utf8)
let encryptedData = testCrypt(data:messageData, keyData:keyData, ivData:ivData, operation:kCCEncrypt)!
let decryptedData = testCrypt(data:encryptedData, keyData:keyData, ivData:ivData, operation:kCCDecrypt)!
var decrypted = String(bytes:decryptedData, encoding:NSUTF8StringEncoding)!
print("message: \(message)");
print("messageData: \(NSData(bytes:messageData, length:messageData.count))");
print("keyData: \(NSData(bytes:keyData, length:keyData.count))");
print("ivData: \(NSData(bytes:ivData, length:ivData.count))");
print("encryptedData: \(NSData(bytes:encryptedData, length:encryptedData.count))");
print("decryptedData: \(NSData(bytes:decryptedData, length:decryptedData.count))");
print("decrypted: \(String(bytes:decryptedData,encoding:NSUTF8StringEncoding)!)");
输出:
message: Don´t try to read this text. Top Secret Stuff messageData: 446f6ec2 b4742074 72792074 6f207265 61642074 68697320 74657874 2e20546f 70205365 63726574 20537475 6666 keyData: 31323334 35363738 39303132 33343536 37383930 31323334 35363738 39303132 ivData: 61626364 65666768 696a6b6c 6d6e6f70 encryptedData: b1b6dc17 62eaf3f8 baa1cb87 21ddc35c dee803ed fb320020 85794848 21206943 a85feb5b c8ee58fc d6fb664b 96b81114 decryptedData: 446f6ec2 b4742074 72792074 6f207265 61642074 68697320 74657874 2e20546f 70205365 63726574 20537475 6666 decrypted: Don´t try to read this text. Top Secret Stuff
Swift 3 with
[UInt8]
type
func testCrypt(data:[UInt8], keyData:[UInt8], ivData:[UInt8], operation:Int) -> [UInt8]? {
let cryptLength = size_t(data.count+kCCBlockSizeAES128)
var cryptData = [UInt8](repeating:0, count:cryptLength)
let keyLength = size_t(kCCKeySizeAES128)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(CCOperation(operation),
algoritm,
options,
keyData, keyLength,
ivData,
data, data.count,
&cryptData, cryptLength,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.removeSubrange(numBytesEncrypted..<cryptData.count)
} else {
print("Error: \(cryptStatus)")
}
return cryptData;
}
Swift 3 & 4 with
Data
type
func testCrypt(data:Data, keyData:Data, ivData:Data, operation:Int) -> Data {
let cryptLength = size_t(data.count + kCCBlockSizeAES128)
var cryptData = Data(count:cryptLength)
let keyLength = size_t(kCCKeySizeAES128)
let options = CCOptions(kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
let cryptStatus = cryptData.withUnsafeMutableBytes {cryptBytes in
data.withUnsafeBytes {dataBytes in
ivData.withUnsafeBytes {ivBytes in
keyData.withUnsafeBytes {keyBytes in
CCCrypt(CCOperation(operation),
CCAlgorithm(kCCAlgorithmAES),
options,
keyBytes, keyLength,
ivBytes,
dataBytes, data.count,
cryptBytes, cryptLength,
&numBytesEncrypted)
}
}
}
}
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.removeSubrange(numBytesEncrypted..<cryptData.count)
} else {
print("Error: \(cryptStatus)")
}
return cryptData;
}
let message = "Don´t try to read this text. Top Secret Stuff"
let messageData = message.data(using:String.Encoding.utf8)!
let keyData = "12345678901234567890123456789012".data(using:String.Encoding.utf8)!
let ivData = "abcdefghijklmnop".data(using:String.Encoding.utf8)!
let encryptedData = testCrypt(data:messageData, keyData:keyData, ivData:ivData, operation:kCCEncrypt)
let decryptedData = testCrypt(data:encryptedData, keyData:keyData, ivData:ivData, operation:kCCDecrypt)
var decrypted = String(bytes:decryptedData, encoding:String.Encoding.utf8)!
已停用文档部分的示例:
CBC 模式下的 AES 加密,随机 IV (Swift 3+)
iv 是加密数据的前缀
aesCBC128Encrypt
将创建一个随机 IV 并作为加密代码的前缀。
aesCBC128Decrypt
将在解密期间使用带前缀的 IV。
输入是数据,键是数据对象。如果需要从调用方法中转换为 and/or 等编码形式,例如 Base64。
密钥的长度应恰好为 128 位(16 字节)、192 位(24 字节)或 256 位(32 字节)。如果使用另一个密钥大小,则会抛出错误。
PKCS#7 padding 默认设置。
此示例需要通用加密
必须有项目的桥接头:
#import <CommonCrypto/CommonCrypto.h>
将 Security.framework
添加到项目中。
这是示例,不是生产代码。
enum AESError: Error {
case KeyError((String, Int))
case IVError((String, Int))
case CryptorError((String, Int))
}
// The iv is prefixed to the encrypted data
func aesCBCEncrypt(data:Data, keyData:Data) throws -> Data {
let keyLength = keyData.count
let validKeyLengths = [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256]
if (validKeyLengths.contains(keyLength) == false) {
throw AESError.KeyError(("Invalid key length", keyLength))
}
let ivSize = kCCBlockSizeAES128;
let cryptLength = size_t(ivSize + data.count + kCCBlockSizeAES128)
var cryptData = Data(count:cryptLength)
let status = cryptData.withUnsafeMutableBytes {ivBytes in
SecRandomCopyBytes(kSecRandomDefault, kCCBlockSizeAES128, ivBytes)
}
if (status != 0) {
throw AESError.IVError(("IV generation failed", Int(status)))
}
var numBytesEncrypted :size_t = 0
let options = CCOptions(kCCOptionPKCS7Padding)
let cryptStatus = cryptData.withUnsafeMutableBytes {cryptBytes in
data.withUnsafeBytes {dataBytes in
keyData.withUnsafeBytes {keyBytes in
CCCrypt(CCOperation(kCCEncrypt),
CCAlgorithm(kCCAlgorithmAES),
options,
keyBytes, keyLength,
cryptBytes,
dataBytes, data.count,
cryptBytes+kCCBlockSizeAES128, cryptLength,
&numBytesEncrypted)
}
}
}
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.count = numBytesEncrypted + ivSize
}
else {
throw AESError.CryptorError(("Encryption failed", Int(cryptStatus)))
}
return cryptData;
}
// The iv is prefixed to the encrypted data
func aesCBCDecrypt(data:Data, keyData:Data) throws -> Data? {
let keyLength = keyData.count
let validKeyLengths = [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256]
if (validKeyLengths.contains(keyLength) == false) {
throw AESError.KeyError(("Invalid key length", keyLength))
}
let ivSize = kCCBlockSizeAES128;
let clearLength = size_t(data.count - ivSize)
var clearData = Data(count:clearLength)
var numBytesDecrypted :size_t = 0
let options = CCOptions(kCCOptionPKCS7Padding)
let cryptStatus = clearData.withUnsafeMutableBytes {cryptBytes in
data.withUnsafeBytes {dataBytes in
keyData.withUnsafeBytes {keyBytes in
CCCrypt(CCOperation(kCCDecrypt),
CCAlgorithm(kCCAlgorithmAES128),
options,
keyBytes, keyLength,
dataBytes,
dataBytes+kCCBlockSizeAES128, clearLength,
cryptBytes, clearLength,
&numBytesDecrypted)
}
}
}
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
clearData.count = numBytesDecrypted
}
else {
throw AESError.CryptorError(("Decryption failed", Int(cryptStatus)))
}
return clearData;
}
用法示例:
let clearData = "clearData0123456".data(using:String.Encoding.utf8)!
let keyData = "keyData890123456".data(using:String.Encoding.utf8)!
print("clearData: \(clearData as NSData)")
print("keyData: \(keyData as NSData)")
var cryptData :Data?
do {
cryptData = try aesCBCEncrypt(data:clearData, keyData:keyData)
print("cryptData: \(cryptData! as NSData)")
}
catch (let status) {
print("Error aesCBCEncrypt: \(status)")
}
let decryptData :Data?
do {
let decryptData = try aesCBCDecrypt(data:cryptData!, keyData:keyData)
print("decryptData: \(decryptData! as NSData)")
}
catch (let status) {
print("Error aesCBCDecrypt: \(status)")
}
示例输出:
clearData: <636c6561 72446174 61303132 33343536>
keyData: <6b657944 61746138 39303132 33343536>
cryptData: <92c57393 f454d959 5a4d158f 6e1cd3e7 77986ee9 b2970f49 2bafcf1a 8ee9d51a bde49c31 d7780256 71837a61 60fa4be0>
decryptData: <636c6561 72446174 61303132 33343536>
备注:
CBC 模式示例代码的一个典型问题是它将随机 IV 的创建和共享留给了用户。此示例包括 IV 的生成、为加密数据添加前缀并在解密期间使用带前缀的 IV。这将临时用户从 CBC mode.
为了安全起见,加密的数据也应该有身份验证,这个示例代码没有提供,为了体积小并允许与其他平台更好的互操作性。
还缺少从密码导出密钥的密钥,建议使用 PBKDF2 文本密码用作密钥 material。
对于稳健的生产就绪多平台加密代码,请参阅 RNCryptor。
我的两分钱:
swift 4 / xcode 9 扩展数据:
extension Data{
func aesEncrypt( keyData: Data, ivData: Data, operation: Int) -> Data {
let dataLength = self.count
let cryptLength = size_t(dataLength + kCCBlockSizeAES128)
var cryptData = Data(count:cryptLength)
let keyLength = size_t(kCCKeySizeAES128)
let options = CCOptions(kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
let cryptStatus = cryptData.withUnsafeMutableBytes {cryptBytes in
self.withUnsafeBytes {dataBytes in
ivData.withUnsafeBytes {ivBytes in
keyData.withUnsafeBytes {keyBytes in
CCCrypt(CCOperation(operation),
CCAlgorithm(kCCAlgorithmAES),
options,
keyBytes, keyLength,
ivBytes,
dataBytes, dataLength,
cryptBytes, cryptLength,
&numBytesEncrypted)
}
}
}
}
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.removeSubrange(numBytesEncrypted..<cryptData.count)
} else {
print("Error: \(cryptStatus)")
}
return cryptData;
}
}
func testAES() -> Bool {
let message = "secret message"
let key = "key890123456"
let ivString = "abcdefghijklmnop" // 16 bytes for AES128
let messageData = message.data(using:String.Encoding.utf8)!
let keyData = key.data(using: .utf8)!
let ivData = ivString.data(using: .utf8)!
let encryptedData = messageData.aesEncrypt( keyData:keyData, ivData:ivData, operation:kCCEncrypt)
let decryptedData = encryptedData.aesEncrypt( keyData:keyData, ivData:ivData, operation:kCCDecrypt)
let decrypted = String(bytes:decryptedData, encoding:String.Encoding.utf8)!
return message == decrypted
}
找到一个名为 RNCryptor 的库,用 swift 语言实现,用于 AES 加密/解密。
可以使用 Cocoapods 或 Carthage 完成安装。 这是加密和解密的示例代码。
// Encryption
let data = "sample data string".data(using: String.Encoding.utf8)
let password = "Secret password"
let encryptedData = RNCryptor.encrypt(data: data, withPassword: password)
// Decryption
do {
let originalData = try RNCryptor.decrypt(data: encryptedData, withPassword: password)
// ...
} catch {
print(error)
}
我用过 CryptoSwift。
首先,我在 pod 文件中安装了 cryptoSwift。然后在我的视图控制器中我导入了 CryptoSwift。
这是我用过的代码:
let value = "xyzzy". // This is the value that we want to encrypt
let key = "abc". // This is the key
let EncryptedValue = try! value.aesEncrypt(key: key)
let DecryptedValue = try! EncryptedValue.aesDecrypt(key: key)
然后,使用字符串扩展:
extension String {
func aesEncrypt(key: String) throws -> String {
var result = ""
do {
let key: [UInt8] = Array(key.utf8) as [UInt8]
let aes = try! AES(key: key, blockMode: .ECB, padding: .pkcs5) // AES128 .ECB pkcs7
let encrypted = try aes.encrypt(Array(self.utf8))
result = encrypted.toBase64()!
print("AES Encryption Result: \(result)")
} catch {
print("Error: \(error)")
}
return result
}
func aesDecrypt(key: String) throws -> String {
var result = ""
do {
let encrypted = self
let key: [UInt8] = Array(key.utf8) as [UInt8]
let aes = try! AES(key: key, blockMode: .ECB, padding: .pkcs5) // AES128 .ECB pkcs7
let decrypted = try aes.decrypt(Array(base64: encrypted))
result = String(data: Data(decrypted), encoding: .utf8) ?? ""
print("AES Decryption Result: \(result)")
} catch {
print("Error: \(error)")
}
return result
}
}
在此我没有使用 iv 和 encrypted.toBase64() 来加密 result = encrypted.toBase64()!
代替加密 result = encrypted.toStringHex()!
中的
和解密类似 let decrypted = try aes.decrypt(Array(base64: encrypted))
代替 let decrypted = try aes.decrypt(Array(Hex: encrypted))
对于无法将字节数组转换为字符串的任何人
String(data: Data(decrypted), encoding: .utf8)
这是我的示例字符串扩展
extension String {
func decryptAES(key: String, iv: String) -> String {
do {
let encrypted = self
let key = Array(key.utf8)
let iv = Array(iv.utf8)
let aes = try AES(key: key, blockMode: CTR(iv: iv), padding: .noPadding)
let decrypted = try aes.decrypt(Array(hex: encrypted))
return String(data: Data(decrypted), encoding: .utf8) ?? ""
} catch {
return "Error: \(error)"
}
}
}
Swift 5
我重构了@ingconti 的代码。
import Foundation
import CommonCrypto
struct AES {
// MARK: - Value
// MARK: Private
private let key: Data
private let iv: Data
// MARK: - Initialzier
init?(key: String, iv: String) {
guard key.count == kCCKeySizeAES128 || key.count == kCCKeySizeAES256, let keyData = key.data(using: .utf8) else {
debugPrint("Error: Failed to set a key.")
return nil
}
guard iv.count == kCCBlockSizeAES128, let ivData = iv.data(using: .utf8) else {
debugPrint("Error: Failed to set an initial vector.")
return nil
}
self.key = keyData
self.iv = ivData
}
// MARK: - Function
// MARK: Public
func encrypt(string: String) -> Data? {
return crypt(data: string.data(using: .utf8), option: CCOperation(kCCEncrypt))
}
func decrypt(data: Data?) -> String? {
guard let decryptedData = crypt(data: data, option: CCOperation(kCCDecrypt)) else { return nil }
return String(bytes: decryptedData, encoding: .utf8)
}
func crypt(data: Data?, option: CCOperation) -> Data? {
guard let data = data else { return nil }
let cryptLength = data.count + key.count
var cryptData = Data(count: cryptLength)
var bytesLength = Int(0)
let status = cryptData.withUnsafeMutableBytes { cryptBytes in
data.withUnsafeBytes { dataBytes in
iv.withUnsafeBytes { ivBytes in
key.withUnsafeBytes { keyBytes in
CCCrypt(option, CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionPKCS7Padding), keyBytes.baseAddress, key.count, ivBytes.baseAddress, dataBytes.baseAddress, data.count, cryptBytes.baseAddress, cryptLength, &bytesLength)
}
}
}
}
guard Int32(status) == Int32(kCCSuccess) else {
debugPrint("Error: Failed to crypt data. Status \(status)")
return nil
}
cryptData.removeSubrange(bytesLength..<cryptData.count)
return cryptData
}
}
这样使用
let password = "UserPassword1!"
let key128 = "1234567890123456" // 16 bytes for AES128
let key256 = "12345678901234561234567890123456" // 32 bytes for AES256
let iv = "abcdefghijklmnop" // 16 bytes for AES128
let aes128 = AES(key: key128, iv: iv)
let aes256 = AES(key: key256, iv: iv)
let encryptedPassword128 = aes128?.encrypt(string: password)
aes128?.decrypt(data: encryptedPassword128)
let encryptedPassword256 = aes256?.encrypt(string: password)
aes256?.decrypt(data: encryptedPassword256)
结果
基于@zaph 的出色回答,我创建了这个 Playground 用于:
Swift 5
import Foundation
import CommonCrypto
protocol Cryptable {
func encrypt(_ string: String) throws -> Data
func decrypt(_ data: Data) throws -> String
}
struct AES {
private let key: Data
private let ivSize: Int = kCCBlockSizeAES128
private let options: CCOptions = CCOptions(kCCOptionPKCS7Padding)
init(keyString: String) throws {
guard keyString.count == kCCKeySizeAES256 else {
throw Error.invalidKeySize
}
self.key = Data(keyString.utf8)
}
}
extension AES {
enum Error: Swift.Error {
case invalidKeySize
case generateRandomIVFailed
case encryptionFailed
case decryptionFailed
case dataToStringFailed
}
}
private extension AES {
func generateRandomIV(for data: inout Data) throws {
try data.withUnsafeMutableBytes { dataBytes in
guard let dataBytesBaseAddress = dataBytes.baseAddress else {
throw Error.generateRandomIVFailed
}
let status: Int32 = SecRandomCopyBytes(
kSecRandomDefault,
kCCBlockSizeAES128,
dataBytesBaseAddress
)
guard status == 0 else {
throw Error.generateRandomIVFailed
}
}
}
}
extension AES: Cryptable {
func encrypt(_ string: String) throws -> Data {
let dataToEncrypt = Data(string.utf8)
let bufferSize: Int = ivSize + dataToEncrypt.count + kCCBlockSizeAES128
var buffer = Data(count: bufferSize)
try generateRandomIV(for: &buffer)
var numberBytesEncrypted: Int = 0
do {
try key.withUnsafeBytes { keyBytes in
try dataToEncrypt.withUnsafeBytes { dataToEncryptBytes in
try buffer.withUnsafeMutableBytes { bufferBytes in
guard let keyBytesBaseAddress = keyBytes.baseAddress,
let dataToEncryptBytesBaseAddress = dataToEncryptBytes.baseAddress,
let bufferBytesBaseAddress = bufferBytes.baseAddress else {
throw Error.encryptionFailed
}
let cryptStatus: CCCryptorStatus = CCCrypt( // Stateless, one-shot encrypt operation
CCOperation(kCCEncrypt), // op: CCOperation
CCAlgorithm(kCCAlgorithmAES), // alg: CCAlgorithm
options, // options: CCOptions
keyBytesBaseAddress, // key: the "password"
key.count, // keyLength: the "password" size
bufferBytesBaseAddress, // iv: Initialization Vector
dataToEncryptBytesBaseAddress, // dataIn: Data to encrypt bytes
dataToEncryptBytes.count, // dataInLength: Data to encrypt size
bufferBytesBaseAddress + ivSize, // dataOut: encrypted Data buffer
bufferSize, // dataOutAvailable: encrypted Data buffer size
&numberBytesEncrypted // dataOutMoved: the number of bytes written
)
guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
throw Error.encryptionFailed
}
}
}
}
} catch {
throw Error.encryptionFailed
}
let encryptedData: Data = buffer[..<(numberBytesEncrypted + ivSize)]
return encryptedData
}
func decrypt(_ data: Data) throws -> String {
let bufferSize: Int = data.count - ivSize
var buffer = Data(count: bufferSize)
var numberBytesDecrypted: Int = 0
do {
try key.withUnsafeBytes { keyBytes in
try data.withUnsafeBytes { dataToDecryptBytes in
try buffer.withUnsafeMutableBytes { bufferBytes in
guard let keyBytesBaseAddress = keyBytes.baseAddress,
let dataToDecryptBytesBaseAddress = dataToDecryptBytes.baseAddress,
let bufferBytesBaseAddress = bufferBytes.baseAddress else {
throw Error.encryptionFailed
}
let cryptStatus: CCCryptorStatus = CCCrypt( // Stateless, one-shot encrypt operation
CCOperation(kCCDecrypt), // op: CCOperation
CCAlgorithm(kCCAlgorithmAES128), // alg: CCAlgorithm
options, // options: CCOptions
keyBytesBaseAddress, // key: the "password"
key.count, // keyLength: the "password" size
dataToDecryptBytesBaseAddress, // iv: Initialization Vector
dataToDecryptBytesBaseAddress + ivSize, // dataIn: Data to decrypt bytes
bufferSize, // dataInLength: Data to decrypt size
bufferBytesBaseAddress, // dataOut: decrypted Data buffer
bufferSize, // dataOutAvailable: decrypted Data buffer size
&numberBytesDecrypted // dataOutMoved: the number of bytes written
)
guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
throw Error.decryptionFailed
}
}
}
}
} catch {
throw Error.encryptionFailed
}
let decryptedData: Data = buffer[..<numberBytesDecrypted]
guard let decryptedString = String(data: decryptedData, encoding: .utf8) else {
throw Error.dataToStringFailed
}
return decryptedString
}
}
do {
let aes = try AES(keyString: "FiugQTgPNwCWUY,VhfmM4cKXTLVFvHFe")
let stringToEncrypt: String = "please encrypt meeee"
print("String to encrypt:\t\t\t\(stringToEncrypt)")
let encryptedData: Data = try aes.encrypt(stringToEncrypt)
print("String encrypted (base64):\t\(encryptedData.base64EncodedString())")
let decryptedData: String = try aes.decrypt(encryptedData)
print("String decrypted:\t\t\t\(decryptedData)")
} catch {
print("Something went wrong: \(error)")
}
输出:
我还基于它创建了一个Swift包:
我知道这是一个老问题。
自 2019 年起您可以使用 Apple 的 CryptoKit
。它介绍了 AES.GCM
import CryptoKit
let key = SymmetricKey(size: .bits256)
let data = Data(...)
do {
let enc = try AES.GCM.seal(data, using: key).combined!
let dec = try AES.GCM.open(try AES.GCM.SealedBox(combined: data), using: key))
} catch {...}
此外,我还制作了一个有用的 swift 包,扩展了 CryptoKit 以允许 AES.CBC
加密 (https://github.com/gal-yedidovich/CryptoExtensions)
那么,就import CBC
import CryptoKit
import CBC
let data: Data = ... //some data to encrypt
let iv: Data = ... //an initial vector
let key: SymmetricKey = ... //encryption key
//one shot crypto operation
do {
let encrypted = try AES.CBC.encrypt(data, using: key, iv: iv)
let decrypted = try AES.CBC.decrypt(encrypted, using: key, iv: iv)
} catch {...}
//using cipher
do {
let cipher = try AES.CBC.Cipher(.encrypt, using: key, iv: iv)
var enc = Data()
enc += try cipher.update(...)
enc += try cipher.update(...)
enc += try cipher.finalize()
} catch {...}
我正在寻找使用 PKC5 填充的 AES 加密 ECB 模式而不使用任何 pod。通过收集不同的信息,我找到了解决问题的正确方法。也许对其他人有帮助。
注意:PKCS5 和 PKCS7 填充没有区别。
import CommonCrypto
func encryptionAESModeECB(messageData data: Data, key: String) -> Data? {
guard let keyData = key.data(using: String.Encoding.utf8) else { return nil }
guard let cryptData = NSMutableData(length: Int((data.count)) + kCCBlockSizeAES128) else { return nil }
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
let iv: String = ""
var numBytesEncrypted: size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
(keyData as NSData).bytes, keyLength,
iv,
(data as NSData).bytes, data.count,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
let encryptedString = cryptData.base64EncodedString(options: .lineLength64Characters)
return encryptedString.data(using: .utf8)
} else {
return nil
}
}
func decryptionAESModeECB(messageData: Data, key: String) -> Data? {
guard let messageString = String(data: messageData, encoding: .utf8) else { return nil }
guard let data = Data(base64Encoded: messageString, options: .ignoreUnknownCharacters) else { return nil }
guard let keyData = key.data(using: String.Encoding.utf8) else { return nil }
guard let cryptData = NSMutableData(length: Int((data.count)) + kCCBlockSizeAES128) else { return nil }
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCDecrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
let iv: String = ""
var numBytesEncrypted: size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
(keyData as NSData).bytes, keyLength,
iv,
(data as NSData).bytes, data.count,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
return cryptData as Data
} else {
return nil
}
}
这样使用:
let encryptedData = encryptionAESModeECB(messageData: data, key: "keyString")
let decryptedData = decryptionAESModeECB(messageData: data, key: "keyString")
这个扩展很容易使用,也支持所有平台的解密。
import CryptoSwift
extension String {
func aesEncrypt() throws -> String {
do {
let encrypted = try AES(key: "your_encryption_key", iv: "your_iv", padding: .pkcs7).encrypt([UInt8](self.data(using: .utf8)!))
return Data(encrypted).base64EncodedString()
} catch {
}
return ""
}
func aesDecrypt() throws -> String {
do {
guard let data = Data(base64Encoded: self) else { return "" }
let decrypted = try AES(key: "your_encryption_key", iv: "your_iv", padding: .pkcs7).decrypt([UInt8](data))
return decrypted
} catch {
}
return ""
}
}