Obfuscatie (deel 2) – Encryptie met Fernet

Na de tutorial over het verhullen van data in Obfuscatie (deel 1), heb ik de volgende stap gezet en heb op basis van hetzelfde script de versleuteling naar een hoger niveau gebracht met de Fernet versleuteling. Het grootste voordeel van Fernet-versleuteling is de combinatie van gebruiksgemak en veiligheid. Het is een vorm van symmetrische encryptie waarbij data wordt versleuteld en geauthenticeerd. 

Fernet is een methode voor symmetrische encryptie. Dit betekent dat dezelfde geheime sleutel wordt gebruikt om data te versleutelen én te ontsleutelen. Het garandeert zowel de vertrouwelijkheid (de data is onleesbaar) als de authenticiteit (de data is niet gemanipuleerd) van je gegevens.

De belangrijkste voordelen zijn:

  • Geïntegreerde authenticatie: Fernet voegt een handtekening (HMAC) toe aan de data. Hierdoor controleert het systeem automatisch of een bericht tijdens verzending of opslag is aangepast.
  • Idioot-proof: Het is ontworpen om veelvoorkomende programmeerfouten te voorkomen. De versleuteling regelt zelfstandig complexe zaken zoals padding en het genereren van een unieke Initialization Vector (IV).
  • Standaardisatie: Fernet gebruikt bewezen, sterke standaarden zoals AES (128-bits in CBC-modus) en SHA256 voor hashing.
  • Tijdstempels: Ingebouwde tijdstempels maken het eenvoudig om berichten na een bepaalde periode automatisch te laten verlopen of weigeren.
  • Sleutelrotatie: Ondersteunt MultiFernet, waardoor je periodiek van sleutel kunt wisselen zonder oude data direct onleesbaar te maken.

Fernet-encryptie omvat een aantal belangrijke componenten:

  • Sleutel: Een geheime sleutel die nodig is voor zowel encryptie als decryptie. Fernet gebruikt een 128-bits sleutel, die op een veilige manier wordt gegenereerd.
  • Initialisatievector (IV): Een unieke waarde die ervoor zorgt dat dezelfde platte tekst elke keer naar een andere versleutelde tekst wordt versleuteld.

Gebruikt Fernet AES-256?

Nee, Fernet gebruikt geen AES-256. Het maakt gebruik van AES-128 in combinatie met HMAC-SHA256 voor authenticatie. Fernet heeft een sleutel van 256 bits nodig die in tweeën is gesplitst. De eerste helft is de ondertekeningssleutel, de tweede helft de versleutelingssleutel. Omdat ze 128 bits lang zijn, gebruiken we AES-128.

Hoe veilig is AES-128?

AES-128 is extreem veilig! Het is de industriestandaard voor de meeste commerciële toepassingen, zoals VPN’s, chatapps en wachtwoordmanagers. Zelfs met de meest geavanceerde supercomputers zou het miljarden jaren duren om een AES-128 sleutel te kraken.

Hier is een kort overzicht van de beveiliging:

  • Brute-force: Het aantal mogelijke combinaties is 2¹²⁸ (dit zijn 39 cijfers). Zelfs als een miljard computers elk een miljard sleutels per seconde proberen, zijn ze nog steeds miljarden jaren bezig.
  • Praktische aanvallen: Er zijn geen bekende praktische methoden om de AES-128 cijfertekst te kraken zonder de sleutel. De wiskundige opbouw is wereldwijd grondig geanalyseerd en goedgekeurd door cryptografen en overheden.
  • Kwantumcomputers: Dit is het enige punt van discussie. Hoewel AES-128 immuun is voor traditionele computers, wordt verwacht dat kwantumcomputers in de toekomst de effectieve beveiliging kunnen halveren tot 64 bits via Grovers algoritme. Hoewel dit nog steeds zeer moeilijk is, stappen overheidsinstanties en zeer strikte beveiligingssectoren (zoals de NSA) in de aanloop naar de toekomst over naar AES-256 voor uiterst geheime data.

AES-128 heeft een uitstekende balans tussen snelheid, efficiëntie en ondoordringbaarheid. Tenzij je werkt met strikt vertrouwelijke staatsgeheimen die over 20 tot 30 jaar nog steeds geheim moeten zijn, is AES-128 voorlopig meer dan veilig genoeg.

Genoeg over FERNET AES-128. Aan de slag met de code!

Crypto bibliotheek

Om de Fernet cryptografie toe te kunnen passen installeren we eerst de Python bibliotheek met:

pip install cryptography

Bestanden downloaden

Door op onderstaande link te klikken download je de ge-update keymanager waarmee eenmalig de sleutel aangemaakt wordt en het hoofdprogramma dat, net als in deel 1, het SSID en het wachtwoord in een apart bestand opslaat, deze keer in een stevige Fernet encryptie.

Voor een goede werking moeten beide bestanden in dezelfde map staan.

Code

Het script start je op met: python wifi_config.py.

#!/usr/bin/env python3

"""
wifi_config_Fernet.py – Raspberry Pi WiFi credential manager
Versleutelt SSID en wachtwoord met Fernet (AES-128-CBC + HMAC-SHA256).
De sleutel wordt geladen vanuit 'secret.key' via key_manager.py.
"""

import os
import sys
import json
import hashlib
import getpass

try:
    from key_manager import ensure_fernet, KEY_FILE
except ImportError:
    sys.exit("✘  'key_manager.py' niet gevonden. Zet beide bestanden in dezelfde map.")

CONFIG_FILE = "wifi_credentials.dat"

# Laad (of genereer) de Fernet-instantie bij opstarten
FERNET = ensure_fernet()

# ── Versleuteling helpers ─────────────────────────────────────────────────────

def encrypt(text: str) -> str:
    """Versleutel een string met Fernet → geef URL-safe base64 token terug."""
    token = FERNET.encrypt(text.encode("utf-8"))
    return token.decode("ascii")

def decrypt(token: str) -> str:
    """Ontsleutel een Fernet-token → geef de originele string terug."""
    plain = FERNET.decrypt(token.encode("ascii"))
    return plain.decode("utf-8")

def _checksum(data: dict) -> str:
    """Integriteitscontrole over de versleutelde waarden."""
    combined = data["ssid"] + data["password"]
    return hashlib.sha256(combined.encode()).hexdigest()[:16]

# ── Opslaan / lezen ───────────────────────────────────────────────────────────

def save_credentials(ssid: str, password: str) -> None:
    """Versleutel en sla WiFi-gegevens op in CONFIG_FILE."""
    payload = {
        "ssid":     encrypt(ssid),
        "password": encrypt(password),
    }
    payload["_chk"] = _checksum(payload)

    with open(CONFIG_FILE, "w") as f:
        json.dump(payload, f, indent=2)

    print(f"\n✔  Gegevens versleuteld opgeslagen in '{CONFIG_FILE}'")

def load_credentials() -> tuple[str, str]:
    """Laad en ontsleutel de opgeslagen WiFi-gegevens."""
    if not os.path.exists(CONFIG_FILE):
        raise FileNotFoundError(f"Bestand '{CONFIG_FILE}' niet gevonden.")

    with open(CONFIG_FILE, "r") as f:
        payload = json.load(f)

    # Integriteitscontrole
    check = {k: v for k, v in payload.items() if k != "_chk"}
    if _checksum(check) != payload.get("_chk", ""):
        raise ValueError("Integriteitscontrole mislukt – bestand mogelijk aangepast!")

    try:
        ssid     = decrypt(payload["ssid"])
        password = decrypt(payload["password"])
    except Exception:
        raise ValueError(
            "Ontsleuteling mislukt – verkeerde sleutel of beschadigde data.\n"
            "Controleer of 'secret.key' overeenkomt met de sleutel waarmee\n"
            "de gegevens oorspronkelijk zijn opgeslagen."
        )

    return ssid, password

# ── Menu ──────────────────────────────────────────────────────────────────────

def menu_save() -> None:
    print ("\033[1;1;1m,\033c")
    print ("\033[1;32m")
    print("\n─── WiFi-gegevens opslaan ───")
    ssid = input("Voer de SSID (netwerknaam) in: ").strip()
    if not ssid:
        print("✘  SSID mag niet leeg zijn.")
        return

    password = getpass.getpass("Voer het WiFi-wachtwoord in: ")
    confirm  = getpass.getpass("Bevestig het wachtwoord:     ")

    if password != confirm:
        print("✘  Wachtwoorden komen niet overeen.")
        return

    save_credentials(ssid, password)

def menu_show() -> None:
    print ("\033[1;32m")
    print("\n─── Opgeslagen gegevens tonen ───")
    try:
        ssid, password = load_credentials()
        print(f"  SSID      : {ssid}")
        print(f"  Wachtwoord: {password}")
    except (FileNotFoundError, ValueError) as e:
        print(f"✘  {e}")

def menu_raw() -> None:
    """Toon de ruwe (versleutelde) inhoud van het bestand."""
    print ("\033[1;32m")
    print("\n─── Ruwe bestandsinhoud ───")
    if not os.path.exists(CONFIG_FILE):
        print(f"✘  '{CONFIG_FILE}' bestaat nog niet.")
        return
    with open(CONFIG_FILE, "r") as f:
        print(f.read())

def menu_key_info() -> None:
    """Toon informatie over de geladen sleutel."""
    print ("\033[1;32m")
    print(f"\n─── Sleutelinformatie ───")
    print(f"  Bestand    : {KEY_FILE}")
    print(f"  Algoritme  : Fernet (AES-128-CBC + HMAC-SHA256)")

# ── Hoofdprogramma ────────────────────────────────────────────────────────────

def main() -> None:
    print ("\033[1;1;1m,\033c")
    print ("\033[1;37m")
    print ("╔══════════════════════════════════╗")
    print ("║ Raspberry Pi – WiFi Configuratie ║")
    print ("╚══════════════════════════════════╝")
    print (f"  Sleutel geladen uit: {KEY_FILE}")

    while True:
        print ("\033[1;37m")
        print("\nKies een optie:")
        print("  1) WiFi-gegevens opslaan / bijwerken")
        print("  2) Opgeslagen gegevens tonen")
        print("  3) Ruwe bestandsinhoud bekijken")
        print("  4) Sleutelinformatie tonen")
        print("  5) Afsluiten")

        keuze = input("\nOptie: ").strip()

        if keuze == "1":
            menu_save()
        elif keuze == "2":
            menu_show()
        elif keuze == "3":
            menu_raw()
        elif keuze == "4":
            menu_key_info()
        elif keuze == "5":
            print("Tot ziens!")
            sys.exit(0)
        else:
            print("✘  Ongeldige keuze, probeer opnieuw.")

if __name__ == "__main__":
    main()

De code van wifi_config.py

Note: Je zult gemerkt hebben dat bij de eerste keer opstarten er een melding verschijnt:

ℹ Geen sleutelbestand gevonden – nieuwe Fernet-sleutel wordt aangemaakt …
✔ Fernet-sleutel opgeslagen in ‘secret.key’ (rechten: 600)

Tenzij het bestand secret.key niet beschikbaar is, komt de melding in principe maar een keer voor. Het hoofdprogramma zorgt ervoor dat de benodigde bestanden (secret.key via keymanager.py en wifi_credentials.dat) automatisch aangemaakt worden.

Let op! Het mag duidelijk zijn dat als het secret.key-bestand beschadigd of niet beschikbaar is, dan de data in het wifi_credentials.dat-bestand NIET bruikbaar is voor het hoofdprogramma!

Conclusie

Zoals ik met de tutorial begon met de volgende stap naar een betere versleuteling. Met Fernet is een hele grote stap gezet naar een zware encryptie oplossing. Al spelenderwijs gebruiken we een keymaker die AES128 versleuteling gebruikt en onze data van een zware encryptie voorziet.

Ik noem het nog maar een keer. De aangemaakte sleutel (secret.key) moet ergens diep worden weggestopt, zoals in een digitale kluis. Wil je gebruik maken van de versleutelde data in het bestand wifi_credentials.dat, dan zal je de secret.key aan het hoofdprogramma (wifi_config.py) beschikbaar moeten stellen.

Kan het hoofdprogramma de sleutel niet vinden of is het bestand beschadigd, zal de volgende melding verschijnen:

✘ Ontsleuteling mislukt – verkeerde sleutel of beschadigde data.
Controleer of ‘secret.key’ overeenkomt met de sleutel waarmee de gegevens oorspronkelijk zijn opgeslagen.

Doe er je voordeel mee en maak er iets moois van!

Have A Nice Day!

Geef als eerste een reactie

Laat een reactie achter

Het e-mailadres wordt niet gepubliceerd.


*