AES128 encryption in swift - ios

I’ve an issue with AES-128 encryption. The encrypted string in iOS is different as compared to Android.
Below is android code :
public class Encryption {
private static final String ALGORITHM = "AES";
private static final String UNICODE_FORMAT = "UTF8";
public static String encryptValue(String valueToEnc) {
try {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encValue = c.doFinal(valueToEnc.getBytes());
String encryptedValue = new Base64().encode(encValue);
String urlEncodeddata = URLEncoder.encode(encryptedValue, "UTF-8");
return urlEncodeddata;
} catch (Exception e) {
}
return valueToEnc;
}
private static Key generateKey() throws Exception {
byte[] keyAsBytes;
keyAsBytes = "MySixteenCharKey".getBytes(UNICODE_FORMAT);
Key key = new SecretKeySpec(keyAsBytes, ALGORITHM);
return key;
}
}

Create string extension and use library CryptoSwift
// String+Addition.swift
import CryptoSwift
extension String {
func aesEncrypt(key: String) throws -> String {
var result = ""
do {
let key: [UInt8] = Array(key.utf8) as [UInt8]
let aes = try! AES(key: key, blockMode: ECB() , padding:.pkcs5) // AES128 .ECB pkcs7
let encrypted = try aes.encrypt(Array(self.utf8))
result = encrypted.toBase64()!
print("AES Encryption Result: \(result)")
} catch {
print("Error: \(error)")
}
return result
}
func aesDecrypt(key: String) throws -> String {
var result = ""
do {
let encrypted = self
let key: [UInt8] = Array(key.utf8) as [UInt8]
let aes = try! AES(key: key, blockMode: ECB(), padding: .pkcs5) // AES128 .ECB pkcs7
let decrypted = try aes.decrypt(Array(base64: encrypted))
result = String(data: Data(decrypted), encoding: .utf8) ?? ""
print("AES Decryption Result: \(result)")
} catch {
print("Error: \(error)")
}
return result
}
}
and to use
#IBAction func onBtnClicked(_ sender: Any) {
let value = "My value to be encrypted"
let key = "MySixteenCharKey"
print(key!)
let encryptedValue = try! value.aesEncrypt(key: key!)
print(encryptedValue)
}
For citation of this particular encryption code:
The 16 char length key implies AES-128
The code is without iVector, This implies ECB mode
Using padding as pkcs5 or pkcs7 did not made any difference in my case

Try this:
func encryptValue(stringToEncrypt:String) -> String{
var encryptedString: String = ""
let value = "MySixteenCharKey"
let input: Array<UInt8> = Array(stringToEncrypt.utf8)
let key: Array<UInt8> = Array("MySixteenCharKey".utf8)
do {
let encrypted = try AES(key: key, blockMode: .ECB, padding: PKCS7()).encrypt(input)
let base64 = encrypted.toBase64()
encryptedString = base64
} catch {
print(error)
}
return encryptedString
}

Create an extension of String and use this below function for encrypting and decrypting.
extension String {
//MARK: - Encrypt AES Method
/// This method will encrypt and return Cipher string
func aesEncrypt(key: String = "fqJfdzGDvfwbedsKSUGty3VZ9taXxMVw", iv: String = "1234567890123456") -> String {
if let data = self.data(using: String.Encoding.utf8) {
do {
let enc = try AES(key: key, iv: iv, blockMode: .CBC, padding: PKCS7()).encrypt(data.bytes)
let encData = Data(bytes: enc, count: Int(enc.count))
let base64String: String = encData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0));
return base64String
}
catch let error {
print(error.localizedDescription)
return ""
}
}
return ""
}
//MARK: - Decrypt AES Method
/// This method will decrypt the Cipher string
func aesDecrypt(key: String = "fqJfdzGDvfwbedsKSUGty3VZ9taXxMVw", iv: String = "1234567890123456") -> String {
if let data = Data(base64Encoded: self, options: Data.Base64DecodingOptions.init(rawValue: 0)) {
do {
let dec = try AES(key: key, iv: iv, blockMode: .CBC, padding: PKCS7()).decrypt(data.bytes)
let decData = Data(bytes: dec, count: Int(dec.count))
let result = String(data: decData, encoding: .utf8)
return result ?? ""
}
catch let error {
print(error.localizedDescription)
return ""
}
}
return ""
}
}

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.

AES CommonCrypto: String Decryption is Not in Proper Format for JSON API response

I am using common crypto encryption and decryption.
I am using AES for API calling and response.
I have done everything right but whenever I am doing decryption of the
received response, only the first key in response in not getting
decrypted, rest other response string is decrypted in good format.
Decrypted Response:
AES Struct Code:
struct AES {
// MARK: - Value
// MARK: Private
private let key: Data
private let iv: Data
// MARK: - Initialzier
init?(key: String, iv: String) {
guard key.count == kCCKeySizeAES128 || key.count == kCCKeySizeAES256, let keyData = key.data(using: .utf8) else {
debugPrint("Error: Failed to set a key.")
return nil
}
guard iv.count == kCCBlockSizeAES128, let ivData = iv.data(using: .utf8) else {
debugPrint("Error: Failed to set an initial vector.")
return nil
}
self.key = keyData
self.iv = ivData
}
// MARK: - Function
// MARK: Public
func encrypt(string: String) -> String? {
let data = crypt(data: string.data(using: .utf8), option: CCOperation(kCCEncrypt))
let str = data?.base64EncodedString()
let ivStr = UIConstants.IV.base64Encoded
let str1 = (str?.replacingOccurrences(of: " ", with: ""))! + ":" + ivStr!
let str2 = str1.replacingOccurrences(of: "+", with: ".")
let str3 = str2.replacingOccurrences(of: "/", with: "_")
let str4 = str3.replacingOccurrences(of: "=", with: "-")
let str5 = str4.replacingOccurrences(of: "\n", with: "")
return str5
}
func decrypt(data: Data?) -> String? {
guard let decryptedData = crypt(data: data, option: CCOperation(kCCDecrypt)) else { return nil }
return String(bytes: decryptedData, encoding: .ascii)
}
func crypt(data: Data?, option: CCOperation) -> Data? {
guard let data = data else { return nil }
let cryptLength = [UInt8](repeating: 0, count: data.count + kCCBlockSizeAES128).count
var cryptData = Data(count: cryptLength)
let keyLength = [UInt8](repeating: 0, count: kCCBlockSizeAES128).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, keyLength, ivBytes, dataBytes, data.count, cryptBytes, 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
}
}
API Response Decryption Code:
let responseDic = response.object! as [String: Any]
let responseString = responseDic["data"] as! String
let str2 = responseString.replacingOccurrences(of: ".", with: "+")
let str3 = str2.replacingOccurrences(of: "_", with: "/")
let str4 = str3.replacingOccurrences(of: "-", with: "=")
let index = str4.range(of: ":")?.lowerBound
let substring = str4[..<index!]
let stringRemovingIV = String(substring)
print(stringRemovingIV)
let data = Data(base64Encoded: stringRemovingIV, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters)
var decryptString = aes256?.decrypt(data: data) as! String
print(decryptString)
let responseObject = NSDictionary.convertStringToDictionary(text: String(decryptString))
print(responseObject as Any)

RNCryptor is not decrypting in iOS

I am using RNCryptor in my application. I generated an encrypted string for a plain text using a password and when I try to decrypt the encrypted string using a password, it does not give me the original plain text instead it gave me some random string.
I guess I am missing something in string encoding/decoding but I am not sure what I am missing here.
Can you please guide me to fix this?
Code
func encrypt(plainText : String, password: String) -> String {
let data: Data = plainText.data(using: .utf8)!
let encryptedData = RNCryptor.encrypt(data: data, withPassword: password)
let encryptedString : String = encryptedData.base64EncodedString(options: Data.Base64EncodingOptions.lineLength76Characters)
return encryptedString
}
func decrypt(encryptedText : String, password: String) -> String {
do {
let data: Data = Data.init(base64Encoded: encryptedText, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters)!
let decryptedData = try RNCryptor.decrypt(data: data, withPassword: password)
let decryptedString : String = decryptedData.base64EncodedString(options: Data.Base64EncodingOptions.lineLength76Characters)
return decryptedString
}
catch {
return "FAILED"
}
}
let plainText = "123456789"
let password = "ABCDEFGHIJ"
let encryptedText = self.encrypt(plainText: plainText, password: password)
let decryptedText = self.decrypt(encryptedText: encryptedText, password: password)
print("ENCRYPTED TEXT : \(encryptedText)")
print("DECRYPTED TEXT : \(decryptedText)")
ENCRYPTED TEXT : AwEsB6wlUSIJ31TAbaeAjVXP272zW89aa2rR9v6zYWwKUf6Hs5GSHekMKQT+n0vw6jMtjsQVhtzO
8AcqGpTLrQ9YR0PUS07P+8HboCp6Ge8UxQ==
DECRYPTED TEXT : MTIzNDU2Nzg5
Just try like below, its worked for me now.
func encrypt(plainText : String, password: String) -> String {
let data: Data = plainText.data(using: .utf8)!
let encryptedData = RNCryptor.encrypt(data: data, withPassword: password)
let encryptedString : String = encryptedData.base64EncodedString() // getting base64encoded string of encrypted data.
return encryptedString
}
func decrypt(encryptedText : String, password: String) -> String {
do {
let data: Data = Data(base64Encoded: encryptedText)! // Just get data from encrypted base64Encoded string.
let decryptedData = try RNCryptor.decrypt(data: data, withPassword: password)
let decryptedString = String(data: decryptedData, encoding: .utf8) // Getting original string, using same .utf8 encoding option,which we used for encryption.
return decryptedString ?? ""
}
catch {
return "FAILED"
}
}
Thanks.
In decrypt func you must decode Base64 first than RNC.

Resources