CryptoSwift AES128-CBC with Swift - ios

I'm getting this error while decrypting an AES128CBC encrypted JSON:
assertion failed: Block size and Initialization Vector must be the
same length!: file ##### for
iOS/CryptoSwift/Sources/CryptoSwift/AES.swift, line 97
I'm using the CryptoSwift Framework with the latest Xcode.
My Method:
func aes128(key: String, iv: String) throws -> String?{
if let aes: AES = try AES(key: key, iv: iv, blockMode: .CBC){
if let encrypted: [UInt8] = try aes.encrypt([UInt8](self.utf8), padding: PKCS7()){
return String(data: NSData.withBytes(encrypted), encoding: NSUTF8StringEncoding)
}
}
return nil
}
Call:
do{
print(try dataStr.aes128("8e0c0e73f97f2eb386ad75ba86051334", iv: "aa17ffc4ea4b1eac8fa0b56872f34e5f"))
}catch{
}

My Method:
func aes128(key: [UInt8], iv: [UInt8]) throws -> String?{
let data = NSData(base64EncodedString: self, options: NSDataBase64DecodingOptions(rawValue: 0))
let dec = try AES(key: key, iv: iv, blockMode:.CBC).decrypt(data!.arrayOfBytes(), padding: PKCS7())
let decData = NSData(bytes: dec, length: Int(dec.count))
let result = NSString(data: decData, encoding: NSUTF8StringEncoding)
return String(result!)
}
The call:
do{
let secret: [UInt8] = self.getAuthSecret(.LoginSecret)!.byte
let ivSlice = secret[0..<16]
let ivArray = Array<UInt8>(ivSlice)
let keySlice = secret[16..<32]
let keyArray = Array<UInt8>(keySlice)
print(try dataStr.aes128(keyArray, iv: ivArray))
}catch{
}

Related

i have a problem with AES encryption the key i have is based 64 string , the data encrypted wrong

I've array of objects , each object may have these
:[ "id", "start_long", "start_lat", "start_date", "type", "second_user_id", "end_long", "end_lat",
"end_date", "feedback"]
when i try to decode a based64String, I get nil, I've tried to use CryptoSwift, RNCryptor
I want to pass 64based string the key ass method key
key : tK5UTui+DPh8lIlBxya5XVsmeDCoUl6vHhdIESMB6sQ=
AES Encryption / Decryption using base64 key, salt and iv (Initialization Vector).
1) Import CommonCrypto. This is Appleā€™s open source cryptography library.
import CommonCrypto
2) Create key with salt.
extension Data {
func createKey(salt: Data) -> Data {
let length = kCCKeySizeAES256
var status = Int32(0)
var derivedBytes = [UInt8](repeating: 0, count: length)
self.withUnsafeBytes { (passwordBytes: UnsafePointer<Int8>!) in
salt.withUnsafeBytes { (saltBytes: UnsafePointer<UInt8>!) in
status = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2), // algorithm
passwordBytes, // password
self.count, // password length
saltBytes, // salt
salt.count, // salt length
CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1), // prf
10000, // rounds
&derivedBytes, // derived key
derivedBytes.count) // derived key length
}
}
if(status != kCCSuccess){
return Data.init(count: 0)
}
return Data(bytes: UnsafePointer<UInt8>(derivedBytes), count: length)
}
}
3) Encryption / Decryption extension
extension String {
func aesEncrypt(key:String, salt:String, iv:String) -> String? {
if let keyData = key.data(using: String.Encoding.utf8)?.createKey(salt: Data.init(base64Encoded: salt, options: .ignoreUnknownCharacters)!),
let data = self.data(using: String.Encoding.utf8),
let ivData = Data.init(base64Encoded: iv, options: .ignoreUnknownCharacters),
let cryptData = NSMutableData(length: Int((data.count)) + kCCKeySizeAES256) {
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
var cryptStatus: CCCryptorStatus = CCCryptorStatus(kCCSuccess)
ivData.withUnsafeBytes { (ivBytes: UnsafePointer<UInt8>!) -> () in
cryptStatus = CCCrypt(operation,
algoritm,
options,
(keyData as NSData).bytes, keyData.count,
ivBytes,
(data as NSData).bytes, data.count,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
}
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
let base64cryptString = cryptData.base64EncodedString(options: .lineLength64Characters)
return base64cryptString
}
else {
return nil
}
}
return nil
}
func aesDecrypt(key:String, salt:String, iv:String) -> String? {
if let keyData = key.data(using: String.Encoding.utf8)?.createKey(salt: Data.init(base64Encoded: salt, options: .ignoreUnknownCharacters)!),
let data = NSData(base64Encoded: self, options: .ignoreUnknownCharacters),
let ivData = Data.init(base64Encoded: iv, options: .ignoreUnknownCharacters),
let cryptData = NSMutableData(length: Int((data.length)) + kCCKeySizeAES256) {
let operation: CCOperation = UInt32(kCCDecrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
var cryptStatus: CCCryptorStatus = CCCryptorStatus(kCCSuccess)
ivData.withUnsafeBytes { (ivBytes: UnsafePointer<UInt8>!) -> () in
cryptStatus = CCCrypt(operation,
algoritm,
options,
(keyData as NSData).bytes, keyData.count,
ivBytes,
data.bytes, data.length,
cryptData.mutableBytes, cryptData.length,
&numBytesEncrypted)
}
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
let unencryptedMessage = String(data: cryptData as Data, encoding:String.Encoding.utf8)
return unencryptedMessage
}
else {
return nil
}
}
return nil
}
4) Dictionary to json string
extension Dictionary {
func toJsonString() -> String {
do {
let jsonData = try JSONSerialization.data(withJSONObject: self, options: [])
let jsonString = String(data: jsonData, encoding: .utf8)
return jsonString ?? ""
} catch {
print(error.localizedDescription)
return ""
}
}
}
5) Sample
let param: [String: AnyObject] = [
"test": "aes Encrypt_Decrypt"
] as [String: AnyObject]
let jsonString = param.toJsonString()
print("Json String : " + jsonString)
let key = "tK5UTui+DPh8lIlBxya5XVsmeDCoUl6vHhdIESMB6sQ="
let salt = "QWlGNHNhMTJTQWZ2bGhpV3U=" // base64 decode => AiF4sa12SAfvlhiWu
let iv = "bVQzNFNhRkQ1Njc4UUFaWA==" // base64 decode => mT34SaFD5678QAZX
let encrypt = (jsonString.aesEncrypt(key: key, salt: salt, iv: iv))
print("Encrypt String : " + (encrypt ?? ""))
print("Decrypt String : " + (encrypt?.aesDecrypt(key: key, salt: salt, iv: iv) ?? ""))
6) Output
Json String : {"test":"aes Encrypt_Decrypt"}
Encrypt String : PdT9Gxy6nz5/b4n7/wbj+6svYD06DokH7PbSWi+Pfmw=
Decrypt String : {"test":"aes Encrypt_Decrypt"}
I have used CryptoSwift in my project for AES Encryption Decryption,
i) Firstly I imported CryptoSwift Library
ii) Created Extension of String
iii) Added methods for Encryption and Decryption
import CryptoSwift
extension String {
func aesEncrypt() -> String? {
do {
let encrypted = try AES(key: Array(AES_KEY.utf8), blockMode:CBC(iv: Array(AES_IV.utf8)), padding: .pkcs5).encrypt([UInt8](self.data(using: .utf8)!))
return Data(encrypted).base64EncodedString()
} catch {
return nil
}
}
func aesDecrypt() -> String? {
guard let data = Data(base64Encoded: self) else { return "" }
do {
let decrypted = try AES(key: Array(AES_KEY.utf8), blockMode:CBC(iv: Array(AES_IV.utf8)), padding: .pkcs5).decrypt([UInt8](data))
return String(bytes: decrypted, encoding: .utf8) ?? self
} catch {
return nil
}
}
}
iv) I have created Extension of Dictionary to convert my parameters([String : Any]) to JsonString
extension Dictionary {
func convertToJson() -> String{
var Json : String!
let dictionary = self
if let theJSONData = try? JSONSerialization.data(
withJSONObject: dictionary,
options: []) {
let theJSONText = String(data: theJSONData,encoding: .utf8)
Json = theJSONText
}
return Json
}
}
Usage:
//MARK:- AES Encryption-Decryption Code
//Convert parameters to JSON String
let jsonString = parameters.convertToJson()
print("Json String : \(jsonString)")
//Encrypted Json String
let encriptionData = jsonString.aesEncrypt()
print("Encription String : \(encriptionData ?? "empty")")
//Decrypt your encrypted Data - This optional if you want to cross verify that your encryption data are correct
let decriptionData = encriptionData?.aesDecrypt()
print("DecriptionData String : \(decriptionData ?? "empty")")
//Pass the encrypted String inside one key to your API Request parameters. In my case it is "data" key
let param:[String:Any] = ["data":encriptionData ?? ""]
print("Encripted param String : \(param)")
Hope it helps!

IOS Decrypt string with CryptoSwift

I want to decrypt string in base64 and when i create string from decrypted data i get nil in my string.
Here's my code:
private func xd(url: String?) -> URL? {
guard let enryptedData = Data(base64Encoded: url ?? "") else { return nil }
let password: Array<UInt8> = "TW9iaWxueUNhdGVyaW5nRGVlcGxpbmtTZWNyZXQ=".bytes
let salt: Array<UInt8> = "ItyuwaSolSaltSalzToteSoSoutAsin".bytes
let iv = Data(count: 16)
Logger.debug("IV: \(iv)")
do {
let data = Padding.pkcs5.add(to: enryptedData.bytes, blockSize: AES.blockSize)
let key = try PKCS5.PBKDF2(password: password,
salt: salt,
keyLength: 32,
variant: .sha256).calculate()
let decryptedData = try AES(key: key, blockMode: CBC(iv: iv.bytes), padding: .pkcs5)
.decrypt(data)
let string = String(bytes: decryptedData, encoding: .utf8)
Logger.debug("Decrypted string: \(string ?? "")")
return URL(string: string ?? "")
} catch {
Logger.info("\(error)")
return nil
}

How to Decrypt a string using AES-256 CFB with PKCS5PADDING in swift?

I am looking to decrypt a string using AES, I tried multiple solution from SO but none of them helped and I was unable to get the solution. The android developer used Cipher to do it below is the code for that:-
private static final byte[] initVector = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
private static Cipher getAES_Cipher(int opmode) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException {
IvParameterSpec iv = new IvParameterSpec(initVector);
SecretKeySpec skeySpec = new SecretKeySpec(Arrays.copyOfRange(getSHA(key), 0, 32), "AES");
Cipher cipher = Cipher.getInstance("AES/CFB/PKCS5PADDING");
cipher.init(opmode, skeySpec, iv);
return cipher;
}
Similarly I tried using CryptoSwift to do it and below is the code I used to do it:-
extension String {
func aesEncrypt(key: String, iv: String) throws -> String {
let data: Array<UInt8> = (self.data(using: .utf8)?.bytes)!
var key: Array<UInt8> = (key.data(using: .utf8)?.bytes)!
let iv: Array<UInt8> = (iv.data(using: .utf8)?.bytes)!
do {
let encrypted = try AES(key: key, blockMode: CFB(iv: iv), padding: .pkcs5).encrypt(data)
let encryptedData = Data(encrypted)
let decrypted = try AES(key: key, blockMode: CFB(iv: iv), padding: .pkcs5).decrypt(encrypted)
let decryptedData = Data(decrypted)
let str = String.init(data: decryptedData, encoding: .utf8)
print(str ?? String())
return encryptedData.base64EncodedString()
} catch {
print(error)
return "error"
}
}
func aesDecrypt(key: String, iv: String) throws -> String {
let data: Array<UInt8> = (Data(base64Encoded: self)?.bytes)!
let key: Array<UInt8> = (key.data(using: .utf8)?.bytes)!
let iv: Array<UInt8> = (iv.data(using: .utf8)?.bytes)!
do {
let decrypted = try AES(key: key.sha256(), blockMode: CFB(iv: iv), padding: .pkcs5).decrypt(data)
let decryptedData = Data(decrypted)
guard let value = String.init(data: decryptedData, encoding: .utf8) else {
return "error"
}
return value
} catch {
print(error)
return "error"
}
}
}
and In ViewDidLoad() I called it like this:-
let message = "My Encrypted String From The Server"
let test = try! message.aesDecrypt(key: "dfksjghlskjdfhglksjdfhglkjsdhfglkjhsfopweiurtypoweirutopiewrutgopiruegoijnsdeghsedrghesrerthigoererhehewthgewrhywertyweyweyewrtewrtyewihgoie", iv: "0000000000000000")
print(test)
One more thing I want to highlight when I pass the same iv as android which is "{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }" I am getting the error from the CryptoSwift library as invalidInitializationVector but when I pass iv: "0000000000000000" I am getting an error -->
guard let value = String.init(data: decryptedData, encoding: .utf8) else {
return "error"
}
I believe it is something related to the iv that I am passing there.
Guys any help would be greatly appreciated!!
Thank You
Found that question when tried to decode some data using CryptoSwift. Tried the code and it didn't work for me too (did return "error").
The issue is in decrypt method and is related to key.
You have for encryption:
let encrypted = try AES(key: key, blockMode: CFB(iv: iv), padding: .pkcs5).encrypt(data)
But to decryption the code looks like this:
let decrypted = try AES(key: key.sha256(), blockMode: CFB(iv: iv), padding: .pkcs5).decrypt(data)
For me removing .sha256() fixed the issue.
So final decrypt method should be:
func aesDecrypt(key: String, iv: String) throws -> String {
let data: Array<UInt8> = (Data(base64Encoded: self)?.bytes)!
let key: Array<UInt8> = (key.data(using: .utf8)?.bytes)!
let iv: Array<UInt8> = (iv.data(using: .utf8)?.bytes)!
do {
let decrypted = try AES(key: key, blockMode: CFB(iv: iv), padding: .pkcs5).decrypt(data)
let decryptedData = Data(decrypted)
guard let value = String.init(data: decryptedData, encoding: .utf8) else {
return "error"
}
return value
} catch {
print(error)
return "error"
}
}
Maybe it will help someone and save some time.

How to convert JSON response string to in JSON object using Swift 3.1?

I am struggling with Swift 3. I am getting (hexString) response from server in encrypted form and I am first converting it into Data.
Below is my method to converting hexString to in Data:
extension String {
func dataFromHexadecimalString() -> Data? {
var data = Data(capacity: characters.count / 2)
let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
regex.enumerateMatches(in: self, options: [], range: NSMakeRange(0, characters.count)) { match, flags, stop in
let byteString = (self as NSString).substring(with: match!.range)
var num = UInt8(byteString, radix: 16)!
data.append(&num, count: 1)
}
guard data.count > 0 else {
return nil
}
return data
}
}
After that I am Decrypting the data using CryptoSwift framework. Here below is my method to decrypt the data :-
func getAllShops(data : [String : AnyObject]) {
AFWrapper.sharedInstance.requestPOSTURL(My_URL, params: data, headers: nil, success: {(json) in
// Key for AES
let key = "My_Key"
// Iv for AES
let iv = "My_IV"
//Hex String from Server Response
let str = json["encrypted"].string!
do {
//Converting Hex String to Data
let dataStr = str.dataFromHexadecimalString()
//Decrypt the Data
let decryptedData = try AES(key: key, iv: iv, blockMode: .CBC, padding: NoPadding()).decrypt(dataStr!)
//Getting json string Response
var jsonResponse = String(bytes: decryptedData, encoding: .utf8)!
/*let response = JSON(jsonResponse)
print(response)
let validJson = JSONSerialization.isValidJSONObject(response)//(JSONSerialization.JSONObjectWithData(jsonData, options:nil, error: &error) != nil)
print("Valid JSON: \(validJson)")*/
/*let data = jsonResponse.data(using: String.Encoding.utf8)
let jsonsdd = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any]
print(jsonsdd)*/
}
catch {
print(error)
}
}, failure: {(Error) in
print(Error.localizedDescription)
})
}.
I am trying to convert that json String to in JSON object so that I can parse the data.
I tried with swiftyJSON and JSONSerialization in my code that part is commented. but not able to convert it.
I guess I am missing small thing that I don't know.

Could not decrypt a base64String using CryptoSwift

String for encryption "secret"
after encryption "64c2VjcmV0"
this is the code that works properly
let inputNSData: NSData = input.dataUsingEncoding(NSUTF8StringEncoding)!
let inputBytes: [UInt8] = inputNSData.arrayOfBytes()
let key: [UInt8] = self.generateArray("secret0key000000") //16
let iv: [UInt8] = self.generateArray("0000000000000000") //16
do {
let encrypted: [UInt8] = try AES(key: key, iv: iv, blockMode: .CBC).encrypt(inputBytes, padding: PKCS7())
let decrypted: [UInt8] = try AES(key: key, iv: iv, blockMode: .CBC).decrypt(encrypted, padding: PKCS7())
let decryptNsData: NSData = NSData(bytes: decrypted, length: decrypted.count)
let c = decryptNsData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
let decryptedString: String = NSString(data: decryptNsData, encoding: NSUTF8StringEncoding) as! String
print("String after decryption\t\(decryptedString)")
} catch {
// some error
}
but i could not decrypt by using the same key and iv
I am getting fatal error: unexpectedly found nil while unwrapping an Optional value for the encrypted string
let key: [UInt8] = self.generateArray("secret0key000000") //16
let iv: [UInt8] = self.generateArray("0000000000000000") //16
let input: String = "64c2VjcmV0"
var encryptedStrData = NSData(base64EncodedString: input, options: NSDataBase64DecodingOptions())!
let inputBytes: [UInt8] = encryptedStrData.arrayOfBytes()
print("String in uint8\(inputBytes)")
//var keyData = keyStr.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
//var ivData:NSData = ivStr.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
do{
let decryptedTryData = try AES(key: key, iv: iv, blockMode: .CBC).decrypt(inputBytes)
print(decryptedTryData)
}
catch{
}
I am getting fatal error: unexpectedly found nil while unwrapping an Optional value for the encrypted string
You are using Base64 when it is not necessary, only Base64 encode non string data.
Here is the first test code:
let inputBytes: [UInt8] = Array("secret".utf8)
let key: [UInt8] = Array("secret0key000000".utf8) //16
let iv: [UInt8] = Array("0000000000000000".utf8) //16
var encryptedBase64 = ""
do {
let encrypted: [UInt8] = try AES(key: key, iv: iv, blockMode: .CBC).encrypt(inputBytes, padding: PKCS7())
let encryptedNSData = NSData(bytes: encrypted, length: encrypted.count)
encryptedBase64 = encryptedNSData.base64EncodedStringWithOptions([])
let decrypted: [UInt8] = try AES(key: key, iv: iv, blockMode: .CBC).decrypt(encrypted, padding: PKCS7())
let result = String(bytes: decrypted, encoding: NSUTF8StringEncoding)!
print("result\t\(result )")
} catch {
// some error
}
print("encryptedBase64: \(encryptedBase64)")
Output:
result: secret
encryptedBase64: 0OCxa0yJszq9MvkrWjn3wg==
let encryptedData = NSData(base64EncodedString: encryptedBase64, options:[])!
print("decodedData: \(encryptedData)")
let encrypted = Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>(encryptedData.bytes), count: encryptedData.length))
do {
let decryptedData = try AES(key: key, iv: iv, blockMode: .CBC).decrypt(encrypted)
let decryptedString = String(bytes: decryptedData, encoding: NSUTF8StringEncoding)!
print("decryptedString: \(decryptedString)")
}
catch{
// some error
}
Output:
decryptedString: secret
Notes:
Don't use CryptoSwift, it does not use the build-in encryption hardware and is 400 to 1000 times slower than the Apple Security.framework Common Crypto. It is also not well vetted and used non-certified encryption code.
Don't use a string directly as the key, it is not secure. Instead derive a key from the string using PBKDK2 (Password Based Key Derivation Function).
Base64 strings must be divisible by 4. Yours is not.
You can use a website, such as https://www.base64decode.org, to test your strings.
For Swift3 (to avoid the UnsafePointer error) this has worked for me for the second part (decoding the base64 variable):
let encryptedData = NSData(base64Encoded: encryptedBase64, options:[])!
print("decodedData: \(encryptedData)")
let count = encryptedData.length / MemoryLayout<UInt8>.size
// create an array of Uint8
var encrypted = [UInt8](repeating: 0, count: count)
// copy bytes into array
encryptedData.getBytes(&encrypted, length:count * MemoryLayout<UInt8>.size)
do {
let decryptedData = try AES(key: key, iv: iv, blockMode: .CBC).decrypt(encrypted)
let decryptedString = String(bytes: decryptedData, encoding: String.Encoding.utf8)!
print("decryptedString: \(decryptedString)")
}
catch{
// some error
}

Resources