我正在尝试快速实现 AES 加密。 Android和C#的加密解密工作正常。我需要 swift 实现它。这是current code对于 android 和 C# 后面跟着这个。
我试过
但都没有用。当我在服务器上发送加密字符串时,它没有被解密。
任何帮助将不胜感激
最佳答案
一定要使用相同的参数,这些参数似乎是 AES 和 CBC 模式,带有 iv、PKCS5Padding
(实际上是 PKCS#7)填充和 16 字节(128 位) key 。
PKCS#5 填充和 PKCS#7 填充本质上是相同的,有时由于历史原因,PKCS#5 填充被指定用于 AES,但实际填充是 PKCS#7。
确保 key 、iv 和加密数据的编码都匹配。十六进制将它们转储到两个平台上以确保它们相同。加密函数并不难用,只要输入参数正确,输出就正确。
为了使这更安全,iv 应该是随机字节并添加到加密数据之前,以便在解密期间使用。
跨平台 AES 加密使用 256 位 key ,因此无法按原样工作。
例子:
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 模式下使用随机 IV 的 AES 加密 (Swift 3+)
iv是加密数据的前缀
aesCBC128Encrypt
将创建一个随机 IV 并作为加密代码的前缀。
aesCBC128Decrypt
将在解密期间使用带前缀的 IV。
输入是数据,键是数据对象。如果需要在调用方法中转换为和/或自 Base64 等编码形式。
key 的长度应恰好为 128 位(16 字节)、192 位(24 字节)或 256 位(32 字节)。如果使用其他 key 大小,则会抛出错误。
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 所需的详细信息中解放出来。
为了安全起见,加密的数据也应该有身份验证,这个示例代码没有提供,为了体积小并允许与其他平台更好的互操作性。
还缺少从密码中导出 key 的 key ,建议使用 PBKDF2 将文本密码用作 key Material 。
对于健壮的生产就绪的多平台加密代码,请参阅 RNCryptor。
关于ios - swift 中的 AES 加密,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37680361/