Проблема с использованием CCCrypt (CommonCrypt) в Swift
Я портирую рабочую категорию Objective-C (NSData+AESCrypt.m) на Swift и обнаружил проблему с указателями. Код для части шифрования в Swift компилируется правильно, но генерирует ошибку EXEC_BAD_ACCES во время выполнения.
Код, который у меня есть, таков (я постарался максимально разбирать код):
let key = "123456789012345678901234567890120"
let keyLength = UInt(kCCKeySizeAES256 + 1)
let keyPointer = strdup(key) // Convert key to <UnsafeMutablePointer<Int8>
let message = "Don´t try to read this text. Top Secret Stuff"
let data = (message as NSString).dataUsingEncoding(NSUTF8StringEncoding)
let dataBytes = data?.bytes
let length = data?.length
let dataLength = UInt(length!)
let dataPointer = UnsafePointer<UInt8>(dataBytes!)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
let cryptBufferSize = UInt(dataLength + kCCBlockSizeAES128)
var cryptBuffer = [UInt8](count: Int(cryptBufferSize), repeatedValue: 0)
var cryptBufferPointer = UnsafeMutablePointer<UInt8>(cryptBuffer)
var numBytesEncrypted = UnsafeMutablePointer<UInt>()
var cryptStatus = CCCrypt(operation, algoritm, options, keyPointer, keyLength, nil, dataPointer, dataLength, cryptBufferPointer, cryptBufferSize, numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
let size = NSInteger(cryptBufferSize)
let encryptedData = NSData(bytes: cryptBufferPointer, length: size)
let encryptedString = NSString(data: encryptedData, encoding: NSUTF8StringEncoding)
println("Encrypted String = \(encryptedString)") // EXEC_BAD_ACCESS error
} else {
println("Error: \(cryptStatus)")
}
Объект encryptedData показывает следующую информацию:
<279c2d0f d3ce2200 0dc10cc1 9df46e76 cb26f423 7c9bde76 f9d8d0e2 632acef9 74fb0614 4717422b 684d1889 e3ce882c 00000000 00000000 00000000 0000>
Но зашифрованная строка показывает 0x0000000000
в отладчике, и пытается println()
генерирует ошибку EXEC_BAD_ACCESS
Есть идеи, чего не хватает?
Rgds....
2 ответа
Swift 2.0
Вот пример
Если это не совсем то, что нужно, методы должны быть хорошим примером.
Примечание: ключевая строка преобразуется в данные
Добавить Security.framework в проект
добавлять #import <CommonCrypto/CommonCryptor.h>
к соединительному заголовку.
let keyString = "12345678901234567890123456789012"
let keyData: NSData! = (keyString as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
print("keyLength = \(keyData.length), keyData = \(keyData)")
let message = "Don´t try to read this text. Top Secret Stuff"
let data: NSData! = (message as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
print("data length = \(data.length), data = \(data)")
let cryptData = NSMutableData(length: Int(data.length) + kCCBlockSizeAES128)!
let keyLength = size_t(kCCKeySizeAES256)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
var cryptStatus = CCCrypt(operation,
algoritm,
options,
keyData.bytes, keyLength,
nil,
data.bytes, data.length,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
print("cryptLength = \(numBytesEncrypted), cryptData = \(cryptData)")
// Not all data is a UTF-8 string so Base64 is used
let base64cryptString = cryptData.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
print("base64cryptString = \(base64cryptString)")
} else {
print("Error: \(cryptStatus)")
}
Выход:
keyLength = 32, keyData = <31323334 35363738 39303132 33343536 37383930 31323334 35363738 39303132>
dataLength = 46, data = <446f6ec2 b4742074 72792074 6f207265 61642074 68697320 74657874 2e20546f 70205365 63726574 20537475 6666>
cryptLength = 48, cryptData = <118a32dc c23f7caa 883abc3c 1c7f0770 e200016b 2737acfa 17bb96fb a02b02a7 c147603b 06acd863 94bb8ff2 6cb14515>
base64cryptString = EYoy3MI/fKqIOrw8HH8HcOIAAWsnN6z6F7uW+6ArAqfBR2A7BqzYY5S7j/JssUUV
Свифт 3
IV имеет префикс к зашифрованным данным
aesCBC128Encrypt
создаст случайный IV и префикс к зашифрованному коду.aesCBC128Decrypt
будет использовать префикс IV во время расшифровки.
Входные данные - это данные, а ключ - объекты данных. Если закодированная форма, такая как Base64, если требуется, преобразуйте в и / или из вызывающего метода.
Ключ должен иметь длину 128 бит (16 байтов), 192 бит (24 байта) или 256 бит (32 байта). Если используется другой размер ключа, будет выдано сообщение об ошибке.
PKCS # 7 заполнение установлено по умолчанию.
Этот пример требует Common Crypto
Необходимо иметь соединительный заголовок к проекту:#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.
В целях безопасности зашифрованные данные также должны иметь аутентификацию, этот пример кода не обеспечивает ее для того, чтобы быть маленькой и обеспечивать лучшую совместимость для других платформ.
Также отсутствует вывод ключа из пароля, предлагается использовать PBKDF2, если в качестве ключевого материала используются текстовые пароли.
Для надежного готового к использованию многоплатформенного кода шифрования см. RNCryptor.
Swift 2.0 Шифрование и дешифрование. Только что обновил ответ выше для поддержки Swift 2.0.
static func AES128Encryption()
{
let keyString = "12345678901234567890123456789012"
let keyData: NSData! = (keyString as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
let keyBytes = UnsafeMutablePointer<Void>(keyData.bytes)
print("keyLength = \(keyData.length), keyData = \(keyData)")
let message = "Don´t try to read this text. Top Secret Stuff"
let data: NSData! = (message as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
let dataLength = size_t(data.length)
let dataBytes = UnsafeMutablePointer<Void>(data.bytes)
print("dataLength = \(dataLength), data = \(data)")
let cryptData = NSMutableData(length: Int(dataLength) + kCCBlockSizeAES128)
let cryptPointer = UnsafeMutablePointer<Void>(cryptData!.mutableBytes)
let cryptLength = size_t(cryptData!.length)
let keyLength = size_t(kCCKeySizeAES256)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionPKCS7Padding + kCCOptionECBMode)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
keyBytes, keyLength,
nil,
dataBytes, dataLength,
cryptPointer, cryptLength,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
// let x: UInt = numBytesEncrypted
cryptData!.length = Int(numBytesEncrypted)
print("cryptLength = \(numBytesEncrypted), cryptData = \(cryptData)")
// Not all data is a UTF-8 string so Base64 is used
let base64cryptString = cryptData!.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
print("base64cryptString = \(base64cryptString)")
} else {
print("Error: \(cryptStatus)")
}
}
static func AES128Decryption(data:NSData) //data = cryptData
{
let keyString = "12345678901234567890123456789012"
let keyData: NSData! = (keyString as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
let keyBytes = UnsafeMutablePointer<Void>(keyData.bytes)
print("keyLength = \(keyData.length), keyData = \(keyData)")
//let message = "Don´t try to read this text. Top Secret Stuff"
// let data: NSData! = (message as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
let dataLength = size_t(data.length)
let dataBytes = UnsafeMutablePointer<Void>(data.bytes)
print("dataLength = \(dataLength), data = \(data)")
let cryptData = NSMutableData(length: Int(dataLength) + kCCBlockSizeAES128)
let cryptPointer = UnsafeMutablePointer<Void>(cryptData!.mutableBytes)
let cryptLength = size_t(cryptData!.length)
let keyLength = size_t(kCCKeySizeAES256)
let operation: CCOperation = UInt32(kCCDecrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionPKCS7Padding + kCCOptionECBMode)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
keyBytes, keyLength,
nil,
dataBytes, dataLength,
cryptPointer, cryptLength,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
// let x: UInt = numBytesEncrypted
cryptData!.length = Int(numBytesEncrypted)
print("DecryptcryptLength = \(numBytesEncrypted), Decrypt = \(cryptData)")
// Not all data is a UTF-8 string so Base64 is used
let base64cryptString = cryptData!.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
print("base64DecryptString = \(base64cryptString)")
print( "utf8 actual string = \(NSString(data: cryptData!, encoding: NSUTF8StringEncoding))");
} else {
print("Error: \(cryptStatus)")
}
}
Zaph адаптирован к современному быстрому (еще одно осуждение еще одного быстрого API вызывает еще один набор предупреждений, о которых никто не заботится, даже если они должны)
extension Data
{
func aes256CBCEncrypt(keyData: Data) -> Data? {
let keyLength = keyData.count
if keyData.count != kCCKeySizeAES256 {
return nil
}
let data = self;
let ivSize = kCCBlockSizeAES128;
let cryptLength = size_t(ivSize + data.count + kCCBlockSizeAES128)
var cryptData = Data(count:cryptLength)
let status: Int32 = cryptData.withUnsafeMutableBytes {
guard let ivBytes = $0.baseAddress else {
return 0
}
return SecRandomCopyBytes(kSecRandomDefault, kCCBlockSizeAES128, ivBytes)
}
if (status != 0) {
// who cares, really? return nil
}
var numBytesEncrypted :size_t = 0
let options = CCOptions(kCCOptionPKCS7Padding)
let cryptStatus: CCCryptorStatus = cryptData.withUnsafeMutableBytes {
guard let cryptBytes = $0.baseAddress else {
return CCCryptorStatus(kCCMemoryFailure) // sadly kCCMoronSwiftLanguageSpecDeveloperDetected does not exist
}
let cryptStatus: CCCryptorStatus = data.withUnsafeBytes {
guard let dataBytes = $0.baseAddress else {
return CCCryptorStatus(kCCMemoryFailure)
}
return keyData.withUnsafeBytes {
guard let keyBytes = $0.baseAddress else {
return CCCryptorStatus(kCCMemoryFailure)
}
return CCCrypt(CCOperation(kCCEncrypt),
CCAlgorithm(kCCAlgorithmAES),
options,
keyBytes, keyLength,
cryptBytes,
dataBytes, data.count,
cryptBytes+kCCBlockSizeAES128, cryptLength,
&numBytesEncrypted)
}
}
return cryptStatus
}
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.count = numBytesEncrypted + ivSize
}
else {
return nil
}
return cryptData;
}
}