Trying to write my own encrypt/decrypt functions in Swift 5, based on tons of other similar questions -- and failing miserably.
I'm using CommonCrypto + CCCrypt to encrypt/decrypt (AES, 256 key, random iv).
I'm favouring NSData.bytes over withUnsafeBytes (which is just too confusing in Swift 5).
My encrypt function goes like this:
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)
}
The decrypt function:
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
}
Those were based on this awesome answer from the user "#zaph".
Although encrypt seems to be working, decrypt fails.
This line specifically:
guard let decryptedString = String(data: decryptedData, encoding: .utf8) else {
throw AESError.dataToStringFailed
}
So of course I'm missing something, but I can't figure out what it is. Could you?
Here is the pastebin with the entire code, which you could copy/paste in a Playground and hit play. Swift 5 is required: https://pastebin.com/raw/h6gacaHX
Update
I'm now following #OOper's suggested approach. The final code can be seen here:
https://github.com/backslash-f/aescryptable
In fact, using Data.withUnsafeBytes is sort of a mess in Swift 5, though NSData.bytes cannot be the easiest way, as using it would sometimes seemingly work, and sometimes not.
You need to be accustomed work with Data.withUnsafeBytes if you want to write an always-works code in Swift with 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
}
}
Related
I am trying to decrypt the encrypted string with a key and iv with CBC no padding.
Am using 3Des algo with CBC no padding.Am getting nil value after decrypting the encrypted string.
let decryptedKey1: () = decrypt3Des(inputString: "31656F05CF3BEBBAC30DAF0D59689FFF", uniqueKey: "77FAA64817954DE1BA12C0C41B4A2128", ivString: "2203090543243693")
func decrypt3Des (inputString: String, uniqueKey: String, ivString: String) {
let inputData: Data = inputString.data (using: String.Encoding.utf8)!
// publicKey = privateKeyString
/* Using the process 3DES to decrypt(kCCKeySize3DES)*/
/* Uses algorithm kCCAlgorithm3DES*/
let keyData: Data = uniqueKey.data (using: String.Encoding.utf8)!
let keyBytes = UnsafeMutableRawPointer (mutating: (keyData as NSData) .bytes)
let keyLength = size_t (kCCKeySize3DES)
let dataLength = Int (inputData.count)
let dataBytes = UnsafeRawPointer ((inputData as NSData) .bytes)
let bufferData = NSMutableData (length: Int (dataLength) + kCCBlockSize3DES)!
let bufferPointer = UnsafeMutableRawPointer (bufferData.mutableBytes)
let bufferLength = size_t (bufferData.length)
var bytesDecrypted = Int (0)
let cryptStatus = CCCrypt(
UInt32 (kCCDecrypt),
UInt32 (kCCAlgorithm3DES),
UInt32 (kCCModeCBC),
keyBytes,
keyLength,
ivString,
dataBytes,
dataLength,
bufferPointer,
bufferLength,
&bytesDecrypted)
if Int32 (cryptStatus) == Int32 (kCCSuccess) {
bufferData.length = bytesDecrypted
print("DecryptcryptLength = \(bytesDecrypted), Decrypt = \(bufferData)")
let myResult : NSData = NSData(bytes: dataBytes, length: bytesDecrypted)
print("decrypt \(myResult)")
let stringResult = NSString(data: myResult as Data, encoding:String.Encoding.utf8.rawValue)
print(stringResult ?? "No string decription")
print(myResult ?? "result")
print( "utf8 actual string = \(NSString(data: bufferData as Data, encoding: String.Encoding.utf8.rawValue))");
let base64cryptString = bufferData.base64EncodedString(options: .lineLength64Characters)
print("BASE64",base64cryptString)
guard
let decryptedString = String(data: bufferData as Data, encoding: String.Encoding.utf8) else {
print("Error retrieving string")
return
}
// bufferData.length = bytesDecrypted
let clearDataAsString = NSString (data: bufferData as Data, encoding: String.Encoding.utf8.rawValue)
if clearDataAsString != nil {
print ("Decrypt content:\(clearDataAsString! as String)")
}
else{
print(clearDataAsString)
}
} else {
print ("Error in decryption process:\(cryptStatus)")
}
}
I'm making an Application with a java backend and a Swift front-end. Using a REST Api to move data. I wish to encrypt the data with AES 128 CBC. The encrypting method is working, but the decrypting method is not.
First off all, this is the Swift code for de AES encrypting and decrypting:
import Foundation
import CommonCrypto
struct AES {
private let key: Data
private let iv: Data
init?() {
let ivProduct: String = "dkghepfowntislqn"
let keyProduct: String = "2949382094230487"
guard keyProduct.count == kCCKeySizeAES128 || keyProduct.count == kCCKeySizeAES256, let keyData = keyProduct.data(using: .utf8) else {
debugPrint("Error: Failed to set a key.")
return nil
}
guard ivProduct.count == kCCBlockSizeAES128, let ivData = ivProduct.data(using: .utf8) else {
debugPrint("Error: Failed to set an initial vector.")
return nil
}
self.key = keyData
self.iv = ivData
}
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 + kCCBlockSizeAES128
var cryptData = Data(count: cryptLength)
let keyLength = key.count
let options = CCOptions(kCCOptionPKCS7Padding)
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), options, keyBytes.baseAddress, keyLength, ivBytes.baseAddress, dataBytes.baseAddress, data.count, cryptBytes.baseAddress, cryptLength, &bytesLength)
}
}
}
}
guard UInt32(status) == UInt32(kCCSuccess) else {
debugPrint("Error: Failed to crypt data. Status \(status)")
return nil
}
cryptData.removeSubrange(bytesLength..<cryptData.count)
return cryptData
}
}
The data is gathered from the REST API like so:
func getTestAllPayments(_ completion: #escaping ([Payment]) -> ()) {
let aes128 = AES()
if let url = URL(string: "\(localhostUrl)/payment") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let res = try JSONDecoder().decode([Payment].self, from: (data))
print(res.self)
completion(res)
return
} catch let error {
print(error)
}
}
}.resume()
}
}
Now for the problem. I've ran a couple of test:
first check if the encrypt and decrypt methods work together:
let aes128 = AES()
let dataEncrypt = aes128?.encrypt(string:"Hello") //Will be :lG7Bqk0nwx732eOQLAzhqQ==
let dataDecrypt = aes128?.decrypt(data:dataEncrypt) //Will be: "Hello"
print(dataDecrypt) --> //output = "Hello"
First test works like a charm. For the second test:
let aes128 = AES()
if let url = URL(string: "\(localhostUrl)/payment") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
print(String(data: data, encoding: .utf8)) //Output = lG7Bqk0nwx732eOQLAzhqQ==
let dataDecrypt = aes128?.decrypt(data: data)
print(dataDecrypt) --> //output = nil
This is where it goes wrong. When fetching the data with the exact same encoding string, it'll always return nil. Has it something to do with the data format that URLSession returns?
Trying to perform encryption and decryption using AES ( common crypto ) in swift, For some reason while decrypting the first 16 characters are getting trimmed off. Can anyone please look at the below playground code and suggest what could be going wrong here ?. The decodedString variable trims off the intial 16 characters of the original payloadString during decryption.
playground code
import UIKit
import CommonCrypto
class AESNew {
enum MyError: LocalizedError {
case first(message: String)
case second(message: String)
var errorDescription: String? { return "Some description here!" }
}
func encrypt(plainText: String, keyData:Data, iv: Data, options:Int = kCCOptionPKCS7Padding) -> Result<Data, Error> {
let plainData = Data(plainText.utf8)
if let cryptData = NSMutableData(length: Int((plainData.count)) + kCCBlockSizeAES128) {
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(options)
var numBytesEncrypted :size_t = 0
//let value = iv.withUnsafeBytes { $0.load(as: UInt32.self) }
let cryptResult = iv.withUnsafeBytes { (dataBytes : UnsafePointer<UInt8>) -> Result<Data, Error> in
print(dataBytes)
print(UnsafeRawPointer(dataBytes))
let cryptStatus = CCCrypt(operation,
algoritm,
options,
(keyData as NSData).bytes,
keyLength,
UnsafeRawPointer(dataBytes),
(plainData as NSData).bytes, plainData.count,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
let base64cryptString = cryptData.base64EncodedString(options: .lineLength64Characters)
print("base64cryptString: \(base64cryptString)")
return .success(cryptData as Data)
} else {
print("failure")
return .failure(MyError.first(message: "crypt failed"))
}
}
return cryptResult
}
return .failure(MyError.second(message: "no value failure"))
}
// The iv is prefixed to the encrypted data
func decrypt(data: Data, keyData: Data) throws -> Data? {
let keyLength = keyData.count
let validKeyLengths = [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256]
if validKeyLengths.contains(keyLength) == false {
print("validKeyLengths does not match")
}
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 {
print("Decryption failed")
}
return clearData
}
}
func randomGenerateBytes(count: Int) -> Data? {
let bytes = UnsafeMutableRawPointer.allocate(byteCount: count, alignment: 1)
defer { bytes.deallocate() }
let status = CCRandomGenerateBytes(bytes, count)
guard status == kCCSuccess else { return nil }
return Data(bytes: bytes, count: count)
}
var ivBytesNew = Data()
if let ivBytes = randomGenerateBytes(count: 16) {
ivBytesNew = ivBytes
} else {
print("randomGenerateBytes failed")
}
let keyString = "keyData890123456"
let keyData = "keyData890123456".data(using:String.Encoding.utf8)!
let payloadString = "asdflkasfdkaslfd12345"
let aesObject = AESNew()
let encrytedObject = aesObject.encrypt(plainText: payloadString, keyData: keyData, iv: ivBytesNew, options: kCCOptionPKCS7Padding)
var encryptedData = Data()
switch encrytedObject {
case .success(let encData):
print(encData)
encryptedData = encData
case .failure(let error):
print(error)
default:
print("enc failed")
}
if let decrypedData = try? aesObject.decrypt(data: encryptedData, keyData: keyData) {
if let decodedString = String(data: decrypedData, encoding: .utf8) {
print(decodedString) // 1234, first 16 characters are omitted.
} else {
print("conversion from data to string failed")
}
} else {
print("decryption failed")
}
Seems you have picked up two different kinds of encrypt and decrypt.
Do you see your decrypt does not have a parameter iv which is needed for AES decryption?
Your decrypt expects first 16 bytes of data as iv and the rest as encrypted data. But your encrypt returns only encrypted data.
Please try changing the line calling decrypt as follows:
if let decrypedData = try? aesObject.decrypt(data: ivBytesNew + encryptedData, keyData: keyData) {
Generally, your encrypt uses NSMutableData and handling bytes inappropriately. That is fragile and it might crash on some different context. I strongly recommend you not to use your current encrypt.
Anyway, you should make encrypt and decrypt consistent, or sort of symmetric, currently they are not.
I trying to write an encryption (AESCBC256) function based on CCCrypt and the CCCrypt is generating a random value.
for example, when I pass 1234567 to function it'll return "HlFP2rCmycZS1269Cm47Q==" or "TTuSJrBcsOmOCDHc5IQ8Dw==" for the same iv and Key.
here's the iv: b5f89591
and the key is : 366e9c1b4b2ed2b1daf751d7500aaa01
func encrypt(Value: String)->String{
let keyData = keyString.data(using: .utf8, allowLossyConversion: false)!
let iv = SecretKey.data(using: .utf8, allowLossyConversion: false)!
let message = SecretKey+Value
let data = message.data(using: .utf8, allowLossyConversion: false)!
let cryptData = NSMutableData(length: Int(data.count) + kCCBlockSizeAES128)!
let keyLength = size_t(kCCKeySizeAES256)
let operation: CCOperation = CCOperation(UInt32(kCCEncrypt))
let algoritm: CCAlgorithm = CCAlgorithm(UInt32(kCCAlgorithmAES128))
let options: CCOptions = CCOptions(UInt32(kCCOptionPKCS7Padding))
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
keyData.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> UnsafePointer<UInt8> in return bytes},
keyLength,
iv.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> UnsafePointer<UInt8> in return bytes},
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> UnsafePointer<UInt8> in return bytes},
data.count,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
return String(describing: cryptData.base64EncodedString(options: .lineLength64Characters))
}else{
return ""
}
}
Your IV is the wrong length. It's 8 bytes long, but AES requires a 16 byte IV, so it's reading random data out of memory for the other 8 bytes.
I have the following method that I want to decrypt. The following method Encrypts. What should I do to decrypt?
Code
PlainString = "5lg3yg7Q+Y8="
func tripleDesEncrypt(PlainString: String) -> String {
let keyData = "FIFEDCBA98765432104AABFD"
let PlainStringvar = PlainString
guard let data = PlainStringvar.data(using: .utf8) else {
return ""
}
let cryptData = NSMutableData(length: Int(data.count) + kCCBlockSize3DES)!
let keyLength = size_t(kCCKeySize3DES)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithm3DES)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(operation,
algoritm,
options,
keyData.bytes, keyLength,
nil,
data.bytes, data.count,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
var base64cryptString = cryptData.base64EncodedString(options: .lineLength64Characters)
return base64cryptString //// encodeString(str: base64cryptString)
} else {
print("Error: \(cryptStatus)")
}
return ""
}
I never did encrypt/decrypt on Swift but based on that code you just need to change this line
let operation: CCOperation = UInt32(kCCEncrypt)
to
let operation: CCOperation = UInt32(kCCDecrypt)
While making a small search for this answer I came across this
https://github.com/DigitalLeaves/CommonCrypto-in-Swift
It might be useful, not only as a library that you can use, but to study code and also looks that you're new to the Swift language so I advise you to take a look
Your tripleDesEncrypt does not compile in Swift 4, so I needed to fix it:
func tripleDesEncrypt(plainString: String) -> String {
let keyData = "FIFEDCBA98765432104AABFD".data(using: .utf8)!
//Converting plainString to UTF-8 encoded data
guard let data = plainString.data(using: .utf8) else {
return ""
}
var cryptData = Data(count: data.count + kCCBlockSize3DES)
let keyLength = size_t(kCCKeySize3DES)
let operation: CCOperation = CCOperation(kCCEncrypt)
let algoritm: CCAlgorithm = CCAlgorithm(kCCAlgorithm3DES)
let options: CCOptions = CCOptions(kCCOptionECBMode + kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
//Encrypting UTF-8 encoded data in 3DES, ECB, PKCS7 with keyData
let cryptStatus = keyData.withUnsafeBytes {keyBytes in
data.withUnsafeBytes {dataBytes in
cryptData.withUnsafeMutableBytes {mutableBytes in
CCCrypt(operation,
algoritm,
options,
keyBytes, keyLength,
nil,
dataBytes, data.count,
mutableBytes, cryptData.count,
&numBytesEncrypted)
}
}
}
if cryptStatus == CCCryptorStatus(kCCSuccess) {
//Converting encrypted data into base-64 String
cryptData.count = Int(numBytesEncrypted)
let base64cryptString = cryptData.base64EncodedString(options: .lineLength64Characters)
return base64cryptString
} else {
print("Error: \(cryptStatus)")
return ""
}
}
The code is made of 3 parts:
Converting plainString to UTF-8 encoded data
Encrypting UTF-8 encoded data in 3DES, ECB, PKCS7 with keyData
Converting encrypted data into base-64 String
So, to decrypt, you need to apply inverse conversion in reversed order:
Converting base-64 String into encrypted data
Decrypting to UTF-8 encoded data in 3DES, ECB, PKCS7 with keyData
Converting UTF-8 encoded data into plainString
The method looks like this:
func tripleDesDecrypt(base64String: String) -> String {
let keyData = "FIFEDCBA98765432104AABFD".data(using: .utf8)!
//Converting base-64 String into encrypted data
guard let encryptedData = Data(base64Encoded: base64String, options: .ignoreUnknownCharacters) else {
return ""
}
var decryptedData = Data(count: encryptedData.count)
let keyLength = size_t(kCCKeySize3DES)
let operation: CCOperation = CCOperation(kCCDecrypt)
let algoritm: CCAlgorithm = CCAlgorithm(kCCAlgorithm3DES)
let options: CCOptions = CCOptions(kCCOptionECBMode + kCCOptionPKCS7Padding)
var numBytesDecrypted :size_t = 0
//Decrypting to UTF-8 encoded data in 3DES, ECB, PKCS7 with keyData
let cryptStatus = keyData.withUnsafeBytes {keyBytes in
encryptedData.withUnsafeBytes {dataBytes in
decryptedData.withUnsafeMutableBytes {mutableBytes in
CCCrypt(operation,
algoritm,
options,
keyBytes, keyLength,
nil,
dataBytes, encryptedData.count,
mutableBytes, decryptedData.count,
&numBytesDecrypted)
}
}
}
if cryptStatus == CCCryptorStatus(kCCSuccess) {
//Converting UTF-8 encoded data into plainString
let plainString = String(data: decryptedData, encoding: .utf8) ?? ""
return plainString
} else {
print("Error: \(cryptStatus)")
return ""
}
}
Usage:
let encryptedString = tripleDesEncrypt(plainString: "This is a test.")
print(encryptedString) //->QmWafUwPY/MVWu3lHa3c5A==
let decryptedString = tripleDesDecrypt(base64String: encryptedString)
print(decryptedString) //->This is a test.
//Try by yourself...
let origString = tripleDesDecrypt(base64String: "5lg3yg7Q+Y8=")
print(origString)