I have generated an Encrypted Text is Python using cryptography
from cryptography.fernet import Fernet
message = "my deep dark secret".encode()
f = Fernet(key)
encrypted = f.encrypt(message)
# decrypting
from cryptography.fernet import Fernet
encrypted = b"...encrypted bytes..."
f = Fernet(key)
decrypted = f.decrypt(encrypted)
ENCRYPTION INFO:
KEY: b'3b-Nqg6ry-jrAuDyVjSwEe8wrdyEPQfPuOQNH1q5olE='
ENC_MESSAGE: b'gAAAAABhBRBGKSwa7AluNJYhwWaHrQGwAA8UpMH8Wtw3tEoTD2E_-nbeoAvxbtBpFiC0ZjbVne_ZetFinKSyMjxwWaPRnXVSVqz5QqpUXp6h-34_TL7BaDs='
Now I'm trying to Decrypt it in Swift but to no luck. So Far I've Tried CryptoSwift with the following:
func testdec(){
let str = "3b-Nqg6ry-jrAuDyVjSwEe8wrdyEPQfPuOQNH1q5olE="
let ba = "gAAAAABhBRBGKSwa7AluNJYhwWaHrQGwAA8UpMH8Wtw3tEoTD2E_-nbeoAvxbtBpFiC0ZjbVne_ZetFinKSyMjxwWaPRnXVSVqz5QqpUXp6h-34_TL7BaDs="
let encodedString = Base64FS.decodeString(str: String(str.utf8))
print(encodedString.count)
let first4 = String(ba.prefix(25))
let start = first4.index(first4.startIndex, offsetBy: 9)
let end = first4.index(first4.endIndex, offsetBy: 0)
let iv = String(first4[start..<end])
let starta = ba.index(ba.startIndex, offsetBy: 25)
let enda = ba.index(ba.endIndex, offsetBy: -32)
let cipher_text = String(ba[starta..<enda])
let cipher_text_bt: [UInt8] = [UInt8](base64: cipher_text)
print(cipher_text)
print(iv)
let cipher_text_bta: [UInt8] = [UInt8](base64: ba)
// print(encodedString.bytes.count)
// let key_bta: [UInt8] = [UInt8](base64: "RgSADaf8w4v9vokuncyzWRbP5hkdhXSETdxIHLDHtKg=")
// let iv_bt: [UInt8] = [UInt8](base64: "7KUDrsPmb28KQqOWv00KXw==")
// let cipher_text_bt: [UInt8] = [UInt8](base64: "gAAAAABhBQ837KUDrsPmb28KQqOWv00KX2KjsP2ar6lHLqIPUKSvF1WHiruquG-tiAEkrCZZbm-lFR9ZwxsqVcXovmQ3Hv6pWw==")
do{
print("A")
let aes = try AES(key: encodedString, blockMode: CBC(iv: iv.bytes), padding: .pkcs7)
print("B")
let cipherTexta = try aes.decrypt(cipher_text_bt)
print(cipherTexta)
}catch{
print(error)
}
}
OUTPUT:
16
WaHrQGwAA8UpMH8Wtw3tEoTD2E_-nbeoAvxbtBpFiC0ZjbVne_ZetFinKSyMjxw
RBGKSwa7AluNJYhw
A
B
invalidData
Any Help would be appreciated
I've managed to get your cipher text decrypted using only Apple provided sources. If you support iOS 13 and up, I suggest you use CryptoKit
to verify the HMAC, but for now, I've adopted a full CommonCrypto
solution.
First a minor extension to create Data
from base64 URL strings.
import Foundation
import CommonCrypto
extension Data {
init?(base64URL base64: String) {
var base64 = base64
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
if base64.count % 4 != 0 {
base64.append(String(repeating: "=", count: 4 - base64.count % 4))
}
self.init(base64Encoded: base64)
}
}
The decrypt function is a bit obscure, but it supports the very old CommonCrypto
syntax. withUnsafeBytes
syntax would be cleaner, but this is a quick workaround.
func decrypt(ciphertext: Data, key: Data, iv: Data) -> Data {
var decryptor: CCCryptorRef?
defer {
CCCryptorRelease(decryptor)
}
var key = Array(key)
var iv = Array(iv)
var ciphertext = Array(ciphertext)
CCCryptorCreate(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionPKCS7Padding), &key, key.count, &iv, &decryptor)
var outputBytes = [UInt8](repeating: 0, count: CCCryptorGetOutputLength(decryptor, ciphertext.count, false))
CCCryptorUpdate(decryptor, &ciphertext, ciphertext.count, &outputBytes, outputBytes.count, nil)
var movedBytes = 0
var finalBytes = [UInt8](repeating: 0, count: CCCryptorGetOutputLength(decryptor, 0, true))
CCCryptorFinal(decryptor, &finalBytes, finalBytes.count, &movedBytes)
return Data(outputBytes + finalBytes[0 ..< movedBytes])
}
Then the HMAC. I suggest you use CryptoKit
if you can. This function is of course fixed, there might be ways to make this dynamic. For Fernet however, only SHA256 is supported.
func verifyHMAC(_ mac: Data, authenticating data: Data, using key: Data) -> Bool {
var data = Array(data)
var key = Array(key)
var macOut = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), &key, key.count, &data, data.count, &macOut)
return Array(mac) == macOut
}
All of that together comes down to the following code. Note that I do not check the version and/or timestamp, which should be done according to the spec.
let fernetKey = Data(base64URL: "3b-Nqg6ry-jrAuDyVjSwEe8wrdyEPQfPuOQNH1q5olE=")!
let signingKey = fernetKey[0 ..< 16]
let cryptoKey = fernetKey[16 ..< fernetKey.count]
let fernetToken = Data(base64URL: "gAAAAABhBRBGKSwa7AluNJYhwWaHrQGwAA8UpMH8Wtw3tEoTD2E_-nbeoAvxbtBpFiC0ZjbVne_ZetFinKSyMjxwWaPRnXVSVqz5QqpUXp6h-34_TL7BaDs=")!
let version = Data([fernetToken[0]])
let timestamp = fernetToken[1 ..< 9]
let iv = fernetToken[9 ..< 25]
let ciphertext = fernetToken[25 ..< fernetToken.count - 32]
let hmac = fernetToken[fernetToken.count - 32 ..< fernetToken.count]
let plainText = decrypt(ciphertext: ciphertext, key: cryptoKey, iv: iv)
print(plainText, String(data: plainText, encoding: .utf8) ?? "Non utf8")
print(verifyHMAC(hmac, authenticating: version + timestamp + iv + ciphertext, using: signingKey))