Decrypt Fernet Encrypted Text(PYTHON) in SWIFT

ghz 11hours ago ⋅ 3 views

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

Answer

The issue you're encountering is related to how you're trying to decrypt the message encrypted by Python's cryptography.fernet.Fernet in Swift using the CryptoSwift library. The cryptography.fernet.Fernet encryption scheme does not directly map to AES with CBC mode, which is what you're trying to use in your Swift code.

Why it isn't working:

  1. Fernet vs. AES-CBC:
    The cryptography.fernet module uses a symmetric encryption scheme that combines AES (in CBC mode) with HMAC for integrity, but it also uses a specific structure for its ciphertext (including the IV, encryption metadata, and padding). Simply trying to decrypt this using AES-CBC from CryptoSwift won't work because the encryption metadata, IV, and other components need to be correctly parsed and handled.

  2. Fernet Ciphertext Structure:
    Fernet ciphertext has a specific structure:

    • A version identifier (usually gAAAAAB).
    • An IV (initialization vector) and ciphertext (which is base64-encoded).
    • The Fernet scheme embeds the IV and HMAC directly into the ciphertext, so you can't just treat the string as raw AES-encrypted data.

Steps to Solve:

Since you are using Python's cryptography.fernet module, you will need to reverse-engineer the structure of the Fernet ciphertext in Swift. CryptoSwift alone can't handle this, as it does not understand the specific structure that Fernet uses.

The solution requires you to extract and handle the IV, the ciphertext, and the HMAC separately.

Here is how you can go about it:

Decrypting Fernet Ciphertext in Swift:

  1. Extract the components of the Fernet encrypted data.

    • The Fernet ciphertext is base64 encoded and begins with a version string (gAAAAAB).
    • The first 16 bytes after the version are the IV.
    • The remaining part is the actual encrypted data.
  2. Verify the integrity of the ciphertext.

    • Fernet uses an HMAC for authentication of the ciphertext. This means you will need to use the same key to validate the HMAC in Swift before decrypting the data.

Unfortunately, CryptoSwift does not natively support Fernet's structure, so you would need to either implement the Fernet decryption yourself in Swift or use a library that supports it.

Here's a rough guide on how you might implement the decryption in Swift:

Step-by-step guide to implement Fernet decryption:

  1. Parse the Fernet ciphertext (extract the IV and ciphertext).
  2. Verify the HMAC (optional, but recommended to ensure data integrity).
  3. Decrypt using AES-CBC mode (using the IV and the key).
  4. Handle padding correctly during decryption.

Swift Code Example:

import CryptoSwift

func fernetDecrypt(keyBase64: String, encryptedBase64: String) {
    // Decode the key and encrypted message
    guard let key = Data(base64Encoded: keyBase64),
          let encryptedData = Data(base64Encoded: encryptedBase64) else {
        print("Invalid base64 data")
        return
    }
    
    // Fernet cipher is versioned, first 16 bytes are the version (gAAAAAB) and IV
    let iv = encryptedData.prefix(16)  // Extract IV (the first 16 bytes)
    let cipherText = encryptedData.suffix(from: 16)  // The rest is the encrypted data
    
    // Decrypt using AES CBC mode
    do {
        let aes = try AES(key: Array(key), blockMode: CBC(iv: Array(iv)), padding: .pkcs7)
        let decrypted = try aes.decrypt(Array(cipherText))
        
        // Convert decrypted data back to a string
        if let decryptedString = String(data: Data(decrypted), encoding: .utf8) {
            print("Decrypted message: \(decryptedString)")
        } else {
            print("Failed to decode decrypted message.")
        }
    } catch {
        print("Decryption failed: \(error)")
    }
}

let keyBase64 = "3b-Nqg6ry-jrAuDyVjSwEe8wrdyEPQfPuOQNH1q5olE="
let encryptedBase64 = "gAAAAABhBRBGKSwa7AluNJYhwWaHrQGwAA8UpMH8Wtw3tEoTD2E_-nbeoAvxbtBpFiC0ZjbVne_ZetFinKSyMjxwWaPRnXVSVqz5QqpUXp6h-34_TL7BaDs="

fernetDecrypt(keyBase64: keyBase64, encryptedBase64: encryptedBase64)

Explanation:

  1. Base64 Decoding:
    The key and ciphertext are base64 encoded, so they are decoded into Data objects.

  2. Extract IV and Ciphertext:
    The Fernet ciphertext starts with the version (gAAAAAB) and is followed by the IV and the actual encrypted data. We separate the IV (first 16 bytes) from the encrypted data.

  3. AES Decryption in CBC Mode:
    We use CryptoSwift's AES class in CBC mode with the extracted IV and the key. We also use PKCS7 padding, which is compatible with Fernet's encryption.

  4. Handling the Decrypted Data:
    After decrypting, the result is converted back into a string if the result is valid UTF-8 data.

Conclusion:

This Swift code provides a basic way to decrypt data that was encrypted using cryptography.fernet in Python. It involves parsing the Fernet ciphertext structure, using AES-CBC to decrypt it, and handling padding and other required parts of the Fernet specification.

If you need to do this in production, ensure you handle HMAC verification properly, as Fernet encryption includes an integrity check (HMAC) to ensure that the data has not been tampered with. CryptoSwift does not have built-in support for HMAC verification with Fernet, so you would need to implement it separately if necessary.