I am using Xcode 9.0 and CryptoSwift (0.7.2). I am trying to extend String to decrypt an AES128 encrypted string. I've added CryptoSwift successfully with Pods but I get the following compilation error - what am I doing wrong?
'PKCS7' cannot be constructed because it has no accessible initializers
Here is the extension:
import Foundation
import CryptoSwift
extension String {
// https://stackoverflow.com/questions/27072021/aes-encrypt-and-decrypt
func aesDecrypt(key: String, iv: String) throws -> String {
let data = Data(base64Encoded: self)!
let decrypted = try! AES(key: key, iv: iv, blockMode: .CBC, padding: PKCS7()).decrypt([UInt8](data))
let decryptedData = Data(decrypted)
return String(bytes: decryptedData.bytes, encoding: .utf8) ?? "Could not decrypt"
}
}
I've checked out CryptoSwift's documentation, and I found a sample code:
let decrypted = try AES(key: key, iv: iv, blockMode: .CBC, padding: .pkcs7).decrypt(encrypted)
and I think it uses .pkcs7, instead of PKCS7().
Related
I am using AES Encryption in swift 3, I use this CryptoSwift library.
This is my code to encrypt a string and the result is readable string: /QOEtrf3o8buv2wA9FeAyg==.
How can I get the strange character (non readable) like this: Ί�^��h��y^ғ?
var input = "CryptoSwift"
var key = "passwordpassword"
var iv = "drowssapdrowssap"
func aesEncrypt(input: String, key: String, iv: String) throws -> String {
let data = input.utf8
let encrypted = try! AES(key: key, iv: iv, blockMode: .CBC, padding: PKCS7()).encrypt([UInt8](data))
let encryptedData = Data(encrypted)
return encryptedData.base64EncodedString()
}
let encrypted = try! aesEncrypt(input: input, key: key, iv: iv)
print("encrypted: \(encrypted)")
---------
Result: /QOEtrf3o8buv2wA9FeAyg==
I want the result something like this: Ί�^��h��y^ғ, the strange characters.
You shouldn't convert it to Base64.
let encrypted = try! AES(key: key, iv: iv, blockMode: .CBC, padding: PKCS7()).encrypt([UInt8](data))
let encryptedData = Data(bytes: UnsafePointer<UInt8>(encrypted), count: Int(encrypted.count))
// let encryptedString = String(data: encryptedData,encoding: String.Encoding.utf8)
// use the encryptedData to write it into a file.
"Ί�^��h��y^ғ" isn't a proper string. The character "�" means "this isn't a character." (It's technical name is the "substitution character" that is used when a byte sequence is not valid for the Unicode-based encoding you're using). Since "�" could be many different byte sequences, "Ί�^��h��y^ғ" isn't really meaningful. There are a huge number of byte sequences that would decode into that nonsense string.
You're getting a "readable string" because you're encoding the random bytes that come out of the encryption function in Base64.
It's not really clear what you mean by "wanting" something that includes � (since that's nonsense). If you want the data, just return the Data (don't call base64EncodedString()). As a general rule, this is what you want to work with. Encrypted data is Data, it's not `String.
What are you trying to do that you want a nonsense string that has lost information in the encoding?
You're returning a base64 encoded string which will always be readable ASCII. I'm unfamiliar with CryptoSwift, but if you can find some way to return the raw encrypted data, it should look the way you want.
Here is how I use it to properly decrypt and encrypt strings. You need to make string in hexString and hexString in data later. This is how CryptoSwift work.
extension String{
func aesEncrypt(key: String, iv: String) throws -> String {
let data = self.data(using: .utf8)!
let encrypted = try! AES(key: key, iv: iv, blockMode: .CBC).encrypt([UInt8](data))
let encryptedData = Data(encrypted)
try? encryptedData.toHexString().aesDecrypt(key: key, iv: iv)
return encryptedData.toHexString()
}
func aesDecrypt(key: String, iv: String) throws -> String {
let data = self.dataFromHexadecimalString()!
print(data)
let decrypted = try! AES(key: key, iv: iv, blockMode: .CBC).decrypt([UInt8](data))
let decryptedData = Data(decrypted)
return String(bytes: decryptedData.bytes, encoding: .utf8) ?? "Could not decrypt"
}
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!)
}
return data
}
}
extension Data {
public var bytes: Array<UInt8> {
return Array(self)
}
public func toHexString() -> String {
return self.bytes.toHexString()
}
}
Here is my code :
func aesEncrypt(key: String, iv: String) throws -> String
{
let data = self.dataUsingEncoding(NSUTF8StringEncoding)
let enc = try AES(key: key, iv: iv, blockMode:.CBC).encrypt(data!.arrayOfBytes())
let encData = NSData(bytes: enc, length: Int(enc.count))
let base64String: String = encData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0));
let result = String(base64String)
return result
}
func aesDecrypt(key: String, iv: String) throws -> String
{
let data = NSData(base64EncodedString: self, options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters)
let dec = try AES(key: key, iv: iv, blockMode:.CBC).decrypt(data!.arrayOfBytes())
let decData = NSData(bytes: dec, length: Int(dec.count))
let result = NSString(data: decData, encoding: NSUTF8StringEncoding)
return String(result!)
}
The line:
data!.arrayOfBytes()
is producing the error
Ambiguous use of 'arrayOfBytes()'
. I have checked similar questions but none have helped.
The error perisist on both Xcode 7.3 Swift 2.2 and Xcode 8.0 Swift 2.3.
I commented out the PusherSwift framework in Xcdoe 7.3 and it worked.
I am not sure if it is a bug or something I have copied wrong.
If PusherSwift is pusher-websocket-swift, then it looks like they just dropped CryptoSwift directly into their module. If you're also importing CryptoSwift directly, then these will collide. This is a mistake by Pusher. They can't just drop another library into their own without taking special care that it will not collide.
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{
}
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
}
i have used CryptoSwift for AES encryption/decryption. when i run this code it throws error:
"fatal error: Array index out of range".
using reference from here
What can be the cause for this?
Any suggestion will be most appreciate.
code:
let key = "1234567890123456" // key
let iv = "1234567890123456" // random
let message = "This is test string"
override func viewDidLoad() {
super.viewDidLoad()
// encrypt
let encrypted = AES(key: key, iv: iv, blockMode: .CBC)
let enc = encrypted?.encrypt(message.utf8Array, padding: PKCS7())
println("enc >> \(enc)")
// decrypt
let decrypted = AES(key: key, iv: iv, blockMode: .CBC)
let dec = encrypted?.decrypt(enc!, padding: PKCS7())
println("dec >> \(dec)")
The problem may be here: message.utf8Array, where you convert String to Array. Here is working example:
let key = "1234567890123456" // key
let iv = "1234567890123456" // random
let message = "This is test string"
let aes = AES(key: key, iv: iv, blockMode: .CBC)
// encrypt
let enc = try! aes?.encrypt(message.dataUsingEncoding(NSUTF8StringEncoding)!.arrayOfBytes(), padding: PKCS7())
print("enc >> \(enc)")
// decrypt
let dec = try! aes?.decrypt(enc!, padding: PKCS7())
let str = NSString(data: NSData.withBytes(dec!), encoding: NSUTF8StringEncoding)
print("dec >> \(str)")