aes - Swift 5 + kCCDecrypt (CommonCrypto):解密失败
问题描述
Swift 5
基于大量其他类似问题,尝试在 中编写我自己的加密/解密函数——结果惨遭失败。
我正在使用CommonCrypto
+CCCrypt
加密/解密(AES,256 密钥,随机 iv)。
我赞成(这太令人困惑NSData.bytes
了)。withUnsafeBytes
Swift 5
我的encrypt
功能是这样的:
func encrypt(_ string: String) throws -> Data {
guard let dataToEncrypt: Data = string.data(using: .utf8) else {
throw AESError.stringToDataFailed
}
// Seems like the easiest way to avoid the `withUnsafeBytes` mess is to use NSData.bytes.
let dataToEncryptNSData = NSData(data: dataToEncrypt)
let bufferSize: Int = ivSize + dataToEncryptNSData.length + kCCBlockSizeAES128
let buffer = UnsafeMutablePointer<NSData>.allocate(capacity: bufferSize)
defer { buffer.deallocate() }
let status: Int32 = SecRandomCopyBytes(
kSecRandomDefault,
kCCBlockSizeAES128,
buffer
)
guard status == 0 else {
throw AESError.generateRandomIVFailed
}
var numberBytesEncrypted: Int = 0
let cryptStatus: CCCryptorStatus = CCCrypt( // Stateless, one-shot encrypt operation
CCOperation(kCCEncrypt), // op: CCOperation
CCAlgorithm(kCCAlgorithmAES), // alg: CCAlgorithm
options, // options: CCOptions
key.bytes, // key: the "password"
key.length, // keyLength: the "password" size
buffer, // iv: Initialization Vector
dataToEncryptNSData.bytes, // dataIn: Data to encrypt bytes
dataToEncryptNSData.length, // dataInLength: Data to encrypt size
buffer + kCCBlockSizeAES128, // dataOut: encrypted Data buffer
bufferSize, // dataOutAvailable: encrypted Data buffer size
&numberBytesEncrypted // dataOutMoved: the number of bytes written
)
guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
throw AESError.encryptDataFailed
}
return Data(bytes: buffer, count: numberBytesEncrypted + ivSize)
}
decrypt
功能:
func decrypt(_ data: Data) throws -> String {
// Seems like the easiest way to avoid the `withUnsafeBytes` mess is to use NSData.bytes.
let dataToDecryptNSData = NSData(data: data)
let bufferSize: Int = dataToDecryptNSData.length - ivSize
let buffer = UnsafeMutablePointer<NSData>.allocate(capacity: bufferSize)
defer { buffer.deallocate() }
var numberBytesDecrypted: Int = 0
let cryptStatus: CCCryptorStatus = CCCrypt( // Stateless, one-shot encrypt operation
CCOperation(kCCDecrypt), // op: CCOperation
CCAlgorithm(kCCAlgorithmAES128), // alg: CCAlgorithm
options, // options: CCOptions
key.bytes, // key: the "password"
key.length, // keyLength: the "password" size
dataToDecryptNSData.bytes, // iv: Initialization Vector
dataToDecryptNSData.bytes + kCCBlockSizeAES128, // dataIn: Data to decrypt bytes
bufferSize, // dataInLength: Data to decrypt size
buffer, // dataOut: decrypted Data buffer
bufferSize, // dataOutAvailable: decrypted Data buffer size
&numberBytesDecrypted // dataOutMoved: the number of bytes written
)
guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
throw AESError.decryptDataFailed
}
let decryptedData = Data(bytes: buffer, count: numberBytesDecrypted)
guard let decryptedString = String(data: decryptedData, encoding: .utf8) else {
throw AESError.dataToStringFailed
}
return decryptedString
}
这些是基于用户“@zaph”的这个很棒的答案。
虽然encrypt
似乎正在工作,但decrypt
失败了。
这一行具体:
guard let decryptedString = String(data: decryptedData, encoding: .utf8) else {
throw AESError.dataToStringFailed
}
所以当然我错过了一些东西,但我无法弄清楚它是什么。您可以...吗?
这是包含整个代码的 pastebin,您可以将其复制/粘贴到 Playground 中并点击播放。Swift 5
是必需的:https ://pastebin.com/raw/h6gacaHX
更新
我现在遵循@OOper 建议的方法。最终代码可以在这里看到:
https ://github.com/backslash-f/aescryptable
解决方案
事实上,Data.withUnsafeBytes
在 Swift 5 中使用有点乱,虽然这NSData.bytes
不是最简单的方法,因为使用它有时看起来可行,但有时却不行。
如果您想Data.withUnsafeBytes
在 Swift 中使用Data
.
struct AES {
private let key: Data //<- Use `Data` instead of `NSData`
private let ivSize: Int = kCCBlockSizeAES128
private let options: CCOptions = CCOptions(kCCOptionPKCS7Padding)
init(keyString: String) throws {
guard keyString.count == kCCKeySizeAES256 else {
throw AESError.invalidKeySize
}
guard let keyData: Data = keyString.data(using: .utf8) else {
throw AESError.stringToDataFailed
}
self.key = keyData
}
}
extension AES: Cryptable {
func encrypt(_ string: String) throws -> Data {
guard let dataToEncrypt: Data = string.data(using: .utf8) else {
throw AESError.stringToDataFailed
}
let bufferSize: Int = ivSize + dataToEncrypt.count + kCCBlockSizeAES128
var buffer = Data(count: bufferSize)
let status: Int32 = buffer.withUnsafeMutableBytes {bytes in
SecRandomCopyBytes(
kSecRandomDefault,
kCCBlockSizeAES128,
bytes.baseAddress!
)
}
guard status == 0 else {
throw AESError.generateRandomIVFailed
}
var numberBytesEncrypted: Int = 0
let cryptStatus: CCCryptorStatus = key.withUnsafeBytes {keyBytes in
dataToEncrypt.withUnsafeBytes {dataBytes in
buffer.withUnsafeMutableBytes {bufferBytes in
CCCrypt( // Stateless, one-shot encrypt operation
CCOperation(kCCEncrypt), // op: CCOperation
CCAlgorithm(kCCAlgorithmAES), // alg: CCAlgorithm
options, // options: CCOptions
keyBytes.baseAddress, // key: the "password"
key.count, // keyLength: the "password" size
bufferBytes.baseAddress, // iv: Initialization Vector
dataBytes.baseAddress, // dataIn: Data to encrypt bytes
dataToEncrypt.count, // dataInLength: Data to encrypt size
bufferBytes.baseAddress! + kCCBlockSizeAES128, // dataOut: encrypted Data buffer
bufferSize, // dataOutAvailable: encrypted Data buffer size
&numberBytesEncrypted // dataOutMoved: the number of bytes written
)
}
}
}
guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
throw AESError.encryptDataFailed
}
return buffer[..<(numberBytesEncrypted + ivSize)]
}
func decrypt(_ data: Data) throws -> String {
let bufferSize: Int = data.count - ivSize
var buffer = Data(count: bufferSize)
var numberBytesDecrypted: Int = 0
let cryptStatus: CCCryptorStatus = key.withUnsafeBytes {keyBytes in
data.withUnsafeBytes {dataBytes in
buffer.withUnsafeMutableBytes {bufferBytes in
CCCrypt( // Stateless, one-shot encrypt operation
CCOperation(kCCDecrypt), // op: CCOperation
CCAlgorithm(kCCAlgorithmAES128), // alg: CCAlgorithm
options, // options: CCOptions
keyBytes.baseAddress, // key: the "password"
key.count, // keyLength: the "password" size
dataBytes.baseAddress, // iv: Initialization Vector
dataBytes.baseAddress! + kCCBlockSizeAES128, // dataIn: Data to decrypt bytes
bufferSize, // dataInLength: Data to decrypt size
bufferBytes.baseAddress, // dataOut: decrypted Data buffer
bufferSize, // dataOutAvailable: decrypted Data buffer size
&numberBytesDecrypted // dataOutMoved: the number of bytes written
)
}
}
}
guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
throw AESError.decryptDataFailed
}
let decryptedData = buffer[..<numberBytesDecrypted]
guard let decryptedString = String(data: decryptedData, encoding: .utf8) else {
throw AESError.dataToStringFailed
}
return decryptedString
}
}
推荐阅读
- python - 升级 seaborn 但运行 sns.__version__ 时版本仍然相同
- python - CUPY:矩阵向量乘法比向量向量乘法和小尺寸的 l2norm 更快
- swift - SwiftUI 在打开地图时显示特定地标
- c# - EF Core 代码首次迁移:“无法确定一对一关系的子/依赖方”
- html - 表格标题和正文布局
- ruby-on-rails - ActiveAdmin 嵌套 has_many
- node.js - 我在使用 AWS SES 发送电子邮件时收到“ConfigError:Missing region in config”
- kubernetes - 设置 Kubernetes 时,为什么我的 Gitlab GUI 看不到 Helm Tiller 应用程序?
- jquery - 如何选择写为标签的文本的第一个字母并更改它的css?
- c - 嵌套并行:为什么只有主线程运行并执行并行 for 循环四次?