paint-brush
Crittografia: Cifrari, Digest, Salt e IV: tutto ciò che devi saperedi@gliimlang
Nuova storia

Crittografia: Cifrari, Digest, Salt e IV: tutto ciò che devi sapere

di Gliimly14m2025/01/04
Read on Terminal Reader

Troppo lungo; Leggere

La crittografia è un metodo per trasformare i dati in una forma inutilizzabile che può essere resa utile solo tramite la decifratura. Qui imparerai come crittografare e decrittografare i dati utilizzando una password.
featured image - Crittografia: Cifrari, Digest, Salt e IV: tutto ciò che devi sapere
Gliimly HackerNoon profile picture

Che cosa è la crittografia?

La crittografia è un metodo per trasformare i dati in una forma inutilizzabile che può essere resa utile solo tramite la decifratura. Lo scopo è rendere i dati disponibili solo a coloro che possono decifrarli (ovvero, renderli utilizzabili). In genere, i dati devono essere crittografati per assicurarsi che non possano essere ottenuti in caso di accesso non autorizzato. È l'ultima linea di difesa dopo che un aggressore è riuscito a violare i sistemi di autorizzazione e il controllo degli accessi.

Ciò non significa che tutti i dati debbano essere crittografati, perché spesso i sistemi di autorizzazione e accesso possono essere sufficienti e, inoltre, c'è una penalità di performance per la crittografia e la decrittografia dei dati. Se e quando i dati vengono crittografati è una questione di pianificazione dell'applicazione e valutazione del rischio e, a volte, è anche un requisito normativo, come con HIPAA o GDPR.

I dati possono essere crittografati quando sono inattivi, ad esempio su disco, o durante il transito, ad esempio tra due parti che comunicano tramite Internet.

Qui imparerai come crittografare e decrittografare i dati utilizzando una password, nota anche come crittografia simmetrica. Questa password deve essere nota a entrambe le parti che scambiano informazioni.

Cifra, Digest, Sale, Iterazioni, IV

Per utilizzare la crittografia in modo corretto e sicuro, è necessario spiegare alcuni concetti.


Un cifrario è l'algoritmo utilizzato per la crittografia. Ad esempio, AES256 è un cifrario. L'idea di un cifrario è ciò a cui la maggior parte delle persone penserà quando si parla di crittografia.


Un digest è fondamentalmente una funzione hash che viene utilizzata per codificare e allungare la password (ovvero la chiave di crittografia) prima che venga utilizzata dal cifrario. Perché viene fatto? Innanzitutto, crea un hash ben randomizzato e di lunghezza uniforme di una chiave che funziona meglio per la crittografia. È anche molto adatto per il "salting", che è il prossimo di cui parleremo.


Il "sale" è un metodo per sconfiggere le cosiddette tabelle "arcobaleno". Un aggressore sa che due valori hash appariranno esattamente uguali se gli originali lo fossero. Tuttavia, se si aggiunge il valore sale all'hashing, allora non lo saranno. Si chiama "sale" perché è in un certo senso mescolato alla chiave per produrre qualcosa di diverso.


Ora, una tabella arcobaleno tenterà di abbinare valori hash noti con dati precalcolati nel tentativo di indovinare una password.


Di solito, il salt viene generato casualmente per ogni chiave e memorizzato con essa. Per far corrispondere hash noti, l'attaccante dovrebbe precalcolare le rainbow table per un gran numero di valori casuali, il che in genere non è fattibile.

Spesso sentirai parlare di "iterazioni" nella crittografia. Un'iterazione è un singolo ciclo in cui una chiave e un sale vengono mescolati in modo tale da rendere più difficile indovinare la chiave. Ciò viene fatto molte volte in modo da rendere computazionalmente difficile per un aggressore indovinare la chiave al contrario, da cui "iterazioni" (plurale). In genere, il numero minimo di iterazioni richiesto è 1000, ma può essere diverso. Se inizi con una password molto forte, in genere ne servono meno.

IV (o "Initialization Vector") è in genere un valore casuale che viene utilizzato per la crittografia di ogni messaggio. Ora, salt viene utilizzato per produrre una chiave basata su una password. E IV viene utilizzato quando si ha già una chiave e ora si stanno crittografando i messaggi. Lo scopo di IV è di far apparire gli stessi messaggi in modo diverso quando vengono crittografati. A volte, IV ha anche una componente sequenziale, quindi è costituito da una stringa casuale più una sequenza che aumenta costantemente.


Ciò rende difficili gli attacchi "replay", ovvero quando l'attaccante non ha bisogno di decifrare un messaggio; piuttosto, un messaggio criptato è stato "sniffato" (vale a dire, intercettato tra il mittente e il destinatario) e poi riprodotto, sperando di ripetere l'azione già eseguita. Sebbene in realtà, la maggior parte dei protocolli di alto livello abbia già una sequenza in atto, in cui ogni messaggio ha, come parte di esso, un numero di pacchetti crescente, quindi nella maggior parte dei casi IV non ne ha bisogno.

Prerequisiti

Questo esempio usa il framework Gliimly . Installalo prima.

Esempio di crittografia

Per eseguire gli esempi qui, creare un'applicazione "enc" in una directory a sé stante (vedere mgrg per maggiori informazioni sul gestore dei programmi di Gliimly):

 mkdir enc_example cd enc_example gg -k enc

Per crittografare i dati, usa l'istruzione encrypt-data . La forma più semplice è crittografare una stringa terminata da null. Crea un file "encrypt.gliim" e copia questo:

 begin-handler /encrypt public set-string str = "This contains a secret code, which is Open Sesame!" // Encrypt encrypt-data str to enc_str password "my_password" p-out enc_str @ // Decrypt decrypt-data enc_str password "my_password" to dec_str p-out dec_str @ end-handler

Puoi vedere l'uso di base di encrypt-data e decrypt-data . Fornisci i dati (originali o criptati) e la password, e via. I dati vengono criptati e poi decriptati, ottenendo l'originale.

Nel codice sorgente, una variabile stringa "enc_str" (creata come "char *") conterrà la versione crittografata di "Questo contiene un codice segreto: Apriti Sesamo!" e "dec_str" saranno i dati decrittografati che devono essere esattamente gli stessi.

Per eseguire questo codice dalla riga di comando, creare prima l'applicazione:

 gg -q

Quindi fai in modo che Gliimly produca il codice bash per eseguirlo: il percorso della richiesta è "/encrypt", che, nel nostro caso, è gestito dalla funzione "void encrypt()" definita nel file sorgente "encrypt.gliim". In Gliimly, questi nomi corrispondono sempre, rendendo facile scrivere, leggere ed eseguire il codice. Usa l'opzione "-r" in gg per specificare il percorso della richiesta e ottenere il codice necessario per eseguire il programma:

 gg -r --req="/encrypt" --silent-header --exec


Riceverai una risposta come questa:

 72ddd44c10e9693be6ac77caabc64e05f809290a109df7cfc57400948cb888cd23c7e98e15bcf21b25ab1337ddc6d02094232111aa20a2d548c08f230b6d56e9 This contains a secret code, which is Open Sesame!


Ciò che hai qui sono i dati criptati, e poi questi dati criptati vengono decriptati usando la stessa password. Non sorprende che il risultato corrisponda alla stringa che hai criptato in primo luogo.

Nota che per impostazione predefinita, encrypt-data produrrà un valore crittografato in una forma esadecimale leggibile dall'uomo, ovvero composta da caratteri esadecimali da "0" a "9" e da "a" a "f". In questo modo, puoi archiviare i dati crittografati in una stringa normale. Ad esempio, possono andare a un documento JSON, a una colonna VARCHAR in un database o praticamente ovunque. Tuttavia, puoi anche produrre dati crittografati binari. Ne parleremo più avanti.

Crittografare i dati in un risultato binario

Nell'esempio precedente, i dati crittografati risultanti sono in una forma esadecimale leggibile dall'uomo. Puoi anche creare dati crittografati binari, che non sono una stringa leggibile dall'uomo e sono anche più corti. Per farlo, usa la clausola "binary". Sostituisci il codice in "encrypt.gliim" con:

 begin-handler /encrypt public set-string str = "This contains a secret code, which is Open Sesame!" // Encrypt encrypt-data str to enc_str password "my_password" binary // Save the encrypted data to a file write-file "encrypted_data" from enc_str get-app directory to app_dir @Encrypted data written to file <<p-out app_dir>>/encrypted_data // Decrypt data decrypt-data enc_str password "my_password" binary to dec_str p-out dec_str @ end-handler

Quando vuoi ottenere dati crittografati binari, dovresti ottenere anche la loro lunghezza in byte, altrimenti non saprai dove finiscono, poiché potrebbero contenere byte nulli. Usa la clausola "output-length" a tale scopo. In questo codice, i dati crittografati nella variabile "enc_str" vengono scritti nel file "encrypted_data" e la lunghezza scritta è "outlen" byte.


Quando un file viene scritto senza un percorso, viene sempre scritto nella directory home dell'applicazione (vedere directory ), quindi per ottenere tale directory si può usare get-app .

Quando si decifrano i dati, si noti l'uso della clausola "input-length". Indica quanti byte hanno i dati crittografati. Ovviamente, è possibile ottenerli dalla variabile "outlen", dove encrypt-data memorizza la lunghezza dei dati crittografati. Quando crittografia e decrittografia sono disaccoppiate, ovvero eseguite in programmi separati, ci si assicura che questa lunghezza sia resa disponibile.

Si noti inoltre che quando i dati vengono crittografati in modo "binario" (ovvero producendo un output binario), la decrittazione deve utilizzare lo stesso metodo.

Effettuare la richiesta:

 gg -q


Eseguilo come prima:

 gg -r --req="/encrypt" --silent-header --exec


Il risultato è:

 Encrypted data written to file /var/lib/gg/enc/app/encrypted_data This contains a secret code, which is Open Sesame!


I dati decifrati sono esattamente gli stessi dell'originale.

È possibile visualizzare i dati effettivamente crittografati scritti nel file utilizzando l'utilità Linux "octal dump" ("od"):

 od -c /var/lib/gg/enc/app/encrypted_data


con il risultato come:

 $ od -c /var/lib/gg/enc/app/encrypted_data 0000000 r 335 324 L 020 351 i ; 346 254 w 312 253 306 N 005 0000020 370 \t ) \n 020 235 367 317 305 t \0 224 214 270 210 315 0000040 # 307 351 216 025 274 362 033 % 253 023 7 335 306 320 0000060 224 # ! 021 252 242 325 H 300 217 # \vm V 351 0000100

Ecco fatto. Noterai che i dati sono binari e contengono effettivamente il/i byte nullo/i.

Crittografare i dati binari

I dati da crittografare in questi esempi sono una stringa, ovvero delimitata da null. Puoi crittografare dati binari con la stessa facilità specificandoli interi (dato che Gliimly tiene traccia di quanti byte ci sono!) o specificandone la lunghezza nella clausola "input-length", ad esempio, copia questo in "encrypt.gliim":

 begin-handler /encrypt public set-string str = "This c\000ontains a secret code, which is Open Sesame!" // Encrypt encrypt-data str to enc_str password "my_password" input-length 12 p-out enc_str @ // Decrypt decrypt-data enc_str password "my_password" to dec_str // Output binary data; present null byte as octal \000 string-length dec_str to res_len start-loop repeat res_len use i start-with 0 if-true dec_str[i] equal 0 p-out "\\000" else-if pf-out "%c", dec_str[i] end-if end-loop @ end-handler

Questo crittograferà 12 byte nella posizione di memoria "enc_str" indipendentemente da qualsiasi byte nullo. In questo caso, è "This c" seguito da un byte nullo seguito da una stringa "ontain", ma può essere qualsiasi tipo di dato binario, ad esempio, il contenuto di un file JPG.

Sul lato della decifratura, otterresti il numero di byte decifrati nella clausola "output-length". Infine, i dati decifrati vengono mostrati come esattamente gli originali e il byte nullo viene presentato in una tipica rappresentazione ottale.

Effettuare la richiesta:

 gg -q


Eseguilo come prima:

 gg -r --req="/encrypt" --silent-header --exec


Il risultato è:

 6bea45c2f901c0913c87fccb9b347d0a This c\000ontai


Il valore crittografato è più breve perché anche in questo caso i dati sono più brevi e il risultato corrisponde esattamente all'originale.

Utilizzare qualsiasi cifrario o digest

La crittografia utilizzata di default è AES256 e SHA256 hashing dalla libreria standard OpenSSL , entrambe ampiamente utilizzate in crittografia. Tuttavia, puoi usare qualsiasi cifrario e digest (ad esempio, hash) disponibile supportato da OpenSSL (anche quelli personalizzati che fornisci).

Per vedere quali algoritmi sono disponibili, procedere come segue nella riga di comando:

 #get list of cipher providers openssl list -cipher-algorithms #get list of digest providers openssl list -digest-algorithms


Questi due forniranno un elenco di algoritmi di cifratura e digest (hash). Alcuni di essi potrebbero essere più deboli di quelli predefiniti scelti da Gliimly, e altri potrebbero essere lì solo per la retrocompatibilità con sistemi più vecchi. Tuttavia, altri potrebbero essere piuttosto nuovi e non hanno avuto abbastanza tempo per essere convalidati nella misura in cui potresti desiderarli.


Quindi, fai attenzione quando scegli questi algoritmi e assicurati di sapere perché stai cambiando quelli predefiniti. Detto questo, ecco un esempio di utilizzo della crittografia Camellia-256 (vale a dire, "CAMELLIA-256-CFB1") con digest "SHA3-512". Sostituisci il codice in "encrypt.gliim" con:

 begin-handler /encrypt public set-string str = "This contains a secret code, which is Open Sesame!" // Encrypt data encrypt-data str to enc_str password "my_password" \ cipher "CAMELLIA-256-CFB1" digest "SHA3-512" p-out enc_str @ // Decrypt data decrypt-data enc_str password "my_password" to dec_str \ cipher "CAMELLIA-256-CFB1" digest "SHA3-512" p-out dec_str @ end-handler


Effettuare la richiesta:

 gg -q


Eseguilo:

 gg -r --req="/encrypt" --silent-header --exec


In questo caso il risultato è:

 f4d64d920756f7220516567727cef2c47443973de03449915d50a1d2e5e8558e7e06914532a0b0bf13842f67f0a268c98da6 This contains a secret code, which is Open Sesame!

Di nuovo, ottieni i dati originali. Nota, devi usare lo stesso cifrario e digest sia in encrypt-data che in decrypt-data!

Naturalmente è possibile produrre il valore binario crittografato proprio come prima utilizzando le clausole "binary" e "output-length".

Se hai sistemi esterni che criptano i dati e sai quale cifrario e digest usano, puoi abbinarli e rendere il tuo codice interoperabile. Gliimly usa la libreria OpenSSL standard, quindi è probabile che anche altri software possano farlo.

Utilizzo del sale

Per aggiungere sale alla crittografia, usa la clausola "salt". Puoi generare sale casuale usando l'istruzione random-string (o random-crypto se necessario). Ecco il codice per "encrypt.gliim":

 begin-handler /encrypt public set-string str = "This contains a secret code, which is Open Sesame!" // Get salt random-string to rs length 16 // Encrypt data encrypt-data str to enc_str password "my_password" salt rs @Salt used is <<p-out rs>>, and the encrypted string is <<p-out enc_str>> // Decrypt data decrypt-data enc_str password "my_password" salt rs to dec_str p-out dec_str @ end-handler


Effettuare la richiesta:

 gg -q


Eseguilo più volte:

 gg -r --req="/encrypt" --silent-header --exec gg -r --req="/encrypt" --silent-header --exec gg -r --req="/encrypt" --silent-header --exec


Il risultato:

 Salt used is VA9agPKxL9hf3bMd, and the encrypted string is 3272aa49c9b10cb2edf5d8a5e23803a5aa153c1b124296d318e3b3ad22bc911d1c0889d195d800c2bd92153ef7688e8d1cd368dbca3c5250d456f05c81ce0fdd This contains a secret code, which is Open Sesame! Salt used is FeWcGkBO5hQ1uo1A, and the encrypted string is 48b97314c1bc88952c798dfde7a416180dda6b00361217ea25278791c43b34f9c2e31cab6d9f4f28eea59baa70aadb4e8f1ed0709db81dff19f24cb7677c7371 This contains a secret code, which is Open Sesame! Salt used is nCQClR0NMjdetTEf, and the encrypted string is f19cdd9c1ddec487157ac727b2c8d0cdeb728a4ecaf838ca8585e279447bcdce83f7f95fa53b054775be1bb2de3b95f2e66a8b26b216ea18aa8b47f3d177e917 This contains a secret code, which is Open Sesame!

Come puoi vedere, viene generato un valore salt casuale (lungo 16 byte in questo caso) per ogni crittografia, e il valore crittografato è diverso ogni volta, anche se i dati crittografati erano gli stessi! Questo rende difficile decifrare una crittografia come questa.

Naturalmente, per decifrare, devi registrare il sale e usarlo esattamente come hai fatto durante la crittografia. Nel codice qui, la variabile "rs" contiene il sale. Se memorizzi i valori crittografati nel database, probabilmente memorizzeresti il sale proprio accanto ad esso.

Vettore di inizializzazione

In pratica, non useresti un valore salt diverso per ogni messaggio. Crea una nuova chiave ogni volta, e questo può ridurre le prestazioni. E non ce n'è davvero bisogno: l'uso del salt serve a rendere ogni chiave (anche le stesse) molto più difficile da indovinare. Una volta fatto questo, potresti non aver bisogno di farlo di nuovo, o spesso.

Invece, useresti un IV (Initialization Vector) per ogni messaggio. Di solito è una stringa casuale che fa apparire gli stessi messaggi diversi e aumenta il costo computazionale per decifrare la password. Ecco il nuovo codice per "encrypt.gliim":

 begin-handler /encrypt public // Get salt random-string to rs length 16 // Encrypt data start-loop repeat 10 use i start-with 0 random-string to iv length 16 encrypt-data "The same message" to enc_str password "my_password" salt rs iterations 2000 init-vector iv cache @The encrypted string is <<p-out enc_str>> // Decrypt data decrypt-data enc_str password "my_password" salt rs iterations 2000 init-vector iv to dec_str cache p-out dec_str @ end-loop end-handler


Effettuare la richiesta:

 gg -q


Eseguilo più volte:

 gg -r --req="/encrypt" --silent-header --exec gg -r --req="/encrypt" --silent-header --exec gg -r --req="/encrypt" --silent-header --exec


Il risultato potrebbe essere:

 The encrypted string is 787909d332fd84ba939c594e24c421b00ba46d9c9a776c47d3d0a9ca6fccb1a2 The same message The encrypted string is 7fae887e3ae469b666cff79a68270ea3d11b771dc58a299971d5b49a1f7db1be The same message The encrypted string is 59f95c3e4457d89f611c4f8bd53dd5fa9f8c3bbe748ed7d5aeb939ad633199d7 The same message The encrypted string is 00f218d0bbe7b618a0c2970da0b09e043a47798004502b76bc4a3f6afc626056 The same message The encrypted string is 6819349496b9f573743f5ef65e27ac26f0d64574d39227cc4e85e517f108a5dd The same message The encrypted string is a2833338cf636602881377a024c974906caa16d1f7c47c78d9efdff128918d58 The same message The encrypted string is 04c914cd9338fcba9acb550a79188bebbbb134c34441dfd540473dd8a1e6be40 The same message The encrypted string is 05f0d51561d59edf05befd9fad243e0737e4a98af357a9764cba84bcc55cf4d5 The same message The encrypted string is ae594c4d6e72c05c186383e63c89d93880c8a8a085bf9367bdfd772e3c163458 The same message The encrypted string is 2b28cdf5a67a5a036139fd410112735aa96bc341a170dafb56818dc78efe2e00 The same message

Puoi vedere che lo stesso messaggio appare diverso quando è crittografato, mentre quando è decrittografato è di nuovo lo stesso. Ovviamente, la password, il salt, il numero di iterazioni e l'init-vector devono essere gli stessi sia per la crittografia che per la decrittografia.

Nota l'uso della clausola "cache" in encrypt-data e decrypt-data. In pratica memorizza nella cache la chiave calcolata (data password, salt, algoritmi di cifratura/digest e numero di iterazioni), quindi non viene calcolata ogni volta nel ciclo. Con "cache", la chiave viene calcolata una volta, e poi viene utilizzato un IV diverso (nella clausola "init-vector") per ogni messaggio.

Se vuoi ricostruire occasionalmente la chiave, usa la clausola "clear-cache", che fornisce un valore booleano. Se è vero, la chiave viene ricalcolata, altrimenti, viene lasciata intatta. Vedi encrypt-data per maggiori informazioni.

Conclusione

Hai imparato come crittografare e decrittografare dati utilizzando diversi cifrari, digest, salt e valori IV in Gliimly. Puoi anche creare un valore crittografato leggibile dall'uomo e un output binario, oltre a crittografare sia stringhe che valori binari (come documenti).