Proste narzędzie do szyfrowania plików CSV

Załóżmy, że mamy zwykły katalog z plikami: notatki, CSV z danymi poufnymi, może jakiś eksport z aplikacji. Zróbmy więc mały program w Pythonie, który jednym poleceniem potrafi plik zaszyfrować, a drugim – odszyfrować.

W projekcie wykorzystujemy bibliotekę cryptography i narzędzie Fernet. Wygenerujemy klucz którym zaszyfrujemy i odszyfrujemy dane. Program jest celowo prosty – działa z terminala, nie ma bazy danych, nie udaje menedżera haseł.

Co znajdziesz poniżej

  • pełny kod skryptu py-cypher5.py,
  • opis funkcja po funkcji: klucz, szyfrowanie, deszyfrowanie, menu,
  • przykładowy plik CSV do testów,
  • konkretne komendy terminalowe i zrzut ekranu z działania.

Kod i użycie skryptu krok po kroku

Przejdźmy od pełnego listingu, przez omówienie poszczególnych funkcji, aż do uruchomienia gotowego narzędzia na realnym pliku.

Pełny skrypt py-cypher5.py

Najpierw spójrzmy na całość – to jeden plik z kilkoma funkcjami i prostym menu tekstowym.

Pełny plik py-cypher5.py wyświetlony w edytorze kodu z komentarzami
Widok całego skryptu w edytorze – od importu Fernet do prostego menu CLI.
Python – pełny skrypt
# Importujemy klasę Fernet z biblioteki cryptography.
# Fernet jest prostym sposobem na symetryczne szyfrowanie danych:
# jednym kluczem szyfrujemy i odszyfrowujemy plik (np. CSV).
from cryptography.fernet import Fernet


def generate_key():
    """
    Generuje nowy klucz Fernet i zapisuje go w pliku klucz.key.
    """
    klucz = Fernet.generate_key()
    with open("klucz.key", "wb") as plikklucz:
        plikklucz.write(klucz)
    print("Klucz zapisano w klucz.key")


def load_key():
    """
    Wczytuje klucz z pliku klucz.key w trybie binarnym.
    """
    return open("klucz.key", "rb").read()


def encrypt_file(targetfile, klucz):
    """
    Szyfruje plik targetfile przy użyciu podanego klucza.
    Wynik zapisuje jako plik o nazwie targetfile + '.enc'.
    """
    f = Fernet(klucz)
    with open(targetfile, "rb") as file:
        data = file.read()              # odczyt danych z pliku (bajty)
    encrypted = f.encrypt(data)         # szyfrowanie
    with open(targetfile + ".enc", "wb") as file:
        file.write(encrypted)           # zapis zaszyfrowanej zawartości
    print(f"Plik {targetfile} zaszyfrowano do {targetfile}.enc")


def decrypt_file(targetfile, klucz):
    """
    Deszyfruje plik targetfile (oczekiwany plik .enc)
    i zapisuje odszyfrowany plik bez rozszerzenia .enc.
    """
    f = Fernet(klucz)
    with open(targetfile, "rb") as file:
        encrypted = file.read()         # odczyt zaszyfrowanych danych
    decrypted = f.decrypt(encrypted)    # odszyfrowanie
    original = targetfile.replace(".enc", "")
    with open(original, "wb") as file:
        file.write(decrypted)           # zapis odszyfrowanego pliku
    print(f"Plik {targetfile} odszyfrowano do {original}")


if __name__ == "__main__":
    print("1: Generuj klucz")
    print("2: Szyfruj plik")
    print("3: Deszyfruj plik")
    choice = input("Wybierz opcję: ")

    if choice == "1":
        generate_key()
    elif choice in ["2", "3"]:
        klucz = load_key()
        targetfile = input("Podaj nazwę pliku: ")
        if choice == "2":
            encrypt_file(targetfile, klucz)
        else:
            decrypt_file(targetfile, klucz)
    else:
        print("Błąd: wybierz 1, 2 lub 3.")

Struktura pliku i odpowiedzialności funkcji

Import i kontekst

Na samej górze importujemy Fernet z biblioteki cryptography. Cały skrypt jest zbudowany wokół tego jednego prymitywu – reszta to tylko obsługa plików i prosty interfejs.

generate_key() – jednorazowe wygenerowanie klucza

Funkcja generate_key() odpowiada za stworzenie klucza, który będzie później używany zarówno do szyfrowania, jak i deszyfrowania. W środku:

  • Fernet.generate_key() – prosimy bibliotekę o bezpieczny klucz,
  • otwieramy plik klucz.key w trybie binarnym ("wb") i zapisujemy bajty bez żadnych modyfikacji,
  • wypisujemy krótki komunikat, żeby użytkownik widział, że operacja się powiodła.

W praktyce ta funkcja jest wywoływana rzadko – typowo raz na nowy „zestaw” danych.

load_key() – odczyt klucza z dysku

load_key() to komplementarna operacja: otwieramy klucz.key w trybie "rb" i zwracamy jego zawartość. Funkcja nie ma żadnych efektów ubocznych; jej jedynym zadaniem jest dostarczenie bajtów klucza kolejnym funkcjom.

encrypt_file() – szyfrowanie wskazanego pliku

Funkcja encrypt_file(targetfile, klucz) implementuje pełny przepływ szyfrowania:

  • tworzymy obiekt Fernet na podstawie przekazanego klucza,
  • otwieramy plik targetfile w trybie binarnym i czytamy całą zawartość,
  • wykonujemy f.encrypt(data), co daje zaszyfrowany, uwierzytelniony blob,
  • tworzymy nowy plik o nazwie targetfile + ".enc",
  • zapisujemy wynik szyfrowania na dysk i logujemy krótką informację w terminalu.

Skrypt nie usuwa oryginalnego pliku – decyzja o tym, czy kasować „jawną” wersję, jest pozostawiona użytkownikowi / procesowi, który wywołuje narzędzie.

decrypt_file() – operacja odwrotna

decrypt_file(targetfile, klucz) bierze plik zaszyfrowany (np. pliktest.csv.enc) i próbuje odtworzyć wersję jawną:

  • znów tworzymy obiekt Fernet z danego klucza,
  • wczytujemy cały zaszyfrowany plik do pamięci,
  • wykonujemy f.decrypt(encrypted) – jeśli klucz nie pasuje lub dane są uszkodzone, dostaniemy wyjątek,
  • konstruujemy nazwę wyjściową przez replace(".enc", ""),
  • zapisujemy odszyfrowane bajty jako zwykły plik.

Dzięki konwencji nazewniczej (.enc) odzyskujemy tę samą nazwę, którą plik miał przed szyfrowaniem, bez trzymania dodatkowych metadanych.

Blok if __name__ == "__main__" – prosty interfejs CLI

Na końcu pliku mamy cienką warstwę interfejsu tekstowego – kilka print() i input(), które spinają funkcje w jedno narzędzie.

Python – menu
if __name__ == "__main__":
    print("1: Generuj klucz")
    print("2: Szyfruj plik")
    print("3: Deszyfruj plik")
    choice = input("Wybierz opcję: ")

    if choice == "1":
        generate_key()
    elif choice in ["2", "3"]:
        klucz = load_key()
        targetfile = input("Podaj nazwę pliku: ")
        if choice == "2":
            encrypt_file(targetfile, klucz)
        else:
            decrypt_file(targetfile, klucz)
    else:
        print("Błąd: wybierz 1, 2 lub 3.")

Logika wyboru jest celowo prosta:

  • opcję 1 obsługuje samo generate_key(),
  • dla opcji 2 i 3 najpierw wczytujemy klucz (load_key()), a dopiero potem prosimy o nazwę pliku,
  • dalszy przepływ przekazujemy do encrypt_file() lub decrypt_file(),
  • dowolny inny wybór kończymy komunikatem o błędzie – bez prób zgadywania intencji użytkownika.

Przygotowanie danych testowych

Do demonstracji używamy prostego pliku CSV – wystarczy zapisać go w tym samym katalogu co skrypt py-cypher5.py.

CSV – dane testowe
ID,Name,Age,City,Score
1,Alice,25,New York,85
2,Bob,30,Los Angeles,90
3,Charlie,22,Chicago,78
4,David,28,Houston,88
5,Eva,26,Miami,92

Uruchamianie skryptu w terminalu

Skrypt działa interaktywnie, ale scenariusz użycia jest zawsze podobny. Zróbmy to krok po kroku na przykładzie pliktest.csv.

Krok 0 – instalacja zależności

Instalacja cryptography
pip install cryptography

Krok 1 – wygenerowanie klucza

Najpierw tworzymy klucz, który będzie używany do wszystkich późniejszych operacji szyfrujących i deszyfrujących:

Generowanie klucza
python py-cypher5.py
# menu: wybieramy 1 (Generuj klucz)

W katalogu pojawia się klucz.key. W prawdziwym systemie taki sekret najczęściej trafia do menedżera haseł / KMS; tutaj zostaje lokalny plik, żeby skupić się na mechanice szyfrowania.

Krok 2 – szyfrowanie pliku

Skoro klucz już mamy, możemy zaszyfrować nasz roboczy CSV:

Szyfrowanie
python py-cypher5.py
# menu: wybieramy 2 (Szyfruj plik)
# podajemy nazwę: pliktest.csv

Po tej operacji obok pliku pliktest.csv pojawia się pliktest.csv.enc. To tę wersję możemy ewentualnie przechowywać długoterminowo lub wysyłać dalej.

Krok 3 – odszyfrowanie i weryfikacja

Kiedy potrzebujemy wrócić do danych w formie jawnej, uruchamiamy skrypt jeszcze raz:

Deszyfrowanie
python py-cypher5.py
# menu: wybieramy 3 (Deszyfruj plik)
# podajemy nazwę: pliktest.csv.enc

Program odtwarza pliktest.csv. Można go porównać z oryginałem – powinien być identyczny bajt po bajcie.

Przykładowa sesja – zrzut ekranu

Dla pełnego obrazu – tak wygląda rzeczywista sesja w terminalu, z wszystkimi trzema krokami po kolei.

Działanie programu py-cypher5.py w terminalu: generowanie klucza, szyfrowanie i deszyfrowanie pliku pliktest.csv
Generowanie klucza, szyfrowanie pliktest.csv do pliktest.csv.enc i ponowne odtworzenie pliku CSV.

Praktyczne uwagi

  • klucz.key jest krytyczny – bez niego Fernet nie odszyfruje danych.
  • Skrypt nie usuwa wersji jawnej – decyzję o tym zostawiamy użytkownikowi lub procesowi automatyzującemu.
  • Konwencja nazewnicza <nazwa>.enc upraszcza parowanie plików zaszyfrowanych z oryginałami.