angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Veilige gegevensoverdracht met Public Key versleuteling en pyNaCl

pyNaCl maken het heel eenvoudig om veilige gegevensoverdracht te implementeren.

2 december 2023 Bijgewerkt 2 december 2023
post main image
https://www.pexels.com/@reezky11

Dit is een korte post over het veilig overdragen van gegevens tussen twee personen. Hiervoor gebruiken we het Python pyNaCl pakket om private en publieke sleutels te genereren en de gegevens te versleutelen en te ontsleutelen. Ik heb ook het Python keyring pakket toegevoegd om de private_key en public_key op te slaan. Niet echt zo moeilijk. Ik had een basisklasse nodig om dit te doen en die deel ik hier. Misschien vind je het nuttig.

Zoals altijd ontwikkel ik op Ubuntu 22.04.

Hoe het werkt

Er zijn twee personen, Bob en Alice. Bob wil gegevens op een veilige manier over het internet naar Alice sturen. Hier gebruiken we Public Key-encryptie.

Bob genereert een unieke private_key en public_key. Hij houdt de private_key geheim en deelt de public_key met Alice. Alice volgt dezelfde procedure, houdt haar private_key geheim en deelt haar public_key met Bob.

Om de gegevens naar Alice te sturen, doet Bob het volgende:

  • Bob versleutelt de gegevens met de public_key van Alice
  • Bob ondertekent de gegevens met zijn eigen private_key

Wanneer Alice de versleutelde gegevens ontvangt, gebruikt ze haar eigen private_key om de gegevens te ontsleutelen en gebruikt ze de public_key van Bob om te verifiëren dat de gegevens inderdaad afkomstig zijn van Bob.

private_key en public_key opslaan

De meeste systemen hebben een plek waar wachtwoorden veilig worden opgeslagen, genaamd de keyring service van het systeem. Het Python keyring pakket kan gebruikt worden om een interface te maken met de keyring service van het systeem of om een standalone opslagoplossing te gebruiken.

Voor elke user van onze applicatie slaan we de public_key en (optioneel) private_key op in het wachtwoordveld, met behulp van een woordenboek dat wordt omgezet van en naar JSON.

De code

Maak een virtual environment en installeer het volgende:

> pip install pynacl
> pip install keyring

Je kunt kiezen voor Base64 of Hex codering voor de sleutels en gegevens. Base64 is veel efficiënter. Vergeet niet de sleutels te verwijderen als je overschakelt naar een andere codering!

# my_app.py
import json
import os
import sys
import traceback 

import keyring as kr

from nacl.utils import random
from nacl.public import Box, PrivateKey, PublicKey
from nacl.encoding import Base64Encoder, HexEncoder

class PKUtils:
    def __init__(
        self,
        key_encoding='hex',
        data_encoding='hex',
        kr_servicename=None,
    ):
        self.kr_servicename = kr_servicename
        self.key_encoder = Base64Encoder if key_encoding == 'base64' else HexEncoder
        self.data_encoder = Base64Encoder if data_encoding == 'base64' else HexEncoder

    def get_publ_key_from_priv_key(self, priv_key):
        priv_key_obj = PrivateKey(priv_key, encoder=self.key_encoder)
        publ_key_obj = priv_key_obj.public_key
        publ_key_encoded = publ_key_obj.encode(self.key_encoder)
        return publ_key_encoded.decode('ascii')

    def create_key_pair(self):
        priv_key_obj = PrivateKey.generate()
        priv_key_encoded = priv_key_obj.encode(self.key_encoder)
        priv_key = priv_key_encoded.decode('ascii')
        publ_key = self.get_publ_key_from_priv_key(priv_key)
        return priv_key, publ_key

    def get_box(self, sender_priv_key, receiver_publ_key):
        return Box(
            PrivateKey(sender_priv_key, encoder=self.key_encoder),
            PublicKey(receiver_publ_key, encoder=self.key_encoder)
        )

    def encrypt_data(self, sender_priv_key, receiver_publ_key, data):
        if isinstance(data, str):
            data = bytes(data, 'utf-8')
        box = self.get_box(sender_priv_key, receiver_publ_key)
        nonce = random(Box.NONCE_SIZE)
        encrypted_data = box.encrypt(data, nonce, encoder=self.data_encoder)
        return encrypted_data

    def decrypt_data(self, receiver_priv_key, sender_publ_key, encrypted_data, decode=None):
        box = self.get_box(receiver_priv_key, sender_publ_key)
        data = box.decrypt(encrypted_data, encoder=self.data_encoder)
        if decode is not None:
            data = data.decode('utf-8')
        return data

    def delete_key_pair_from_kr(self, username):
        try:
            kr.delete_password(self.kr_servicename, username)
        except:
            pass

    def get_key_pair_from_kr_or_create_new(self, username):
        try:
            keys = json.loads(kr.get_password(self.kr_servicename, username))
            return keys['priv_key'], keys['publ_key']
        except:
            pass
        priv_key, publ_key = self.create_key_pair()
        kr.set_password(
            self.kr_servicename,
            username,
            json.dumps({'priv_key': priv_key, 'publ_key': publ_key})
        )
        return priv_key, publ_key

    def set_public_key_in_kr(self, username, publ_key, keep_priv_key=False):
        priv_key_cur, publ_key_cur = self.get_key_pair_from_kr_or_create_new(username)
        if not keep_priv_key:
            priv_key_cur = None
        kr.set_password(
            self.kr_servicename,
            username,
            json.dumps({'priv_key': priv_key_cur, 'publ_key': publ_key})
        )
       

def main():
    pku = PKUtils(
        #key_encoding='base64',
        #data_encoding='base64',
        kr_servicename='my_app',
    )

    #pku.delete_key_pair_from_kr('bob')
    #pku.delete_key_pair_from_kr('alice')

    # bob gets/generates priv_key and publ_key
    bob_priv_key, bob_publ_key = pku.get_key_pair_from_kr_or_create_new('bob')
    print(f'bob_priv_key = {bob_priv_key}, bob_publ_key = {bob_publ_key}')

    # alice gets/generates priv_key and publ_key
    alice_priv_key, alice_publ_key = pku.get_key_pair_from_kr_or_create_new('alice')
    print(f'alice_priv_key = {alice_priv_key}, alice_publ_key = {alice_publ_key}')

    # bob wants to send this data
    data = """this is line 1
this is line 2
this is line 3
this is line 4
this is line 5
"""
    print(f'data = {data}')

    # bob encrypts the data
    try:
        encrypted_data = pku.encrypt_data(bob_priv_key, alice_publ_key, data)
        print(f'encrypted_data = {encrypted_data}')
    except Exception as e:
        print(f'encryption exception = {type(e).__name__}, e = {e}')
        traceback.print_exc() 

    # bob sends encrypted_data to alice ...

    # verify it is working, inject error
    #alice_priv_key = alice_publ_key
    #bob_publ_key = alice_publ_key

    # alice decrypts the received encrypted_data
    try:
        decrypted_data = pku.decrypt_data(alice_priv_key, bob_publ_key, encrypted_data, decode='utf-8')
        print(f'decrypted_data = {decrypted_data}')
    except Exception as e:
        print(f'decryption exception = {type(e).__name__}, e = {e}')
        traceback.print_exc() 

    print('ready')


if __name__ == '__main__':
    main() 

Samenvatting

Het Python pyNaCl pakket maakt het erg eenvoudig om public_key encryptie te implementeren. Publiekesleutelversleuteling is zwak voor man in the middle-aanvallen. Een derde partij kan de publieke sleutels aanpassen.
Vergeet ook niet dat de versleutelde gegevens onderweg gekopieerd kunnen worden. Dit betekent dat zodra een derde partij de sleutels in handen krijgt, alle (!) gegevens die door een derde partij (over een lange periode) zijn gekopieerd, kunnen worden ontcijferd. Om dit te voorkomen kun je je sleutels na verloop van tijd vernieuwen en de oude sleutels van je systeem verwijderen!

Links / credits

C 431: Public-Key Encryption With Sodium (25 extra)
https://samsclass.info/141/proj/C431.htm

Public Key Encryption
https://www.geeksforgeeks.org/public-key-encryption

PyNaCl
https://pynacl.readthedocs.io/en/latest

Lees meer

Cryptography

Laat een reactie achter

Reageer anoniem of log in om commentaar te geven.

Opmerkingen

Laat een antwoord achter

Antwoord anoniem of log in om te antwoorden.