<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>cryptography &amp;mdash; Cyberdyne Systems</title>
    <link>https://noblogo.org/aytin/tag:cryptography</link>
    <description>&#34;Fare o non fare. Non c&#39;è provare!&#34;</description>
    <pubDate>Thu, 30 Apr 2026 09:56:18 +0000</pubDate>
    <item>
      <title>Archiviare le password in sicurezza con KDF/password-hashing (Trilogia Della Password - 3 di 3)</title>
      <link>https://noblogo.org/aytin/archiviare-le-password-in-sicurezza-con-kdf-password-hashing-trilogia-della</link>
      <description>&lt;![CDATA[password hashing&#xA;&#xA;Dopo aver capito come creare password inviolabili anche avendo a disposizione tutta l&#39;energia termica dell&#39;universo, pensiamo al modo migliore per archiviarle.&#xA;&#xA;Pensare di lasciare la password raw in un database, rappresenta un grosso rischio in virtù di un possibile attacco offline.&#xA;!--more--&#xA;&#xA;Rainbow Table&#xA;Come ti blocco la Rainbow Table: Il “Salt” (sale)&#xA;Il livello successivo: Il Pepper&#xA;Final step: L’hashing&#xA;  PBKDF2&#xA;  Bcrypt&#xA;  Scrypt&#xA;  yescrypt&#xA;  Argon2&#xA;Un piccolo esempio: hashing di una password con Argon2&#xA;Gestione del pepper&#xA;HSM&#xA;Key / Algo Rotation&#xA;  Caso A: Algo rotation&#xA;  Caso B: Pepper rotation&#xA;&#xA;Una prima linea di difesa consiste nel memorizzare il digest della password, una stringa alfanumerica univoca generata da un apposito algoritmo, così da lasciare nelle mani dell&#39;attaccante degli oggetti che, per la loro non invertibilità, non permettono di risalire alle password.&#xA;&#xA;La scelta dell&#39;algoritmo di hashing diventa critica al fine di scongiurare altri tipi di attacchi. Ad es. SHA-256, pur essendo ottimo e molto efficiente per il digest e la firma anche di file di grandi dimensioni, mostra il fianco, proprio in virtù della sua velocità, nel caso di attacchi con:&#xA;&#xA;brute-force: si calcola l’hash di password casuali fino a trovare una corrispondenza,&#xA;dizionario: un&#39;alternativa intelligente alla forza bruta. Si punta alle password più comuni, si calcola l&#39;hash e si controlla se c&#39;è corrispondenza.&#xA;Rainbow Table: l&#39;attacco al dizionario più insidioso di tutti&#xA;&#xA;Oltre al fatto che, la funzione di hashing , essendo deterministica, permette di capire chi sono gli utenti che hanno la stessa password, dal momento che avranno lo stesso digest.&#xA;Rainbow Table&#xA;Una Rainbow Table è un enorme dizionario pre-calcolato che contiene:&#xA;&#xA;Milioni di password comuni.&#xA;Il relativo hash corrispondente.&#xA;&#xA;Invece di calcolare l’entropia di ogni tentativo, l’attaccante ruba il database degli hash e fa un semplice “Cerca e Trova”. Se l’hash della tua password è nella tabella, la tua password è violata in millisecondi, indipendentemente da quanto fosse alta la sua entropia teorica.&#xA;&#xA;smallOss.: Una password totalmente casuale, generata da un CSPRNG affidabile, con un&#39;alta entropia (  120), rimarrebbe comunque inviolabile anche dalla rainbow table perché la probabilità che quella password si trovi nel dizionario, sarebbe equivalente ad indovinarla./small&#xA;Come ti blocco la Rainbow Table: Il “Salt” (sale)&#xA;Per rendere inutili le Rainbow Table, i sistemi sicuri utilizzano il Salt. Il salt è una stringa di dati casuali (generati da una sorgente d’entropia affidabile ovviamente) che viene aggiunta alla password prima di calcolarne il digest.&#xA;&#xA;In questo modo le rainbow table vengono vanificate perché gli hash precalcolati sulle password raccolte, mancando il salt, non valgono più. Anche se due utenti avessero la stessa password, avrebbero degli hash completamente diversi.&#xA;&#xA;Anche per questo motivo non è un problema che il salt sia pubblico, perché il suo obiettivo non è nascondere quello che è un pezzetto di password a tutti gli effetti, ma di impedire economie di scala degli attacchi perché, pur potendo disporre offline di un database di decine di milioni di utenti, gli hash della mia Rainbow Table (che può arrivare a pesare anche decine di GB) andrebbero tutti ricalcolati per ogni utente, con un costo computazionale e di archiviazione inimmaginabile.&#xA;&#xA;Per riassumere, gli ingredienti di base sono:&#xA;&#xA;una buona sorgente d’entropia: una fonte di casualità certificata per generare un salt unico;&#xA;entropia della password: sempre buona norma, ove possibile, come sappiamo ormai fare (https://noblogo.org/aytin/come-generare-una-password-o-un-keyfile-sicuri-trilogia-della-password-1-di). Evita attacchi brute-force o al dizionario in cui l’attaccante prova a indovinare;&#xA;salt: protegge la password dagli attacchi basati su database pre-calcolati (Rainbow Table).&#xA;&#xA;Il livello successivo: Il Pepper&#xA;È vero che col salt andiamo a complicare lo sfruttamento di un attacco offline ma possiamo fare di meglio.&#xA;&#xA;Il punto d&#39;attenzione è che il salt protegge le password di tutti gli utenti.&#xA;Ma un attaccante potrebbe non essere affatto interessato a violare ogni singolo utente (niente economia di scala) ma solo alcuni. E allora l&#39;attacco attraverso Ranbow Table potrebbe essere di nuovo praticabile.&#xA;&#xA;Ma gli informatici sono dei gran giocherelloni, si sa.&#xA;Visto che abbiamo già il &#34;sale&#34;, perché non finire aggiustando con un po’ di “pepe”? Detto, fatto!&#xA;&#xA;Il pepper, come il salt, è un&#39;altra password generata con gli stessi criteri del salt ma le analogie finiscono qua perché:&#xA;&#xA;a differenza del salt che si trova nel database, il pepper è separato da ques&#39;ultimo. L&#39;ideale sarebbe un HSM;&#xA;Il salt è visibile a tutti, attaccante compreso. Il pepper è segreto. Un eventuale data breach che permette all&#39;attaccante di disporre offline di tutto il database degli utenti, &#34;vedrà&#34; certamente gli eventuali salt ma sarà ignaro del fatto che gli mancherà sempre un pezzo di chiave;&#xA;il salt è diverso per ogni utente, il pepper, di solito, è unico;&#xA;il salt serve a rendere uniche la password degli utenti, il pepper protegge l&#39;intero database da attacchi offline.&#xA;&#xA;Il pepper è la chiave di un HMAC, o di un meccanismo di cifratura simmetrica, applicato al digest della password (che ricordo essere salt+password in realtà), che sarà ciò che verrà archiviato.&#xA;&#xA;Va detto che l&#39;uso del &#34;pepper&#34; complica ulteriormente lo scenario di archiviazione.&#xA;Nella stragrande maggioranza dei casi è sufficiente scegliere un buon algoritmo di password hashing (vedi paragrafo successivo) per scoraggiare gli attaccanti.&#xA;&#34;Pepare&#34; le password prevederebbe, come detto sopra, l&#39;uso di un HSM per es, e tutta una serie di riflessioni di contorno che evidenzierò più avanti.&#xA;Final step: L&#39;hashing&#xA;L&#39;ultimo punto da dettagliare è l&#39;hash della password.&#xA;&#xA;L&#39;hash crittografico, in uso in questi casi, deve soddisfare le seguenti proprietà:&#xA;&#xA;resistenza alla pre-immagine: dato un hash h, deve essere impossibile trovare una password p t.c. H(p) = h (non invertibilità della funzione hash)&#xA;resistenza alla pre-immagine secondaria: dato una password psub1/sub, deve essere impossibile trovare un&#39;altra password psub2/sub t.c. H(psub1/sub) = H(psub2/sub) (resistenza debole alle collisioni)&#xA;resistenza alle collisioni: è impossibile trovare due password diverse, psub1/sub e psub2/sub, t.c. H(psub1/sub) = H(psub2/sub) (resistenza forte alle collisioni)&#xA;effetto valanga: il cambio di un solo bit della password deve cambiare radicalmente l&#39;intero hash&#xA;&#xA;In un sistema moderno, l&#39;hash non può essere delegato a funzioni di tipo SHA perché nascono per altri compiti,&#xA;&#xA;SHA-2 e SHA-3 nascono per il digest veloce, per verificare l&#39;integrità di file anche molto grossi o firmare documenti. La loro eccellente velocità diventa il loro più grosso difetto quando si parla di password.&#xA;Negli scenari precedenti di attachi offline, l&#39;hacker che dispone di una grossa potenza di calcolo, può ricostituire velocemente le rainbow table per n utenti. Magari non di tutti ma di quelli attenzionati.&#xA;&#xA;Le funzioni di derivazione della chiave (KDF) come pbkdf2 e quelle ancora più estreme come B/Scrypt, Argon2, oltre che soddisfare tutti i punti precedentemente elencati tipici di funzioni di password hashing, sono progettate per essere computazionalmente pesantissime da calcolare perché il loro scopo non è il digest ma la protezione di un segreto contro il brute-force.&#xA;E mentre le vecchie KDF come pbdfk2 sono CPU bound, ma non GPU bound, le KDF più moderne come Bcrypt, Scrypt ma soprattutto Argon2, agiscono pesantemente su tempo, memoria e parallelismo e l&#39;attacco offline di cui sopra diventa impraticabile.&#xA;PBKDF2&#xA;È il decano delle KDF. Applica iterativamente una funzione pseudorandomica, come HMAC con uno SHA, con salt alla password.&#xA;Il conteggio delle iterazioni è un parametro configurabile.&#xA;&#xA;PBDKF2 è uno standard di lunga data ampiamente adottato. Se non ci sono necessità stringenti di sicurezza o requisiti legacy, è una buona scelta.&#xA;&#xA;Il fatto di essere solo CPU bound però non la rende la scelta ideale in scenari dove gli attaccanti possono attingere a risorse di calcolo considerevoli&#xA;Bcrypt&#xA;Basato su Blowfish, anche Bcrypt usa un hash crittografico sulla password con parametri il salt e un fattore di costo.&#xA;&#xA;Il fattore di costo aumenta esponenzialmente il numero di iterazioni per adattarsi all&#39;aumento di potenza di calcolo dell&#39;hardware.&#xA;&#xA;Bcrypt è stato progetto per essere lento e resistente a semplici attacchi di forzat bruta.&#xA;Tuttavia, il basso utilizzo di ram richiesto dal calcolo lo rendono poco resistente ad attacchi sferrati usando hardware specializzato.&#xA;&#xA;Bcrypt ha dalla sua una storia solidissima in ragione della quale da 20 anni a questa parte non sono state trovate vulnerabilità critiche nel suo design.&#xA;&#xA;Per questo motivo Bcrypt cifra le password di sistema di OpenBSD dal 1999, come pure ha cifrato quelle di tante distro Linux per anni, prima che passassero ad Argon2 o yescrypt (default di Fedora).&#xA;&#xA;Domina nei framework web (\[Python\] Django, \[Ruby\] Ruby on Rails, \[PHP\] Laravel), \[Java\] Spring, Node.js), nelle applicazioni (Ansible / Terraform, Docker), nel web (la cifratura in .htpasswd di Apache e Nginx) visto che la sua semplcitià di implementazione gli ha permesso di trovarsi praticamente in ogni linguaggio.&#xA;&#xA;È presente come alternativa anche nei password manager benché molti di essi abbiano spostato il default verso Argon2 o PBKDF2 per conformità agli standard FIPS.&#xA;&#xA;È molto semplice implementare e anche da usare perché bisogna agire solo sul fattore di costo (consigliato almeno 10-12, altrimenti diventa troppo vulnerabile ad attacchi sferrati attraverso la GPU)&#xA;Scrypt&#xA;Rilasciato nel 2009, Scrypt è stato il primo algoritmo a introdurre il concetto di Memory Hardness ed è stato progettato per rendere economicamente poco conveniente il ricorso ad hardware specializzato come gli ASIC o i FPGA e incidere pesantemente su CPU, ram e parallelismo.&#xA;&#xA;Il suo alveo principale sono state le cripto-valute, molte monete lo usano per il mining.&#xA;&#xA;Scrypt lo troviamo in quasi tutti i linguaggi di programmazione, in Tarsnap, servizio di baclup online creato dallo stesso autore di Scrypt, è stato usato da LastPass ed è presente come opzione in VeraCrypt per derivare la chiave dalla password.&#xA;Fino ad Android 9 era l&#39;algoritmo usato per la FDE del dispositvio (passato poi al FBE) .&#xA;Presente anche su FreeBSD come opzione per la cifratura delle password di sistema e come opzione su LUKS per la cifratura degli slot delle chiavi.&#xA;&#xA;Su Scrypt i parametri da configurare sono:&#xA;&#xA;Costro CPU/Memoria (N): un parametro che aumenta i costi computazionali di cpu e memoria&#xA;DImensione del blocco (R): influenza la larghezza di banda della memoria&#xA;Parallelizzazione (p): indica quanto deve incidere sul calcolo parallelo&#xA;&#xA;In questo modo riesce ad essere sia CPU bound che GPU bound che, a differenza di Bcrypt, lo rende resistente anche ad attacchi facenti uso di hardware specializzato..&#xA;&#xA;Di contro, in ambiente in cui siamo vincolati dalle risorse disponibili, la sua potenza diventa un fattore limitante.&#xA;Quasi paragonabile ad Argon2 in quanto a robustezza, il suo unico tallone d&#39;Achille è la permeabilità ad attacchi di tipo side-channel. &#xA;yescrypt&#xA;Piccola menzione per yescrypt, appartenente alla famiglia &#34;Scrypt&#34;, pensato per essere ancora più resistente di Scrypt agli attacchi GPU e FPGA ma con una gestione più intelligente delle risorse.&#xA;&#xA;Grazie alle sue peculiarità, di fatto, è diventato il successore spirituale di Bcrypt nei sistemi operativi gnu/linux dove, a cominciare da Fedora, passando per Debian, Ubuntu, Arch, Kali, è il default per la cifratura delle password di sistema in /etc/shadow.&#xA;&#xA;È talmente incardinato ormai nei sistemi operativi, che è la libreria libxcrypt di yescrypt a gestire la tipica funzione crypt() di C che è la base della crittografia su tutti i sistemi gnu/linux moderni.&#xA;&#xA;La sua robustezza unita alla gestione intelligente delle risorse lo rende un coltellino svizzero di riferimento utile per es. per versione custom di LUKS su sistemi embedded, che magari fanno uso di cpu meno recenti,  oppure come opzione per strumenti di backup specialistici&#xA;&#xA;Di fatto, sui sistemi operativi, yescrypt s&#39;è guadagnato un consenso amplissimo dovuto alla sua scalabilità, alla sua capacità di usare anche la ROM per rendere il cracking ancora più difficile e senza pesare sulla RAM e alla sua compatibilità potendosi inserire perfettamente nella storica funzione crypt() di C come detto prima.&#xA;&#xA;Se Argon2 è il vincitore accademico avendo vinto il Password Hashing Competition del 2015, yescrypt per la sua robustezza, efficienza e flessibilità si ritaglia un profilo di indispensabilità nei sistemi operativi,&#xA;Argon2&#xA;E veniamo al dominatore indiscusso di questa che non è una llista esaustiva di KDF.&#xA;&#xA;Argon2 è LO standard moderno per il password hashing raccomandato da OWASP e IETF.&#xA;&#xA;È il riferimento per praticamente ogni password manager: Bitwarden, KeppasXC, 1Password, a cui assegnanp la protezione della Master Password&#xA;&#xA;È la scelta principale per la cifratura degli hard disk anche con impostazioni molto aggressive, in ragione delle quali un ritardo di mezzo secondo (un tempo enorme se venisse scalato esponenzialmente) nell&#39;apertura di un HD è assolutamente accettabile.&#xA;È il default di LUKS2 (LUKS1 usava PBKDF2) e di VeraCrypt, con cui ha sostituito SHA-512.&#xA;&#xA;Come Bcrypt, è implementato estensivamente su praticamente ogni frameword web e backend, da PHP, Django (Python), Laravel fino a Node.js.&#xA;&#xA;Nei sistemi operativi, laddove yescrytpt domina nella gestione delle password utente, Argon2 è usato per compiti più critici.&#xA;Dal kernel Linux per gestire internamente le chiavi crittografiche o da macOS / iOS, dove algoritmi proprietari ispirati fortemente ad Argon2, proteggono i dati nel Secure Enclave.&#xA;&#xA;Argon2  setta 3 parametri principali per regolare la sua forza:&#xA;&#xA;t: iterazioni, quante volte vengono rimescolati i dati (default Bitwarden = 3)&#xA;m: memoria, quanta ram deve occupare il calcolo. Questa è la misura anti-GPU (default Bitwarden = 16 (64MB))&#xA;p: parallelismo, quanti core della cpu usare. Questa è la misura anti-CPU (default Bitwarden = 4)&#xA;&#xA;La variante id è anche resistente agli attacchi side-channel perché impediscono a un attaccante di capire la password osservando i tempi di accesso alla memoria.&#xA;&#xA;Un piccolo esempio: hashing di una password con Argon2&#xA;Il grosso vantaggio degli algoritmi di kdf è che sono naturalmente resilienti rispetto all&#39;evoluzione tecnologica che produce macchine con sempre maggiore potenza di calcolo.&#xA;Da pbkdf2 in poi, il salt implicito che invalida le rainbow table precalcolate e la possibilità di calibrare il key stretching in moda da agire intensivamente su ram e cpu, permettono all&#39;algoritmo di adeguarsi per conservare la sua robustezza.&#xA;&#xA;Mini-script per l&#39;hashing di una password fornita dall&#39;utente con argon2 settato al default di Bitwarden:&#xA;echo -n &#34;Password: &#34;; read -s PASSWORD&#xA;Genero un Salt casuale di 128 bit&#xA;SALT=$(openssl rand -base64 128)&#xA;PASSWORDHASH=$(echo &#34;${PASSWORD}&#34; | argon2 &#34;${SALT}&#34; -m 16 -t 3 -p 4 -id -e)&#xA;&#xA;PASSWORDHASH e SALT sono i dati che verranno archiviati e, poiché argon2 &#34;frulla&#34; la password con un salt, è praticamente impossibile risalire alla password originale.&#xA;&#xA;La verifica è tuttavia banale perché, avendo il salt e la password da verificare, si ricrea l&#39;hash con argon2 e si confronta con l&#39;hash memorizzato.&#xA;&#xA;Per maggior sicurezza salt e digest possono essere memorizzati in punti differenti. L&#39;importante è che possano essere recuperate a partire dall&#39;utente.&#xA;Gestione del pepper&#xA;Col pepper le cose cambiano un pochino perché:&#xA;&#xA;deve essere archiviato con tutte le paranoie possibili in un punto diverso dal database degli utenti&#xA;il key rotation del pepper non è banale&#xA;&#xA;Mini-script che mostra come applicare salt e pepper all&#39;hashing di una password:&#xA;L&#39;utente inserisce la password&#xA;echo -n &#34;Password: &#34;; read -s PASSWORD&#xA;&#xA;Genero un Salt casuale di 128 bit unico per ogni utente&#xA;SALT=$(openssl rand -base64 128)&#xA;&#xA;Anche PEPPER sarà qualcosa del tipo &#34;openssl rand -base64 128&#34;&#xA;e si troverà in un punto esterno al database degli utenti.&#xA;PEPPER=$(getpepperfromext)&#xA;&#xA;Digest della password+salt&#xA;PASSWORDHASH=$(echo &#34;${PASSWORD}&#34; | argon2 &#34;${SALT}&#34; -m 16 -t 3 -p 4 -id -e)&#xA;&#xA;HMAC del digest con PEPPER come chiave&#xA;PASSWORDPEPPER=$(echo &#34;${PASSWORDHASH}&#34; | openssl dgst -sha256 -hmac &#34;${PEPPER}&#34; -binary | base64)&#xA;HSM&#xA;Quella vista prima è una versione molto edulcorata di ciò che avviene nella realtà.&#xA;Il pepper, non può essere gestito con leggerezza visto che è un segreto che protegge non un singolo oggetto ma intere classi, come db di utenti.&#xA;&#xA;L&#39;apparato che gestisce chiavi di questo tipo e di questa importanza, deve essere robusto, praticamente inattaccabile, quasi completamente isolato dal resto dei sistemi a meno delle applicazioni, e solo di quelle, che hanno il permesso di richiedere una chiave,&#xA;&#xA;Apparati hardware specializzati che assolvono a tutte queste funzioni e anche di più, sono gli HSM (Hardware Security Module) che garantiscono il ciclo di vita delle chiavi, dalla generazione alla distruzione, includendo versionamento, rotazione e backup.&#xA;Sono concepiti per resistere anche a manipolazioni forzate che possono innescare un meccanismo di autodistruzione e, particolare rilevante, le operazioni crittografiche basate sulle chiavi protette vengono svolte dall&#39;hsm che consegna al client il risultato delle operazioni, non le chiavi. Nel nostro caso, l&#39;HSM dovrebbe restituirci l&#39;hmac del digest della password che gli inviamo.&#xA;Key / Algo Rotation&#xA;Cosa succede se cambio pepper o algoritmo (anche la sua configurazione)?&#xA;Non avendo disponibilità in alcun modo della password dovrò adottare una strategia ad-hoc.&#xA;Fra tutti gli scenari possibili, il miglior compromesso fra sicurezza e comodità secondo me, è quello basato sul wrapping.&#xA;&#xA;È necessario innanzitutto che vengano conservate le versioni delle chiavi per i servizi che le richiedono. E a questo dovrebbe pensarci l&#39;HSM, se ce n&#39;è uno o qualcosa di custom che abbia funzionalità analoghe.&#xA;Inoltre dovrebbero esserci dei flag che indichino quali sono gli utenti a cui sono state applicate le nuove configurazioni.&#xA;&#xA;Caso A: Algo rotation&#xA;Supponiamo che l&#39;algoritmo di hashing venga cambiato o vengano cambiate le sue configurazioni.&#xA;&#xA;Premessa:&#xA;Nel mio DB degli utenti, in corrispondenza di ogni utente, avrò:&#xA;&#xA;il digest della password &#34;pepato&#34;: HMAC ( pepper, HASH ( salt, password ) ) &#xA;il salt&#xA;&#xA;Il wrapping:&#xA;La strategia sarà quello di &#34;avvolgere&#34; la password di ogni utente col nuovo algoritmo, settare un qualche flag che mi indichi l&#39;operazione compiuta e archiviare il tutto.&#xA;&#xA;Imponiamo il nuovo algoritmo a tutti gli utenti &#34;imbustando&#34; il digest attuale (in questo caso &#39;HMAC in realtà, visto che abbiamo a che fare anche col pepper) con il nuovo digest HASH\NEW:&#xA;HASH\NEW ( salt\new, HMAC ( pepper, HASH ( salt, password ) ) ).&#xA;Per ogni utente averemo dunque:&#xA;&#x9;il nuovo digest al posto di quello vecchio,&#xA;&#x9;il nuovo salt &#xA;&#x9;il vecchio salt&#xA;Settiamo il flag del cambio algoritmo a true (o quello che è)&#xA;Quando l&#39;utente effettuerà il login con successo e il flag sarà a &#34;true&#34;, abbiamo la password che ci permetterà di eliminare il vecchio &#34;involucro&#34; e ripristinare l&#39;HMAC del nuovo digest: HMAC ( pepper, HASH\NEW ( salt\new, password ) ) e il flag ritornerà a &#34;false&#34;&#xA;&#xA;Considerazioni:&#xA;&#xA;La sicurezza non viene compromessa perché il digest di un digest, con KDF configurate a dovere, non comporta alcun rischio.&#xA;La fase di verifica è quella che si complica di più perché in base al valore del flag, dovrà essere effettuata in maniera differente.&#xA;   Se il flag è &#34;true&#34; (nella nostra convenzione), dopo il login devo avere gli elementi per calcolare il digest in questo modo: HASH\NEW ( salt\new, HMAC ( pepper, HASH ( salt, password ) ) ).&#xA;   Se il flag è a false, calcolerò al solito: HMAC ( pepper, HASH\NEW ( salt\new, password ) )&#xA;&#xA;Caso B: Pepper rotation&#xA;Supponiamo che a ruotare sia il pepper.&#xA;Procediamo sempre con il wrapping massivo su tutti gli utenti incapsulando il digest :&#xA;&#xA;HMAC ( pepper, HASH ( salt, password ) )&#xA;&#xA;con quello nuovo:&#xA;&#xA;HMAC ( pepper\new, HMAC ( pepper, HASH ( salt, password ) ) )&#xA;&#xA;mettendo il flag a &#34;true&#34;.&#xA;&#xA;Come prima, una volta che gli utenti cominceranno a fare il login, se il flag è &#34;true&#34; innanzitutto verificherò che:&#xA;&#xA;HMAC ( pepper\new, HMAC ( pepper, HASH ( salt, password ) ) ) &#xA;&#xA;sia uguale a ciò che è stato archiviato.&#xA;Se così fosse, ora che sono di nuovo in possesso della password, ripristinerò l&#39;HMAC con:&#xA;&#xA;HMAC ( pepper\new, HASH ( salt, password ) )_&#xA;&#xA;memorizzandolo al posto di quello vecchio e rimettendo il flag a false.&#xA;&#xA;Considerazioni:&#xA;La modifica massiva delle password degli utenti, stavolta passa dall&#39;HSM e potrebbe essere un problema perché un HSM è progettato per scoraggiare flooding di richieste.&#xA;&#xA;È vero che il pepper è sempre lo stesso per tutti gli utenti ma, come ricordavo prima, di solito un HSM non fornisce i suoi segreti ma solo i risultati crittografici delle loro applicazioni.&#xA;&#xA;#kdf #pbkdf2 #bcrypt #scrypt #yescrypt #argon2 #luks #cryptography #aes #sha #digest #RainbowTable #BruteForce #salt #pepper #entropy #hsm #hmac #hash]]&gt;</description>
      <content:encoded><![CDATA[<p><img src="https://pixelfed.uno/storage/m/_v2/489827599091373610/ffe7c43a6-a8b5f2/dnumO91UA8rS/L0t7UvdH6Z7pYPEvBDVb3aulMdNhW1WPBRIRKHMF.jpg" alt="password hashing"></p>

<p>Dopo aver capito come creare password inviolabili anche avendo a disposizione tutta l&#39;energia termica dell&#39;universo, pensiamo al modo migliore per archiviarle.</p>

<p>Pensare di lasciare la password raw in un database, rappresenta un grosso rischio in virtù di un possibile attacco offline.
</p>
<ul><li><a href="#rainbow-table" rel="nofollow">Rainbow Table</a></li>
<li><a href="#come-ti-blocco-la-rainbow-table-il-salt-sale" rel="nofollow">Come ti blocco la Rainbow Table: Il “Salt” (sale)</a></li>
<li><a href="#il-livello-successivo-il-pepper" rel="nofollow">Il livello successivo: Il Pepper</a></li>
<li><a href="#final-step-l-hashing" rel="nofollow">Final step: L’hashing</a>
<ul><li><a href="#pbkdf2" rel="nofollow">PBKDF2</a></li>
<li><a href="#bcrypt" rel="nofollow">Bcrypt</a></li>
<li><a href="#scrypt" rel="nofollow">Scrypt</a></li>
<li><a href="#yescrypt" rel="nofollow">yescrypt</a></li>
<li><a href="#argon2" rel="nofollow">Argon2</a></li></ul></li>
<li><a href="#un-piccolo-esempio-hashing-di-una-password-con-argon2" rel="nofollow">Un piccolo esempio: hashing di una password con Argon2</a></li>
<li><a href="#gestione-del-pepper" rel="nofollow">Gestione del pepper</a></li>
<li><a href="#hsm" rel="nofollow">HSM</a></li>
<li><a href="#key-algo-rotation" rel="nofollow">Key / Algo Rotation</a>
<ul><li><a href="#caso-a-algo-rotation" rel="nofollow">Caso A: Algo rotation</a></li>
<li><a href="#caso-b-pepper-rotation" rel="nofollow">Caso B: Pepper rotation</a></li></ul></li></ul>

<p>Una prima linea di difesa consiste nel memorizzare il <strong>digest della password</strong>, una stringa alfanumerica univoca generata da un apposito algoritmo, così da lasciare nelle mani dell&#39;attaccante degli oggetti che, per la loro non invertibilità, non permettono di risalire alle password.</p>

<p>La <strong>scelta</strong> dell&#39;algoritmo di hashing diventa critica al fine di scongiurare altri tipi di attacchi. Ad es. SHA-256, pur essendo ottimo e molto efficiente per il digest e la firma anche di file di grandi dimensioni, mostra il fianco, proprio in virtù della sua velocità, nel caso di attacchi con:</p>
<ul><li><strong>brute-force:</strong> si calcola l’hash di password casuali fino a trovare una corrispondenza,</li>
<li><strong>dizionario:</strong> un&#39;alternativa intelligente alla forza bruta. Si punta alle password più comuni, si calcola l&#39;hash e si controlla se c&#39;è corrispondenza.</li>
<li><strong>Rainbow Table:</strong> l&#39;attacco al dizionario più insidioso di tutti</li></ul>

<p>Oltre al fatto che, la funzione di hashing , essendo deterministica, permette di capire chi sono gli utenti che hanno la stessa password, dal momento che avranno lo stesso digest.</p>

<h2 id="rainbow-table">Rainbow Table</h2>

<p>Una <strong>Rainbow Table</strong> è un enorme dizionario pre-calcolato che contiene:</p>
<ul><li>Milioni di password comuni.</li>
<li>Il relativo hash corrispondente.</li></ul>

<p>Invece di calcolare l’entropia di ogni tentativo, l’attaccante ruba il database degli hash e fa un semplice “Cerca e Trova”. Se l’hash della tua password è nella tabella, la tua password è violata in millisecondi, indipendentemente da quanto fosse alta la sua entropia teorica.</p>

<p><small><strong>Oss.:</strong> Una password totalmente casuale, generata da un CSPRNG affidabile, con un&#39;alta entropia (&gt;120), rimarrebbe comunque inviolabile anche dalla rainbow table perché la probabilità che quella password si trovi nel dizionario, sarebbe equivalente ad indovinarla.</small></p>

<h2 id="come-ti-blocco-la-rainbow-table-il-salt-sale">Come ti blocco la Rainbow Table: Il “Salt” (sale)</h2>

<p>Per rendere inutili le Rainbow Table, i sistemi sicuri utilizzano il <strong>Salt</strong>. Il salt è una stringa di dati casuali (generati da una sorgente d’entropia affidabile ovviamente) che viene aggiunta alla password prima di calcolarne il digest.</p>

<p>In questo modo le rainbow table vengono vanificate perché gli hash precalcolati sulle password raccolte, mancando il salt, non valgono più. Anche se due utenti avessero la stessa password, avrebbero degli hash completamente diversi.</p>

<p>Anche per questo motivo non è un problema che il salt sia pubblico, perché il suo obiettivo non è nascondere quello che è un pezzetto di password a tutti gli effetti, ma di impedire <strong>economie di scala</strong> degli attacchi perché, pur potendo disporre offline di un database di decine di milioni di utenti, gli hash della mia Rainbow Table (che può arrivare a pesare anche decine di GB) <strong>andrebbero tutti ricalcolati per ogni utente</strong>, con un costo computazionale e di archiviazione inimmaginabile.</p>

<p>Per riassumere, gli ingredienti di base sono:</p>
<ol><li><strong>una buona sorgente d’entropia:</strong> una fonte di casualità certificata per generare un salt unico;</li>
<li><strong>entropia della password:</strong> sempre buona norma, ove possibile, come sappiamo ormai fare (<a href="https://noblogo.org/aytin/come-generare-una-password-o-un-keyfile-sicuri-trilogia-della-password-1-di" rel="nofollow">https://noblogo.org/aytin/come-generare-una-password-o-un-keyfile-sicuri-trilogia-della-password-1-di</a>). Evita attacchi brute-force o al dizionario in cui l’attaccante prova a indovinare;</li>
<li><strong>salt:</strong> protegge la password dagli attacchi basati su database pre-calcolati (Rainbow Table).</li></ol>

<h2 id="il-livello-successivo-il-pepper">Il livello successivo: Il Pepper</h2>

<p>È vero che col salt andiamo a complicare lo sfruttamento di un attacco offline ma possiamo fare di meglio.</p>

<p>Il punto d&#39;attenzione è che il salt protegge le password di <strong>tutti gli utenti</strong>.
Ma un attaccante potrebbe non essere affatto interessato a violare <strong>ogni singolo utente</strong> (niente economia di scala) ma solo alcuni. E allora l&#39;attacco attraverso Ranbow Table potrebbe essere di nuovo praticabile.</p>

<p>Ma gli informatici sono dei gran giocherelloni, si sa.
Visto che abbiamo già il “sale”, perché non finire aggiustando con un po’ di “pepe”? Detto, fatto!</p>

<p>Il <strong>pepper</strong>, come il salt, è un&#39;altra password generata con gli stessi criteri del salt ma le analogie finiscono qua perché:</p>
<ul><li>a differenza del <strong>salt</strong> che si trova nel database, il <strong>pepper</strong> è separato da ques&#39;ultimo. L&#39;ideale sarebbe un <strong>HSM</strong>;</li>
<li>Il <strong>salt</strong> è visibile a tutti, attaccante compreso. Il <strong>pepper</strong> è segreto. Un eventuale data breach che permette all&#39;attaccante di disporre offline di tutto il database degli utenti, “vedrà” certamente gli eventuali salt ma sarà ignaro del fatto che gli mancherà sempre un pezzo di chiave;</li>
<li>il <strong>salt</strong> è diverso per ogni utente, il <strong>pepper</strong>, di solito, è unico;</li>
<li>il <strong>salt</strong> serve a rendere uniche la password degli utenti, il <strong>pepper</strong> protegge l&#39;intero database da attacchi offline.</li></ul>

<p><strong>Il pepper è la chiave di un HMAC, o di un meccanismo di cifratura simmetrica, applicato al digest della password</strong> (che ricordo essere salt+password in realtà), che sarà ciò che verrà archiviato.</p>

<p>Va detto che l&#39;uso del “pepper” complica ulteriormente lo scenario di archiviazione.
Nella stragrande maggioranza dei casi è sufficiente scegliere un buon algoritmo di password hashing (vedi paragrafo successivo) per scoraggiare gli attaccanti.
“Pepare” le password prevederebbe, come detto sopra, l&#39;uso di un HSM per es, e tutta una serie di riflessioni di contorno che evidenzierò più avanti.</p>

<h2 id="final-step-l-hashing">Final step: L&#39;hashing</h2>

<p>L&#39;ultimo punto da dettagliare è l&#39;hash della password.</p>

<p>L&#39;<strong>hash crittografico</strong>, in uso in questi casi, deve soddisfare le seguenti proprietà:</p>
<ol><li><strong>resistenza alla pre-immagine:</strong> dato un hash <em>h</em>, deve essere impossibile trovare una password <em>p</em> t.c. <em>H(p) = h</em> (non invertibilità della funzione hash)</li>
<li><strong>resistenza alla pre-immagine secondaria:</strong> dato una password <em>p<sub>1</sub></em>, deve essere impossibile trovare un&#39;altra password <em>p<sub>2</sub></em> t.c. <em>H(p<sub>1</sub>) = H(p<sub>2</sub>)</em> (resistenza debole alle collisioni)</li>
<li><strong>resistenza alle collisioni:</strong> è impossibile trovare due password diverse, <em>p<sub>1</sub></em> e <em>p<sub>2</sub></em>, t.c. <em>H(p<sub>1</sub>) = H(p<sub>2</sub>)</em> (resistenza forte alle collisioni)</li>
<li><strong>effetto valanga:</strong> il cambio di un solo bit della password deve cambiare radicalmente l&#39;intero hash</li></ol>

<p>In un sistema moderno, l&#39;hash non può essere delegato a funzioni di tipo SHA perché nascono per altri compiti,</p>

<p><strong>SHA-2</strong> e <strong>SHA-3</strong> nascono per il digest veloce, per verificare l&#39;integrità di file anche molto grossi o firmare documenti. La loro eccellente velocità diventa il loro più grosso difetto quando si parla di password.
Negli scenari precedenti di attachi offline, l&#39;hacker che dispone di una grossa potenza di calcolo, può ricostituire velocemente le rainbow table per <em>n</em> utenti. Magari non di tutti ma di quelli attenzionati.</p>

<p>Le funzioni di derivazione della chiave (<strong>KDF</strong>) come <strong>pbkdf2</strong> e quelle ancora più estreme come <strong>B/Scrypt</strong>, <strong>Argon2</strong>, oltre che soddisfare tutti i punti precedentemente elencati tipici di funzioni di password hashing, sono progettate per essere computazionalmente pesantissime da calcolare perché il loro scopo non è il digest ma la protezione di un segreto contro il brute-force.
E mentre le vecchie KDF come <strong>pbdfk2</strong> sono CPU bound, ma non GPU bound, le KDF più moderne come <strong>Bcrypt</strong>, <strong>Scrypt</strong> ma soprattutto <strong>Argon2</strong>, agiscono pesantemente su tempo, memoria e parallelismo e l&#39;attacco offline di cui sopra diventa impraticabile.</p>

<h3 id="pbkdf2">PBKDF2</h3>

<p>È il decano delle KDF. Applica iterativamente una funzione pseudorandomica, come HMAC con uno SHA, con salt alla password.
Il conteggio delle iterazioni è un parametro configurabile.</p>

<p>PBDKF2 è uno standard di lunga data ampiamente adottato. Se non ci sono necessità stringenti di sicurezza o requisiti legacy, è una buona scelta.</p>

<p>Il fatto di essere solo CPU bound però non la rende la scelta ideale in scenari dove gli attaccanti possono attingere a risorse di calcolo considerevoli</p>

<h3 id="bcrypt">Bcrypt</h3>

<p>Basato su Blowfish, anche Bcrypt usa un hash crittografico sulla password con parametri il salt e un fattore di costo.</p>

<p>Il fattore di costo aumenta esponenzialmente il numero di iterazioni per adattarsi all&#39;aumento di potenza di calcolo dell&#39;hardware.</p>

<p>Bcrypt è stato progetto per essere lento e resistente a semplici attacchi di forzat bruta.
Tuttavia, il basso utilizzo di ram richiesto dal calcolo lo rendono poco resistente ad attacchi sferrati usando hardware specializzato.</p>

<p>Bcrypt ha dalla sua una storia solidissima in ragione della quale da 20 anni a questa parte non sono state trovate vulnerabilità critiche nel suo design.</p>

<p>Per questo motivo Bcrypt cifra le password di sistema di OpenBSD dal 1999, come pure ha cifrato quelle di tante distro Linux per anni, prima che passassero ad Argon2 o <strong>yescrypt</strong> (default di Fedora).</p>

<p>Domina nei framework web ([Python] Django, [Ruby] Ruby on Rails, [PHP] Laravel), [Java] Spring, Node.js), nelle applicazioni (Ansible / Terraform, Docker), nel web (la cifratura in .htpasswd di Apache e Nginx) visto che la sua semplcitià di implementazione gli ha permesso di trovarsi praticamente in ogni linguaggio.</p>

<p>È presente come alternativa anche nei password manager benché molti di essi abbiano spostato il default verso Argon2 o PBKDF2 per conformità agli standard FIPS.</p>

<p>È molto semplice implementare e anche da usare perché bisogna agire solo sul <strong>fattore di costo</strong> (consigliato almeno 10-12, altrimenti diventa troppo vulnerabile ad attacchi sferrati attraverso la GPU)</p>

<h3 id="scrypt">Scrypt</h3>

<p>Rilasciato nel 2009, Scrypt è stato il primo algoritmo a introdurre il concetto di <strong>Memory Hardness</strong> ed è stato progettato per rendere economicamente poco conveniente il ricorso ad hardware specializzato come gli ASIC o i FPGA e incidere pesantemente su CPU, ram e parallelismo.</p>

<p>Il suo alveo principale sono state le cripto-valute, molte monete lo usano per il mining.</p>

<p>Scrypt lo troviamo in quasi tutti i linguaggi di programmazione, in <strong>Tarsnap</strong>, servizio di baclup online creato dallo stesso autore di Scrypt, è stato usato da <strong>LastPass</strong> ed è presente come opzione in <strong>VeraCrypt</strong> per derivare la chiave dalla password.
Fino ad Android 9 era l&#39;algoritmo usato per la <strong>FDE</strong> del dispositvio (passato poi al <strong>FBE</strong>) .
Presente anche su FreeBSD come opzione per la cifratura delle password di sistema e come opzione su <strong>LUKS</strong> per la cifratura degli slot delle chiavi.</p>

<p>Su Scrypt i parametri da configurare sono:</p>
<ul><li><strong>Costro CPU/Memoria <em>(N)</em>:</strong> un parametro che aumenta i costi computazionali di cpu e memoria</li>
<li><strong>DImensione del blocco <em>®</em>:</strong> influenza la larghezza di banda della memoria</li>
<li><strong>Parallelizzazione <em>(p)</em>:</strong> indica quanto deve incidere sul calcolo parallelo</li></ul>

<p>In questo modo riesce ad essere sia CPU bound che GPU bound che, a differenza di Bcrypt, lo rende resistente anche ad attacchi facenti uso di hardware specializzato..</p>

<p>Di contro, in ambiente in cui siamo vincolati dalle risorse disponibili, la sua potenza diventa un fattore limitante.
Quasi paragonabile ad Argon2 in quanto a robustezza, il suo unico tallone d&#39;Achille è la permeabilità ad attacchi di tipo <strong>side-channel</strong>.</p>

<h3 id="yescrypt">yescrypt</h3>

<p>Piccola menzione per <strong>yescrypt</strong>, appartenente alla famiglia “Scrypt”, pensato per essere ancora più resistente di Scrypt agli attacchi GPU e FPGA ma con una gestione più intelligente delle risorse.</p>

<p>Grazie alle sue peculiarità, di fatto, è diventato il successore spirituale di Bcrypt nei sistemi operativi gnu/linux dove, a cominciare da Fedora, passando per Debian, Ubuntu, Arch, Kali, è il default per la cifratura delle password di sistema in <code>/etc/shadow</code>.</p>

<p>È talmente incardinato ormai nei sistemi operativi, che è la libreria <code>libxcrypt</code> di <strong>yescrypt</strong> a gestire la tipica funzione <code>crypt()</code> di C che è la base della crittografia su tutti i sistemi gnu/linux moderni.</p>

<p>La sua robustezza unita alla gestione intelligente delle risorse lo rende un coltellino svizzero di riferimento utile per es. per versione custom di LUKS su sistemi embedded, che magari fanno uso di cpu meno recenti,  oppure come opzione per strumenti di backup specialistici</p>

<p>Di fatto, sui sistemi operativi, <strong>yescrypt</strong> s&#39;è guadagnato un consenso amplissimo dovuto alla sua scalabilità, alla sua capacità di usare anche la ROM per rendere il cracking ancora più difficile e senza pesare sulla RAM e alla sua compatibilità potendosi inserire perfettamente nella storica funzione <code>crypt()</code> di C come detto prima.</p>

<p>Se Argon2 è il vincitore accademico avendo vinto il Password Hashing Competition del 2015, <strong>yescrypt</strong> per la sua robustezza, efficienza e flessibilità si ritaglia un profilo di indispensabilità nei sistemi operativi,</p>

<h3 id="argon2">Argon2</h3>

<p>E veniamo al dominatore indiscusso di questa che non è una llista esaustiva di KDF.</p>

<p>Argon2 è LO standard moderno per il password hashing raccomandato da OWASP e IETF.</p>

<p>È il riferimento per praticamente ogni password manager: <strong>Bitwarden</strong>, <strong>KeppasXC</strong>, <strong>1Password</strong>, a cui assegnanp la protezione della <strong>Master Password</strong></p>

<p>È la scelta principale per la cifratura degli hard disk anche con impostazioni molto aggressive, in ragione delle quali un ritardo di mezzo secondo (un tempo enorme se venisse scalato esponenzialmente) nell&#39;apertura di un HD è assolutamente accettabile.
È il default di <strong>LUKS2</strong> (LUKS1 usava PBKDF2) e di <strong>VeraCrypt</strong>, con cui ha sostituito SHA-512.</p>

<p>Come Bcrypt, è implementato estensivamente su praticamente ogni frameword web e backend, da PHP, Django (Python), Laravel fino a Node.js.</p>

<p>Nei sistemi operativi, laddove <strong>yescrytpt</strong> domina nella gestione delle password utente, <strong>Argon2</strong> è usato per compiti più critici.
Dal kernel Linux per gestire internamente le chiavi crittografiche o da macOS / iOS, dove algoritmi proprietari ispirati fortemente ad Argon2, proteggono i dati nel Secure Enclave.</p>

<p>Argon2  setta 3 parametri principali per regolare la sua forza:</p>
<ul><li><strong>t: iterazioni</strong>, quante volte vengono rimescolati i dati <em>(default Bitwarden = 3)</em></li>
<li><strong>m: memoria</strong>, quanta ram deve occupare il calcolo. Questa è la misura anti-GPU <em>(default Bitwarden = 16 (64MB))</em></li>
<li><strong>p: parallelismo</strong>, quanti core della cpu usare. Questa è la misura anti-CPU <em>(default Bitwarden = 4)</em></li></ul>

<p>La variante <em>id</em> è anche resistente agli <strong>attacchi side-channel</strong> perché impediscono a un attaccante di capire la password osservando i tempi di accesso alla memoria.</p>

<h2 id="un-piccolo-esempio-hashing-di-una-password-con-argon2">Un piccolo esempio: hashing di una password con Argon2</h2>

<p>Il grosso vantaggio degli algoritmi di kdf è che sono naturalmente resilienti rispetto all&#39;evoluzione tecnologica che produce macchine con sempre maggiore potenza di calcolo.
Da pbkdf2 in poi, il salt implicito che invalida le rainbow table precalcolate e la possibilità di calibrare il key stretching in moda da agire intensivamente su ram e cpu, permettono all&#39;algoritmo di adeguarsi per conservare la sua robustezza.</p>

<p>Mini-script per l&#39;hashing di una password fornita dall&#39;utente con argon2 settato al default di Bitwarden:</p>

<pre><code class="language-bash">echo -n &#34;Password: &#34;; read -s PASSWORD
# Genero un Salt casuale di 128 bit
SALT=$(openssl rand -base64 128)
PASSWORD_HASH=$(echo &#34;${PASSWORD}&#34; | argon2 &#34;${SALT}&#34; -m 16 -t 3 -p 4 -id -e)
</code></pre>

<p><code>PASSWORD_HASH</code> e <code>SALT</code> sono i dati che verranno archiviati e, poiché argon2 “frulla” la password con un salt, è praticamente impossibile risalire alla password originale.</p>

<p>La verifica è tuttavia banale perché, avendo il salt e la password da verificare, si ricrea l&#39;hash con argon2 e si confronta con l&#39;hash memorizzato.</p>

<p>Per maggior sicurezza salt e digest possono essere memorizzati in punti differenti. L&#39;importante è che possano essere recuperate a partire dall&#39;utente.</p>

<h2 id="gestione-del-pepper">Gestione del pepper</h2>

<p>Col pepper le cose cambiano un pochino perché:</p>
<ul><li>deve essere archiviato con tutte le paranoie possibili in un punto diverso dal database degli utenti</li>
<li>il key rotation del pepper non è banale</li></ul>

<p>Mini-script che mostra come applicare salt e pepper all&#39;hashing di una password:</p>

<pre><code class="language-bash"># L&#39;utente inserisce la password
echo -n &#34;Password: &#34;; read -s PASSWORD

# Genero un Salt casuale di 128 bit unico per ogni utente
SALT=$(openssl rand -base64 128)

# Anche PEPPER sarà qualcosa del tipo &#34;openssl rand -base64 128&#34;
# e si troverà in un punto esterno al database degli utenti.
PEPPER=$(get_pepper_from_ext)

# Digest della password+salt
PASSWORD_HASH=$(echo &#34;${PASSWORD}&#34; | argon2 &#34;${SALT}&#34; -m 16 -t 3 -p 4 -id -e)

# HMAC del digest con PEPPER come chiave
PASSWORD_PEPPER=$(echo &#34;${PASSWORD_HASH}&#34; | openssl dgst -sha256 -hmac &#34;${PEPPER}&#34; -binary | base64)
</code></pre>

<h2 id="hsm">HSM</h2>

<p>Quella vista prima è una versione molto edulcorata di ciò che avviene nella realtà.
Il pepper, non può essere gestito con leggerezza visto che è un segreto che protegge non un singolo oggetto ma intere classi, come db di utenti.</p>

<p>L&#39;apparato che gestisce chiavi di questo tipo e di questa importanza, deve essere robusto, praticamente inattaccabile, quasi completamente isolato dal resto dei sistemi a meno delle applicazioni, e solo di quelle, che hanno il permesso di richiedere una chiave,</p>

<p>Apparati hardware specializzati che assolvono a tutte queste funzioni e anche di più, sono gli <strong>HSM</strong> (<strong>H</strong>ardware <strong>S</strong>ecurity <strong>M</strong>odule) che garantiscono il ciclo di vita delle chiavi, dalla generazione alla distruzione, includendo versionamento, rotazione e backup.
Sono concepiti per resistere anche a manipolazioni forzate che possono innescare un meccanismo di autodistruzione e, particolare rilevante, <strong>le operazioni crittografiche basate sulle chiavi protette vengono svolte dall&#39;hsm che consegna al client il risultato delle operazioni, non le chiavi</strong>. Nel nostro caso, l&#39;HSM dovrebbe restituirci l&#39;hmac del digest della password che gli inviamo.</p>

<h2 id="key-algo-rotation">Key / Algo Rotation</h2>

<p>Cosa succede se cambio pepper o algoritmo (anche la sua configurazione)?
Non avendo disponibilità in alcun modo della password dovrò adottare una strategia ad-hoc.
Fra tutti gli scenari possibili, il miglior compromesso fra sicurezza e comodità secondo me, è quello basato sul wrapping.</p>

<p>È necessario innanzitutto che vengano conservate le versioni delle chiavi per i servizi che le richiedono. E a questo dovrebbe pensarci l&#39;HSM, se ce n&#39;è uno o qualcosa di custom che abbia funzionalità analoghe.
Inoltre dovrebbero esserci dei flag che indichino quali sono gli utenti a cui sono state applicate le nuove configurazioni.</p>

<h3 id="caso-a-algo-rotation">Caso A: Algo rotation</h3>

<p>Supponiamo che <strong>l&#39;algoritmo di hashing venga cambiato</strong> o vengano cambiate le sue configurazioni.</p>

<p><strong>Premessa:</strong>
Nel mio DB degli utenti, in corrispondenza di ogni utente, avrò:</p>
<ol><li>il digest della password “pepato”: <em>HMAC ( pepper, HASH ( salt, password ) )</em></li>
<li>il salt</li></ol>

<p><strong>Il wrapping:</strong>
La strategia sarà quello di “avvolgere” la password di ogni utente col nuovo algoritmo, settare un qualche flag che mi indichi l&#39;operazione compiuta e archiviare il tutto.</p>
<ol><li>Imponiamo il nuovo algoritmo a tutti gli utenti “imbustando” il digest attuale (in questo caso &#39;HMAC in realtà, visto che abbiamo a che fare anche col pepper) con il nuovo digest <em><strong>HASH_NEW</strong></em>:
<em><strong>HASH_NEW</strong> ( <strong>salt_new</strong>, HMAC ( pepper, HASH ( salt, password ) ) )</em>.</li>
<li>Per ogni utente averemo dunque:
<ol><li>il nuovo digest al posto di quello vecchio,</li>
<li>il nuovo salt</li>
<li>il vecchio salt</li></ol></li>
<li>Settiamo il flag del cambio algoritmo a <em>true</em> (o quello che è)</li>
<li>Quando l&#39;utente effettuerà il login con successo e il flag sarà a “<em>true</em>”, abbiamo la password che ci permetterà di eliminare il vecchio “involucro” e ripristinare l&#39;HMAC del nuovo digest: <em>HMAC ( pepper, <strong>HASH_NEW</strong> ( <strong>salt_new</strong>, password ) )</em> e il flag ritornerà a “<em>false</em>“</li></ol>

<p><strong>Considerazioni:</strong></p>
<ul><li>La sicurezza non viene compromessa perché il digest di un digest, con KDF configurate a dovere, non comporta alcun rischio.</li>
<li>La fase di verifica è quella che si complica di più perché in base al valore del flag, dovrà essere effettuata in maniera differente.
<ul><li>Se il flag è “<em>true</em>” (nella nostra convenzione), dopo il login devo avere gli elementi per calcolare il digest in questo modo: <em><strong>HASH_NEW</strong> ( <strong>salt_new</strong>, HMAC ( pepper, HASH ( salt, password ) ) )</em>.</li>
<li>Se il flag è a <em>false</em>, calcolerò al solito: <em>HMAC ( pepper, <strong>HASH_NEW</strong> ( <strong>salt_new</strong>, password ) )</em></li></ul></li></ul>

<h3 id="caso-b-pepper-rotation">Caso B: Pepper rotation</h3>

<p>Supponiamo che a ruotare sia il pepper.
Procediamo sempre con il wrapping massivo su tutti gli utenti incapsulando il digest :</p>

<p><em>HMAC ( pepper, HASH ( salt, password ) )</em></p>

<p>con quello nuovo:</p>

<p><em>HMAC ( <strong>pepper_new</strong>, HMAC ( pepper, HASH ( salt, password ) ) )</em></p>

<p>mettendo il flag a “<em>true</em>”.</p>

<p>Come prima, una volta che gli utenti cominceranno a fare il login, se il flag è “<em>true</em>” innanzitutto verificherò che:</p>

<p><em>HMAC ( <strong>pepper_new</strong>, HMAC ( pepper, HASH ( salt, password ) ) )</em></p>

<p>sia uguale a ciò che è stato archiviato.
Se così fosse, ora che sono di nuovo in possesso della password, ripristinerò l&#39;HMAC con:</p>

<p><em>HMAC ( <strong>pepper_new</strong>, HASH ( salt, password ) )</em></p>

<p>memorizzandolo al posto di quello vecchio e rimettendo il flag a false.</p>

<p><strong>Considerazioni:</strong>
La modifica massiva delle password degli utenti, stavolta passa dall&#39;HSM e potrebbe essere un problema perché un HSM è progettato per scoraggiare flooding di richieste.</p>

<p>È vero che il pepper è sempre lo stesso per tutti gli utenti ma, come ricordavo prima, di solito un HSM non fornisce i suoi segreti ma solo <strong>i risultati crittografici delle loro applicazioni.</strong></p>

<p><a href="/aytin/tag:kdf" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">kdf</span></a> <a href="/aytin/tag:pbkdf2" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">pbkdf2</span></a> <a href="/aytin/tag:bcrypt" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">bcrypt</span></a> <a href="/aytin/tag:scrypt" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">scrypt</span></a> <a href="/aytin/tag:yescrypt" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">yescrypt</span></a> <a href="/aytin/tag:argon2" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">argon2</span></a> <a href="/aytin/tag:luks" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">luks</span></a> <a href="/aytin/tag:cryptography" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">cryptography</span></a> <a href="/aytin/tag:aes" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">aes</span></a> <a href="/aytin/tag:sha" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">sha</span></a> <a href="/aytin/tag:digest" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">digest</span></a> <a href="/aytin/tag:RainbowTable" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">RainbowTable</span></a> <a href="/aytin/tag:BruteForce" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">BruteForce</span></a> <a href="/aytin/tag:salt" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">salt</span></a> <a href="/aytin/tag:pepper" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">pepper</span></a> <a href="/aytin/tag:entropy" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">entropy</span></a> <a href="/aytin/tag:hsm" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">hsm</span></a> <a href="/aytin/tag:hmac" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">hmac</span></a> <a href="/aytin/tag:hash" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">hash</span></a></p>
]]></content:encoded>
      <guid>https://noblogo.org/aytin/archiviare-le-password-in-sicurezza-con-kdf-password-hashing-trilogia-della</guid>
      <pubDate>Thu, 29 Jan 2026 17:49:50 +0000</pubDate>
    </item>
    <item>
      <title>Gestione TOTP in bash</title>
      <link>https://noblogo.org/aytin/gestione-totp-in-bash</link>
      <description>&lt;![CDATA[totp&#xA;&#xA;In una giornata in cui avevo un po’ di tempo da investire, ho ripreso la versione 2 dello script &#34;Generazione OTP via bash&#34;, e ho deciso di renderlo un po’ più flessibile introducendo una logica crud. Anzi, il termine corretto dovrebbe essere &#34;dave&#34; (Delete, Add, View, Edit).&#xA;!--more--&#xA;Che altro aggiungere?&#xA;&#xA;Il file è ancora un insieme di coppie chiave-valore (nome account e chiave privata).&#xA;Il separatore è il simbolo &#34;:&#34;. Ma può essere configurato cambiando il valore della variabile &#34;DELIMITER&#34;.&#xA;Lo script è discretamente commentato così, in futuro, riuscirò a ricordare perché ho fatto quello che ho fatto (tipo &#34;Memento&#34;).&#xA;Le opzioni di gpg per la cifratura/decifratura del file sono configurabili nello script modificando il valore delle variabili &#34;GPG\ENC\OPTIONS&#34; e &#34;GPG\DEC\OPTIONS&#34;.&#xA;L’uso di getopts, mi rendo conto, è solo un esercizio di stile. Non è molto utile per come l’ho usato, visto che ogni optarg corrisponde ad un’operazione autoconclusiva.&#xA;Ero partito per rimpiazzare ogni costrutto if con combinazioni di lista and / lista or ma le condizioni risultavano talmente complesse da rendere il codice francamente molto più incomprensibile di adesso. In alternativa avrei potuto delegare l’esecuzione delle condizioni ad un insieme di funzioni con una stratificazione tale da ricadere nel punto precedente: offuscamento permanente del codice che va a vanificare l’intenzione iniziale di semplificare il codice e renderlo più leggibile.&#xA;&#xA;!/usr/local/bin/bash&#xA;&#xA;init() {&#xA;    # CODICI D&#39;ERRORE&#xA;    DECRYPTERR=64&#xA;    INVALIDOPTIONERR=65&#xA;    INVALIDACCOUNTERR=66&#xA;    ACCOUNTNOTFOUNDERR=67&#xA;    ACCOUNTNAMEEMPTYERR=68&#xA;    ACCOUNTEXISTSERR=69&#xA;    INVALIDKEYERR=70&#xA;&#xA;    # Colorscheme&#xA;    LIGHTGREEN=&#39;\033[1;32m&#39;&#xA;    LIGHTRED=&#39;\033[1;31m&#39;&#xA;    NC=&#39;\033[0m&#39;&#xA;&#xA;    # Path del file cifrato contentente i codici 2FA&#xA;    KEYS2FAFILE=&#34;path/fileenc.gpg&#34;&#xA;&#xA;    # Separatore delle coppie chiave-valore&#xA;    DELIMITER=&#34;:&#34;&#xA;&#xA;    # path gpg e opzioni di cifratura/decifratura&#xA;    GPGTTY=$(tty)&#xA;    export GPGTTY&#xA;&#xA;    #GPGCOMMAND=&#34;/usr/local/bin/gpg&#34;&#xA;    GPGENCOPTIONS=&#34;--yes -c --s2k-cipher-algo aes256 --s2k-digest-algo sha512 --s2k-mode 3 --s2k-count 260000 - &#34;&#xA;    GPGDECOPTIONS=&#39;-d&#39;&#xA;&#xA;    # Caratteri consentiti nel nome account&#xA;    PATTERN=&#39;^[a-zA-Z0-9.-]+$&#39;;&#xA;&#xA;    # Su MacOS è disponibile &#34;pbcopy&#34;, su nix &#34;xsel&#34;&#xA;    case &#34;$(sysctl hw.targettype 2  /dev/null)&#34; in&#xA;        &#34;&#34;)&#xA;            CLIPBOARD=&#34;xsel -bi&#34;&#xA;            GPGCOMMAND=&#34;/usr/bin/gpg&#34;;;&#xA;         )&#xA;            CLIPBOARD=&#34;pbcopy&#34;&#xA;            GPGCOMMAND=&#34;/usr/local/bin/gpg&#34;;;&#xA;    esac&#xA;&#xA;    # Decifra e carica in ram il contenuto del file cifrato.&#xA;    echo &#34;Decifratura del file degli account in corso...&#34;&#xA;    KEYS2FAFILEDEC=$(${GPGCOMMAND} ${GPGDECOPTIONS} &#34;${KEYS2FAFILE}&#34; 2  /dev/null)&#xA;&#xA;    # Se la password non è corretta, restituisce un errore.&#xA;    [[ -z &#34;${KEYS2FAFILEDEC}&#34; ]] &amp;&amp; { errmsg &#34;${DECRYPTERR}&#34; 1; }&#xA;    infomsg &#34;Fatto.&#34;&#xA;}&#xA;&#xA;getaccount() {&#xA;    ACCOUNTSTDIO=&#34;$1&#34;&#xA;&#xA;    # Check nome account vuoto&#xA;    [[ -z &#34;${ACCOUNTSTDIO}&#34; ]] &amp;&amp; return &#34;${ACCOUNTNAMEEMPTYERR}&#34;&#xA;&#xA;    # Check caratteri invalidi nel nome account&#xA;    [[ ! &#34;${ACCOUNTSTDIO}&#34; =~ $PATTERN ]] &amp;&amp; return &#34;${INVALIDACCOUNTERR}&#34;&#xA;&#xA;    # Estrae l&#39;elemento contenente l&#39;id dato in input&#xA;    OTPLINE=$(echo &#34;${KEYS2FAFILEDEC}&#34; | grep &#34;${ACCOUNTSTDIO}:&#34;)&#xA;&#xA;    # Se l&#39;account non esiste, restituisce un errore&#xA;    [[ -z &#34;${OTPLINE}&#34; ]] &amp;&amp; return &#34;${ACCOUNTNOTFOUNDERR}&#34;&#xA;&#xA;    ACCOUNT=$(echo $OTPLINE | cut -d&#34;${DELIMITER}&#34; -f 1)&#xA;    KEY=$(echo $OTPLINE | cut -d&#34;${DELIMITER}&#34; -f 2)&#xA;}&#xA;&#xA;cryptaccountfile() {&#xA;    ACCOUNTFILE=&#34;$1&#34;&#xA;    echo -e &#34;\nCifratura del file degli account in corso...&#34;&#xA;    ${GPGCOMMAND} -o &#34;$KEYS2FAFILE&#34; ${GPGENCOPTIONS} &lt;&lt;&lt; $(echo -e &#34;${ACCOUNTFILE}&#34;)&#xA;}&#xA;&#xA;noparam() {&#xA;    clear&#xA;    help&#xA;    exit 0&#xA;}&#xA;&#xA;Visualizza messaggi di errore su stdio&#xA;errmsg() {&#xA;    if [[ $# -ne 2 ]]; then&#xA;        echo -r&#34;${LIGHTRED}[DEBUG] [errmsg] Numero di parametri non corretto.${NC}.&#34;; exit 1&#xA;    else&#xA;        case $1 in&#xA;            &#34;${DECRYPTERR}&#34;            ) echo -e &#34;${LIGHTRED}[ERRORE] Impossibile decriptare il file.${NC}&#34;;;&#xA;            &#34;${INVALIDOPTIONERR}&#34;     ) echo -e &#34;${LIGHTRED}[ERRORE] Opzione \&#34;$OPTARG\&#34; non valida.${NC}&#34;;;&#xA;            &#34;${INVALIDACCOUNTERR}&#34;    ) echo -e &#34;${LIGHTRED}ERRORE] Rilevati caratteri non consentiti.\nI caratteri ammessi sono: [a-z[0-9].-${NC}&#34;;;&#xA;            &#34;${ACCOUNTNOTFOUNDERR}&#34;  ) echo -e &#34;${LIGHTRED}[ERRORE] Account non trovato${NC}&#34;;;&#xA;            &#34;${ACCOUNTNAMEEMPTYERR}&#34; ) echo -e &#34;${LIGHTRED}[ERRORE] Nome account vuoto.${NC}&#34;;;&#xA;            &#34;${ACCOUNTEXISTSERR}&#34;     ) echo -e &#34;${LIGHTRED}[ERRORE] Nome account già esistente${NC}&#34;;;&#xA;            &#34;${INVALIDKEYERR}&#34;        ) echo -e &#34;${LIGHTRED}[ERRORE] Chiave privata nulla o non corretta.\nLa chiave privata deve essere base32.\n${NC}&#34;;;&#xA;            ) echo -e &#34;${LIGHTRED}[ERRORE] Errore generico.${NC}&#34;;;&#xA;        esac&#xA;&#xA;        [[ $2 -eq 1 ]] &amp;&amp; exit $2;&#xA;    fi&#xA;}&#xA;&#xA;Visualizza messaggi info su stdio&#xA;infomsg() {&#xA;    echo -e &#34;${LIGHTGREEN}$1${NC}&#34;&#xA;}&#xA;&#xA;continuemsg() {&#xA;    case $1 in&#xA;        &#34;insert&#34;    ) ACTION=&#34;creato&#34;;;&#xA;        &#34;edit&#34;      ) ACTION=&#34;modificato&#34;;;&#xA;        &#34;delete&#34;    ) ACTION=&#34;cancellato&#34;;;&#xA;    esac&#xA;&#xA;    echo -n &#34;L&#39;account \&#34;${ACCOUNT}\&#34; sta per essere ${ACTION}. Vuoi continuare? [y|N]: &#34;; read ANSWER;&#xA;}&#xA;&#xA;continueyn() {&#xA;        ANSWER=&#34;0&#34;&#xA;        while [[ &#34;${ANSWER}&#34; != &#34;y&#34; ]]; do&#xA;            [[ -z &#34;${ANSWER}&#34; || &#34;${ANSWER}&#34; == &#34;N&#34; ]] &amp;&amp; { echo &#34;Operazione annullata&#34;; exit; }&#xA;            [[ &#34;${ANSWER}&#34; != &#34;y&#34; || &#34;${ANSWER}&#34; != &#34;N&#34; ]] &amp;&amp; continuemsg &#34;$1&#34;&#xA;        done&#xA;}&#xA;&#xA;totp() {&#xA;    # Se la&#39;ccount non esiste o è invalido, esco con errore&#xA;    getaccount &#34;$1&#34;; RETCODE=$?&#xA;    case &#34;${RETCODE}&#34; in&#xA;        &#34;${ACCOUNTNOTFOUNDERR}&#34;  ) errmsg &#34;${ACCOUNTNOTFOUNDERR}&#34; 1;;&#xA;        &#34;${INVALIDACCOUNTERR}&#34;    ) errmsg &#34;${INVALIDACCOUNTERR}&#34; 1;;&#xA;    esac&#xA;&#xA;    echo -e &#34;\nCopia OTP nella clipboard...&#34;&#xA;    # Calcola l&#39;otp e lo trasferisce nella clipboard con xsel&#xA;    oathtool --totp -b &#34;${KEY}&#34; | $CLIPBOARD&#xA;    infomsg &#34;Fatto.&#34;&#xA;    exit&#xA;}&#xA;&#xA;insert() {&#xA;    # Se l&#39;account non esiste o è invalido, esco con errore&#xA;    getaccount &#34;$1&#34;; RETCODE=$?&#xA;    case &#34;${RETCODE}&#34; in&#xA;        0                           ) errmsg &#34;${ACCOUNTEXISTSERR}&#34; 1;;&#xA;        &#34;${INVALIDACCOUNTERR}&#34;    ) errmsg &#34;${INVALIDACCOUNTERR}&#34; 1;;&#xA;    esac&#xA;&#xA;    ACCOUNT=&#34;$1&#34;&#xA;&#xA;    # Inserisci la chiave&#xA;    echo -en &#34;Nuovo account: ${ACCOUNT}\nInserisci la nuova chiave privata: &#34;; read KEY&#xA;    while [[ -z &#34;${KEY}&#34; || ! $(echo ${KEY} | oathtool -b - 2  /dev/null) ]]; do&#xA;        errmsg &#34;${INVALIDKEYERR}&#34; 0&#xA;        echo -n &#34;Inserisci la chiave privata: &#34;; read KEY&#xA;    done&#xA;&#xA;    infomsg &#34;\nNuovo account:        ${ACCOUNT}\nNuova chiave privata: ${KEY}&#34;&#xA;    continueyn &#34;insert&#34;&#xA;&#xA;    # Appendo il nuovo record nel file degli account&#xA;    KEYS2FAFILEDEC+=&#34;\n${ACCOUNT}${DELIMITER}${KEY}&#34;&#xA;    infomsg &#34;L&#39;account \&#34;${ACCOUNT}\&#34; è stato creato con successo.&#34;&#xA;&#xA;    # Cifratura del file degli account&#xA;    cryptaccountfile &#34;${KEYS2FAFILEDEC}&#34;&#xA;    infomsg &#34;Fatto.&#34;&#xA;    exit&#xA;}&#xA;&#xA;edit() {&#xA;    # Se la&#39;ccount non esiste o è invalido, esco con errore&#xA;    getaccount &#34;$1&#34;; RETCODE=$?&#xA;    case &#34;${RETCODE}&#34; in&#xA;        &#34;${ACCOUNTNOTFOUNDERR}&#34;  ) errmsg &#34;${ACCOUNTNOTFOUNDERR}&#34; 1;;&#xA;        &#34;${INVALIDACCOUNTERR}&#34;    ) errmsg &#34;${INVALIDACCOUNTERR}&#34; 1;;&#xA;    esac&#xA;&#xA;    # Faccio una copia dell&#39;account per un eventuale ripristino durante&#xA;    # la fase di input&#xA;    ACCOUNTTEMP=&#34;${ACCOUNT}&#34;&#xA;    ACCOUNTMOD=&#34;${ACCOUNT}&#34;&#xA;&#xA;    # Inserisco il nuovo nome account.&#xA;    # 1. Se è vuoto: è la conferma dell&#39;account inserito all&#39;inizio e proseguo&#xA;    # 2. Se il nome è invalido: sollevo un&#39;eccezione e reitero&#xA;    # 3. Se il nome è valido:&#xA;    #    a. Se è un nome nuovo: acquisisco il nuovo nome, recupero nome e chiave dell&#39;account di partenza e proseguo&#xA;    #    b. Sè è lo stesso nome di partenza: è la conferma dell&#39;account iniziale e proseguo&#xA;    #    c. Se è un altro nome esistente o invalido: come il punto 2.&#xA;    while true; do&#xA;        echo&#xA;        echo -en &#34;Account: ${ACCOUNT}\nInserisci nuovo nome account: &#34;; read ACCOUNTMOD;&#xA;        getaccount &#34;${ACCOUNTMOD}&#34;; RETCODE=$?&#xA;        case &#34;${RETCODE}&#34; in&#xA;            # Punto 1.&#xA;            &#34;${ACCOUNTNAMEEMPTYERR}&#34; ) ACCOUNTMOD=&#34;${ACCOUNT}&#34;; break;;&#xA;            # Punto 2&#xA;            &#34;${INVALIDACCOUNTERR}&#34;    ) errmsg &#34;${INVALIDACCOUNTERR}&#34; 0;;&#xA;            # Punto 3a.&#xA;            &#34;${ACCOUNTNOTFOUNDERR}&#34;  ) getaccount &#34;${ACCOUNTTEMP}&#34;; break;;&#xA;            # Punti 3b e 3c, rispettivamente&#xA;            0                           ) [[ &#34;${ACCOUNTMOD}&#34; == &#34;${ACCOUNTTEMP}&#34; ]] &amp;&amp; break \&#xA;                                                                                 || { getaccount &#34;${ACCOUNTTEMP}&#34;; errmsg &#34;${ACCOUNTEXISTSERR}&#34; 0; };;&#xA;        esac&#xA;    done&#xA;&#xA;    [[ &#34;${ACCOUNT}&#34; != &#34;${ACCOUNTMOD}&#34; &amp;&amp; ! -z &#34;${ACCOUNTMOD}&#34; ]] &amp;&amp; msg=&#34;\nL&#39;account \&#34;${ACCOUNT}\&#34; è stato modificato in \&#34;${ACCOUNTMOD}\&#34;.&#34; \&#xA;                                                                    || msg=&#34;\nNessuna modifica rilevata.\n&#34;&#xA;    infomsg &#34;${msg}&#34;&#xA;&#xA;    # Modifica la chiave privata&#xA;    KEYMOD=&#34;${KEY}&#34;&#xA;    echo&#xA;&#xA;    while true; do&#xA;        echo -en &#34;Chiave privata: ${KEY}\nInserisci la nuova chiave privata: &#34;; read KEYMOD&#xA;        if [[ -z $KEYMOD ]]; then&#xA;            KEYMOD=&#34;${KEY}&#34;; break&#xA;        elif [[ ! $(echo &#34;${KEYMOD}&#34; | oathtool -b - 2  /dev/null) ]]; then&#xA;            errmsg &#34;${INVALIDKEYERR}&#34; 0; KEYMOD=&#34;${KEY}&#34;&#xA;        else&#xA;            break&#xA;        fi&#xA;    done&#xA;&#xA;    # Se c&#39;è stata almeno una modifica (account/key), sovrascrivo il record. Altrimenti esco senza fare nulla.&#xA;    if [[ &#34;${ACCOUNT}&#34; != &#34;${ACCOUNTMOD}&#34; || &#34;${KEY}&#34; != &#34;${KEYMOD}&#34; ]]; then&#xA;        # Conferma modifica&#xA;&#xA;        infomsg &#34;\nAccount:              ${ACCOUNT}\nChiave privata:       ${KEY}\nNuovo account:        ${ACCOUNTMOD}\nNuova chiave privata: ${KEYMOD}\n&#34;&#xA;        continueyn &#34;edit&#34;&#xA;&#xA;        # Modifica del file degli account&#xA;        KEYS2FAFILEDECTEMP=$(sed &#34;s/${ACCOUNT}${DELIMITER}${KEY}/${ACCOUNTMOD}${DELIMITER}${KEYMOD}/&#34; &lt;&lt;&lt; &#34;${KEYS2FAFILEDEC}&#34;)&#xA;        infomsg &#34;L&#39;account \&#34;${ACCOUNT}\&#34; è stato modificato con successo.&#34;&#xA;&#xA;        # Cifratura del file degli account&#xA;        cryptaccountfile &#34;${KEYS2FAFILEDECTEMP}&#34;&#xA;    else&#xA;        infomsg &#34;Nessuna modifica rilevata.&#34;&#xA;    fi&#xA;    infomsg &#34;Fatto.&#34;&#xA;}&#xA;&#xA;delete() {&#xA;    if getaccount &#34;$1&#34;; then&#xA;        # Conferma eliminazione&#xA;        infomsg &#34;\nAccount:              ${ACCOUNT}\nChiave privata:       ${KEY}\n&#34;&#xA;        continueyn &#34;delete&#34;&#xA;&#xA;        # Cancellazione account&#xA;        KEYS2FAFILEDECTEMP=$(sed &#34;/$ACCOUNT/d&#34; &lt;&lt;&lt; &#34;${KEYS2FAFILEDEC}&#34;)&#xA;        infomsg &#34;L&#39;account \&#34;$ACCOUNT\&#34; è stato eliminato.&#34;&#xA;&#xA;        # Cifratura del nuovo file degli account&#xA;        cryptaccountfile &#34;${KEYS2FAFILEDECTEMP}&#34;&#xA;        infomsg &#34;Fatto.&#34;&#xA;    else&#xA;        errmsg &#34;${ACCOUNTNOTFOUNDERR}&#34; 1&#xA;    fi&#xA;}&#xA;&#xA;list() {&#xA;    echo&#xA;    while read LINE; do&#xA;        cut -d&#34;${DELIMITER}&#34; -f 1 &lt;&lt;&lt; &#34;${LINE}&#34;&#xA;    done &lt;&lt;&lt; &#34;${KEYS2FAFILEDEC}&#34;&#xA;}&#xA;&#xA;help() {&#xA;cat&lt;&lt;EOF&#xA;Usa come: ./totp.sh [options] ARG&#xA;dove:&#xA;    [options]&#xA;        -l restituisce la lista degli account.&#xA;        -t nomeaccount restituisce l&#39;otp per quell&#39;account.&#xA;        -i nomeaccount inserisce un nuovo account.&#xA;        -d nomeaccount cancella nomeaccount.&#xA;        -e nomeaccount modifica nomeaccount.&#xA;        -h stampa questa pagina di help.&#xA;&#xA;ESEMPI:&#xA;    RESTITUISCE LA LISTA DEGLI ACCOUNT&#xA;    totp.sh -l&#xA;&#xA;    INSERISCE UN ACCOUNT&#xA;    totp.sh -i dropbox&#xA;&#xA;    CANCELLA UN ACCOUNT&#xA;    totp.sh -d megaupload&#xA;&#xA;    MODIFICA UN ACCOUNT&#xA;    totp.sh -e paypal&#xA;&#xA;    GENERA OTP&#xA;    totp.sh -t firefoxsync&#xA;EOF&#xA;}&#xA;&#xA;main() {&#xA;    init&#xA;    while getopts &#34;lt:i:e:d:&#34; opt; do&#xA;        case $opt in&#xA;            l ) list ;;&#xA;            t ) totp $OPTARG ;;&#xA;            i ) insert $OPTARG ;;&#xA;            e ) edit $OPTARG ;;&#xA;            d ) delete $OPTARG ;;&#xA;            ) clear;errmsg &#34;${INVALIDOPTIONERR}&#34; 1;;&#xA;        esac&#xA;    done&#xA;    shift $(($OPTIND - 1))&#xA;}&#xA;&#xA;[[ $# -eq 0 || $# -eq 1 &amp;&amp; &#34;$1&#34; == &#34;-h&#34; ]] &amp;&amp; noparam || main $*&#xA;&#xA;#bash #totp #cryptography #gpg #scripting]]&gt;</description>
      <content:encoded><![CDATA[<p><img src="https://pixelfed.uno/storage/m/_v2/489827599091373610/7c649620b-69b6c5/x5Ykru5oywKu/RE9KMTKwgIzhwDrkGnZqsTThRVvDSlNlyB2XwSv4.webp" alt="totp"></p>

<p>In una giornata in cui avevo un po’ di tempo da investire, ho ripreso la versione 2 dello script “<a href="https://noblogo.org/aytin/generazione-otp-via-bash" rel="nofollow">Generazione OTP via bash</a>”, e ho deciso di renderlo un po’ più flessibile introducendo una logica <strong>crud</strong>. Anzi, il termine corretto dovrebbe essere “<strong>dave</strong>” (<strong>D</strong>elete, <strong>A</strong>dd, <strong>V</strong>iew, <strong>E</strong>dit).

Che altro aggiungere?</p>
<ul><li>Il file è ancora un insieme di coppie chiave-valore (nome account e chiave privata).</li>
<li>Il separatore è il simbolo “<strong>:</strong>”. Ma può essere configurato cambiando il valore della variabile “<strong>DELIMITER</strong>”.</li>
<li>Lo script è discretamente commentato così, in futuro, riuscirò a ricordare perché ho fatto quello che ho fatto (tipo “Memento”).</li>
<li>Le opzioni di gpg per la cifratura/decifratura del file sono configurabili nello script modificando il valore delle variabili “<strong>GPG_ENC_OPTIONS</strong>” e “<strong>GPG_DEC_OPTIONS</strong>”.</li>
<li>L’uso di <strong>getopts,</strong> mi rendo conto, è solo un esercizio di stile. Non è molto utile per come l’ho usato, visto che ogni optarg corrisponde ad un’operazione autoconclusiva.</li>
<li>Ero partito per rimpiazzare ogni costrutto <strong>if</strong> con combinazioni di lista and / lista or ma le condizioni risultavano talmente complesse da rendere il codice francamente molto più incomprensibile di adesso. In alternativa avrei potuto delegare l’esecuzione delle condizioni ad un insieme di funzioni con una stratificazione tale da ricadere nel punto precedente: offuscamento permanente del codice che va a vanificare l’intenzione iniziale di semplificare il codice e renderlo più leggibile.</li></ul>

<pre><code class="language-bash">
#!/usr/local/bin/bash


init() {
    # CODICI D&#39;ERRORE
    DECRYPT_ERR=64
    INVALID_OPTION_ERR=65
    INVALID_ACCOUNT_ERR=66
    ACCOUNT_NOT_FOUND_ERR=67
    ACCOUNT_NAME_EMPTY_ERR=68
    ACCOUNT_EXISTS_ERR=69
    INVALID_KEY_ERR=70

    # Colorscheme
    LIGHT_GREEN=&#39;\033[1;32m&#39;
    LIGHT_RED=&#39;\033[1;31m&#39;
    NC=&#39;\033[0m&#39;

    # Path del file cifrato contentente i codici 2FA
    KEYS_2FA_FILE=&#34;&lt;path/file_enc.gpg&gt;&#34;

    # Separatore delle coppie chiave-valore
    DELIMITER=&#34;:&#34;

    # path gpg e opzioni di cifratura/decifratura
    GPG_TTY=$(tty)
    export GPG_TTY

    #GPG_COMMAND=&#34;/usr/local/bin/gpg&#34;
    GPG_ENC_OPTIONS=&#34;--yes -c --s2k-cipher-algo aes256 --s2k-digest-algo sha512 --s2k-mode 3 --s2k-count 260000 - &#34;
    GPG_DEC_OPTIONS=&#39;-d&#39;

    # Caratteri consentiti nel nome account
    PATTERN=&#39;^[a-zA-Z0-9._-]+$&#39;;

    # Su MacOS è disponibile &#34;pbcopy&#34;, su *nix &#34;xsel&#34;
    case &#34;$(sysctl hw.targettype 2&gt;/dev/null)&#34; in
        &#34;&#34;)
            CLIPBOARD=&#34;xsel -bi&#34;
            GPG_COMMAND=&#34;/usr/bin/gpg&#34;;;
         *)
            CLIPBOARD=&#34;pbcopy&#34;
            GPG_COMMAND=&#34;/usr/local/bin/gpg&#34;;;
    esac

    # Decifra e carica in ram il contenuto del file cifrato.
    echo &#34;Decifratura del file degli account in corso...&#34;
    KEYS_2FA_FILE_DEC=$(${GPG_COMMAND} ${GPG_DEC_OPTIONS} &#34;${KEYS_2FA_FILE}&#34; 2&gt;/dev/null)

    # Se la password non è corretta, restituisce un errore.
    [[ -z &#34;${KEYS_2FA_FILE_DEC}&#34; ]] &amp;&amp; { err_msg &#34;${DECRYPT_ERR}&#34; 1; }
    info_msg &#34;Fatto.&#34;
}


get_account() {
    ACCOUNT_STDIO=&#34;$1&#34;

    # Check nome account vuoto
    [[ -z &#34;${ACCOUNT_STDIO}&#34; ]] &amp;&amp; return &#34;${ACCOUNT_NAME_EMPTY_ERR}&#34;

    # Check caratteri invalidi nel nome account
    [[ ! &#34;${ACCOUNT_STDIO}&#34; =~ $PATTERN ]] &amp;&amp; return &#34;${INVALID_ACCOUNT_ERR}&#34;

    # Estrae l&#39;elemento contenente l&#39;id dato in input
    OTP_LINE=$(echo &#34;${KEYS_2FA_FILE_DEC}&#34; | grep &#34;${ACCOUNT_STDIO}:&#34;)

    # Se l&#39;account non esiste, restituisce un errore
    [[ -z &#34;${OTP_LINE}&#34; ]] &amp;&amp; return &#34;${ACCOUNT_NOT_FOUND_ERR}&#34;

    ACCOUNT=$(echo $OTP_LINE | cut -d&#34;${DELIMITER}&#34; -f 1)
    KEY=$(echo $OTP_LINE | cut -d&#34;${DELIMITER}&#34; -f 2)
}


crypt_account_file() {
    ACCOUNT_FILE=&#34;$1&#34;
    echo -e &#34;\nCifratura del file degli account in corso...&#34;
    ${GPG_COMMAND} -o &#34;$KEYS_2FA_FILE&#34; ${GPG_ENC_OPTIONS} &lt;&lt;&lt; $(echo -e &#34;${ACCOUNT_FILE}&#34;)
}


no_param() {
    clear
    help
    exit 0
}


# Visualizza messaggi di errore su stdio
err_msg() {
    if [[ $# -ne 2 ]]; then
        echo -r&#34;${LIGHT_RED}[DEBUG] [err_msg] Numero di parametri non corretto.${NC}.&#34;; exit 1
    else
        case $1 in
            &#34;${DECRYPT_ERR}&#34;            ) echo -e &#34;${LIGHT_RED}[ERRORE] Impossibile decriptare il file.${NC}&#34;;;
            &#34;${INVALID_OPTION_ERR}&#34;     ) echo -e &#34;${LIGHT_RED}[ERRORE] Opzione \&#34;$OPTARG\&#34; non valida.${NC}&#34;;;
            &#34;${INVALID_ACCOUNT_ERR}&#34;    ) echo -e &#34;${LIGHT_RED}[ERRORE] Rilevati caratteri non consentiti.\nI caratteri ammessi sono: [a-z][A-Z][0-9].-_${NC}&#34;;;
            &#34;${ACCOUNT_NOT_FOUND_ERR}&#34;  ) echo -e &#34;${LIGHT_RED}[ERRORE] Account non trovato${NC}&#34;;;
            &#34;${ACCOUNT_NAME_EMPTY_ERR}&#34; ) echo -e &#34;${LIGHT_RED}[ERRORE] Nome account vuoto.${NC}&#34;;;
            &#34;${ACCOUNT_EXISTS_ERR}&#34;     ) echo -e &#34;${LIGHT_RED}[ERRORE] Nome account già esistente${NC}&#34;;;
            &#34;${INVALID_KEY_ERR}&#34;        ) echo -e &#34;${LIGHT_RED}[ERRORE] Chiave privata nulla o non corretta.\nLa chiave privata deve essere base32.\n${NC}&#34;;;
            *                           ) echo -e &#34;${LIGHT_RED}[ERRORE] Errore generico.${NC}&#34;;;
        esac

        [[ $2 -eq 1 ]] &amp;&amp; exit $2;
    fi
}


# Visualizza messaggi info su stdio
info_msg() {
    echo -e &#34;${LIGHT_GREEN}$1${NC}&#34;
}


continue_msg() {
    case $1 in
        &#34;insert&#34;    ) ACTION=&#34;creato&#34;;;
        &#34;edit&#34;      ) ACTION=&#34;modificato&#34;;;
        &#34;delete&#34;    ) ACTION=&#34;cancellato&#34;;;
    esac

    echo -n &#34;L&#39;account \&#34;${ACCOUNT}\&#34; sta per essere ${ACTION}. Vuoi continuare? [y|N]: &#34;; read ANSWER;
}


continue_yn() {
        ANSWER=&#34;0&#34;
        while [[ &#34;${ANSWER}&#34; != &#34;y&#34; ]]; do
            [[ -z &#34;${ANSWER}&#34; || &#34;${ANSWER}&#34; == &#34;N&#34; ]] &amp;&amp; { echo &#34;Operazione annullata&#34;; exit; }
            [[ &#34;${ANSWER}&#34; != &#34;y&#34; || &#34;${ANSWER}&#34; != &#34;N&#34; ]] &amp;&amp; continue_msg &#34;$1&#34;
        done
}


totp() {
    # Se la&#39;ccount non esiste o è invalido, esco con errore
    get_account &#34;$1&#34;; RET_CODE=$?
    case &#34;${RET_CODE}&#34; in
        &#34;${ACCOUNT_NOT_FOUND_ERR}&#34;  ) err_msg &#34;${ACCOUNT_NOT_FOUND_ERR}&#34; 1;;
        &#34;${INVALID_ACCOUNT_ERR}&#34;    ) err_msg &#34;${INVALID_ACCOUNT_ERR}&#34; 1;;
    esac

    echo -e &#34;\nCopia OTP nella clipboard...&#34;
    # Calcola l&#39;otp e lo trasferisce nella clipboard con xsel
    oathtool --totp -b &#34;${KEY}&#34; | $CLIPBOARD
    info_msg &#34;Fatto.&#34;
    exit
}


insert() {
    # Se l&#39;account non esiste o è invalido, esco con errore
    get_account &#34;$1&#34;; RET_CODE=$?
    case &#34;${RET_CODE}&#34; in
        0                           ) err_msg &#34;${ACCOUNT_EXISTS_ERR}&#34; 1;;
        &#34;${INVALID_ACCOUNT_ERR}&#34;    ) err_msg &#34;${INVALID_ACCOUNT_ERR}&#34; 1;;
    esac

    ACCOUNT=&#34;$1&#34;

    # Inserisci la chiave
    echo -en &#34;Nuovo account: ${ACCOUNT}\nInserisci la nuova chiave privata: &#34;; read KEY
    while [[ -z &#34;${KEY}&#34; || ! $(echo ${KEY} | oathtool -b - 2&gt;/dev/null) ]]; do
        err_msg &#34;${INVALID_KEY_ERR}&#34; 0
        echo -n &#34;Inserisci la chiave privata: &#34;; read KEY
    done

    info_msg &#34;\nNuovo account:        ${ACCOUNT}\nNuova chiave privata: ${KEY}&#34;
    continue_yn &#34;insert&#34;

    # Appendo il nuovo record nel file degli account
    KEYS_2FA_FILE_DEC+=&#34;\n${ACCOUNT}${DELIMITER}${KEY}&#34;
    info_msg &#34;L&#39;account \&#34;${ACCOUNT}\&#34; è stato creato con successo.&#34;

    # Cifratura del file degli account
    crypt_account_file &#34;${KEYS_2FA_FILE_DEC}&#34;
    info_msg &#34;Fatto.&#34;
    exit
}


edit() {
    # Se la&#39;ccount non esiste o è invalido, esco con errore
    get_account &#34;$1&#34;; RET_CODE=$?
    case &#34;${RET_CODE}&#34; in
        &#34;${ACCOUNT_NOT_FOUND_ERR}&#34;  ) err_msg &#34;${ACCOUNT_NOT_FOUND_ERR}&#34; 1;;
        &#34;${INVALID_ACCOUNT_ERR}&#34;    ) err_msg &#34;${INVALID_ACCOUNT_ERR}&#34; 1;;
    esac

    # Faccio una copia dell&#39;account per un eventuale ripristino durante
    # la fase di input
    ACCOUNT_TEMP=&#34;${ACCOUNT}&#34;
    ACCOUNT_MOD=&#34;${ACCOUNT}&#34;

    # Inserisco il nuovo nome account.
    # 1. Se è vuoto: è la conferma dell&#39;account inserito all&#39;inizio e proseguo
    # 2. Se il nome è invalido: sollevo un&#39;eccezione e reitero
    # 3. Se il nome è valido:
    #    a. Se è un nome nuovo: acquisisco il nuovo nome, recupero nome e chiave dell&#39;account di partenza e proseguo
    #    b. Sè è lo stesso nome di partenza: è la conferma dell&#39;account iniziale e proseguo
    #    c. Se è un altro nome esistente o invalido: come il punto 2.
    while true; do
        echo
        echo -en &#34;Account: ${ACCOUNT}\nInserisci nuovo nome account: &#34;; read ACCOUNT_MOD;
        get_account &#34;${ACCOUNT_MOD}&#34;; RET_CODE=$?
        case &#34;${RET_CODE}&#34; in
            # Punto 1.
            &#34;${ACCOUNT_NAME_EMPTY_ERR}&#34; ) ACCOUNT_MOD=&#34;${ACCOUNT}&#34;; break;;
            # Punto 2
            &#34;${INVALID_ACCOUNT_ERR}&#34;    ) err_msg &#34;${INVALID_ACCOUNT_ERR}&#34; 0;;
            # Punto 3a.
            &#34;${ACCOUNT_NOT_FOUND_ERR}&#34;  ) get_account &#34;${ACCOUNT_TEMP}&#34;; break;;
            # Punti 3b e 3c, rispettivamente
            0                           ) [[ &#34;${ACCOUNT_MOD}&#34; == &#34;${ACCOUNT_TEMP}&#34; ]] &amp;&amp; break \
                                                                                 || { get_account &#34;${ACCOUNT_TEMP}&#34;; err_msg &#34;${ACCOUNT_EXISTS_ERR}&#34; 0; };;
        esac
    done

    [[ &#34;${ACCOUNT}&#34; != &#34;${ACCOUNT_MOD}&#34; &amp;&amp; ! -z &#34;${ACCOUNT_MOD}&#34; ]] &amp;&amp; msg=&#34;\nL&#39;account \&#34;${ACCOUNT}\&#34; è stato modificato in \&#34;${ACCOUNT_MOD}\&#34;.&#34; \
                                                                    || msg=&#34;\nNessuna modifica rilevata.\n&#34;
    info_msg &#34;${msg}&#34;

    # Modifica la chiave privata
    KEY_MOD=&#34;${KEY}&#34;
    echo

    while true; do
        echo -en &#34;Chiave privata: ${KEY}\nInserisci la nuova chiave privata: &#34;; read KEY_MOD
        if [[ -z $KEY_MOD ]]; then
            KEY_MOD=&#34;${KEY}&#34;; break
        elif [[ ! $(echo &#34;${KEY_MOD}&#34; | oathtool -b - 2&gt;/dev/null) ]]; then
            err_msg &#34;${INVALID_KEY_ERR}&#34; 0; KEY_MOD=&#34;${KEY}&#34;
        else
            break
        fi
    done

    # Se c&#39;è stata almeno una modifica (account/key), sovrascrivo il record. Altrimenti esco senza fare nulla.
    if [[ &#34;${ACCOUNT}&#34; != &#34;${ACCOUNT_MOD}&#34; || &#34;${KEY}&#34; != &#34;${KEY_MOD}&#34; ]]; then
        # Conferma modifica

        info_msg &#34;\nAccount:              ${ACCOUNT}\nChiave privata:       ${KEY}\nNuovo account:        ${ACCOUNT_MOD}\nNuova chiave privata: ${KEY_MOD}\n&#34;
        continue_yn &#34;edit&#34;

        # Modifica del file degli account
        KEYS_2FA_FILE_DEC_TEMP=$(sed &#34;s/${ACCOUNT}${DELIMITER}${KEY}/${ACCOUNT_MOD}${DELIMITER}${KEY_MOD}/&#34; &lt;&lt;&lt; &#34;${KEYS_2FA_FILE_DEC}&#34;)
        info_msg &#34;L&#39;account \&#34;${ACCOUNT}\&#34; è stato modificato con successo.&#34;

        # Cifratura del file degli account
        crypt_account_file &#34;${KEYS_2FA_FILE_DEC_TEMP}&#34;
    else
        info_msg &#34;Nessuna modifica rilevata.&#34;
    fi
    info_msg &#34;Fatto.&#34;
}


delete() {
    if get_account &#34;$1&#34;; then
        # Conferma eliminazione
        info_msg &#34;\nAccount:              ${ACCOUNT}\nChiave privata:       ${KEY}\n&#34;
        continue_yn &#34;delete&#34;

        # Cancellazione account
        KEYS_2FA_FILE_DEC_TEMP=$(sed &#34;/$ACCOUNT/d&#34; &lt;&lt;&lt; &#34;${KEYS_2FA_FILE_DEC}&#34;)
        info_msg &#34;L&#39;account \&#34;$ACCOUNT\&#34; è stato eliminato.&#34;

        # Cifratura del nuovo file degli account
        crypt_account_file &#34;${KEYS_2FA_FILE_DEC_TEMP}&#34;
        info_msg &#34;Fatto.&#34;
    else
        err_msg &#34;${ACCOUNT_NOT_FOUND_ERR}&#34; 1
    fi
}


list() {
    echo
    while read LINE; do
        cut -d&#34;${DELIMITER}&#34; -f 1 &lt;&lt;&lt; &#34;${LINE}&#34;
    done &lt;&lt;&lt; &#34;${KEYS_2FA_FILE_DEC}&#34;
}


help() {
cat&lt;&lt;EOF
Usa come: ./totp.sh [options] ARG
dove:
    [options]
        -l restituisce la lista degli account.
        -t &lt;nome_account&gt; restituisce l&#39;otp per quell&#39;account.
        -i &lt;nome_account&gt; inserisce un nuovo account.
        -d &lt;nome_account&gt; cancella &lt;nome_account&gt;.
        -e &lt;nome_account&gt; modifica &lt;nome_account&gt;.
        -h stampa questa pagina di help.

ESEMPI:
    RESTITUISCE LA LISTA DEGLI ACCOUNT
    totp.sh -l

    INSERISCE UN ACCOUNT
    totp.sh -i dropbox

    CANCELLA UN ACCOUNT
    totp.sh -d megaupload

    MODIFICA UN ACCOUNT
    totp.sh -e paypal

    GENERA OTP
    totp.sh -t firefoxsync
EOF
}


main() {
    init
    while getopts &#34;lt:i:e:d:&#34; opt; do
        case $opt in
            l ) list ;;
            t ) totp $OPTARG ;;
            i ) insert $OPTARG ;;
            e ) edit $OPTARG ;;
            d ) delete $OPTARG ;;
            * ) clear;err_msg &#34;${INVALID_OPTION_ERR}&#34; 1;;
        esac
    done
    shift $(($OPTIND - 1))
}

[[ $# -eq 0 || $# -eq 1 &amp;&amp; &#34;$1&#34; == &#34;-h&#34; ]] &amp;&amp; no_param || main $*

</code></pre>

<p><a href="/aytin/tag:bash" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">bash</span></a> <a href="/aytin/tag:totp" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">totp</span></a> <a href="/aytin/tag:cryptography" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">cryptography</span></a> <a href="/aytin/tag:gpg" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">gpg</span></a> <a href="/aytin/tag:scripting" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">scripting</span></a></p>
]]></content:encoded>
      <guid>https://noblogo.org/aytin/gestione-totp-in-bash</guid>
      <pubDate>Wed, 21 Feb 2024 15:37:02 +0000</pubDate>
    </item>
    <item>
      <title>Come creare un certificato digitale</title>
      <link>https://noblogo.org/aytin/come-creare-un-certificato-digitale</link>
      <description>&lt;![CDATA[(pubblicato il 30 gennaio 2023)&#xA;certificati digitali&#xA;smalliFonte:  Foto di a href=&#34;https://pixabay.com/it/users/skylarvision-2957633/?utmsource=link-attribution&amp;utmmedium=referral&amp;utmcampaign=image&amp;utmcontent=3344700&#34;skylarvision/a da a href=&#34;https://pixabay.com/it//?utmsource=link-attribution&amp;utmmedium=referral&amp;utmcampaign=image&amp;utmcontent=3344700&#34;Pixabay/a/i/small&#xA;&#xA;A volte, per ragioni di test o per uso domestico (configurazione di una vpn con openvpn per es. o di un server stunnel), mi càpita di avere bisogno di un certificato digitale.&#xA;&#xA;Finché il certificato è self-signed, l&#39;operazione richiede veramente pochissimo sforzo. Ma quando devo far riferimento ad una CA interna, la questione si complica.&#xA;!--more--&#xA;Proverò adesso a descrivere le operazioni che compio quando devo creare:&#xA;&#xA;CA + certificati server +  certificati client&#xA;CA + Intermediate CA + certificati server + certificati client&#xA;&#xA;Indice&#xA;&#xA;a href=&#34;#cose-un-certificato-digitale&#34;Cos’è un certificato digitale/a&#xA;a href=&#34;#openssl&#34;OpenSSL/a&#xA;a href=&#34;#struttura-laboratorio&#34;Struttura del “laboratorio”/a&#xA;a href=&#34;#creazione-ca-certificati-client-server&#34;Caso 1: Creazione delle CA e dei certificati client e server/a&#xA;   a href=&#34;#creazione-ca-certificati-server&#34;Creazione della CA per i certificati server/a&#xA;      a href=&#34;#configurazione-ca-server&#34;Configurazione CA server/a&#xA;      a href=&#34;#creazione-ca-server&#34;Creazione CA server/a&#xA;   a href=&#34;#creazione-certificato-server&#34;Creazione del certificato server/a&#xA;      a href=&#34;#configurazione-csr-server&#34;Configurazione csr server/a&#xA;      a href=&#34;#creazione-csr-server&#34;Creazione csr server/a&#xA;      a href=&#34;#configurazione-crt-server&#34;Configurazione crt server/a&#xA;      a href=&#34;#creazione-crt-server&#34;Creazione crt server/a&#xA;   a href=&#34;#creazione-ca-certificati-client&#34;Creazione della CA per i certificati client/a&#xA;      a href=&#34;#configurazione-ca-client&#34;Configurazione CA client/a&#xA;      a href=&#34;#creazione-ca-client&#34;Creazione CA client/a&#xA;   a href=&#34;#creazione-certificato-client&#34;Creazione del certificato client/a&#xA;      a href=&#34;#configurazione-csr-client&#34;Configurazione csr client/a&#xA;      a href=&#34;#creazione-csr-client&#34;Creazione csr client/a&#xA;      a href=&#34;#configurazione-crt-client&#34;Configurazione crt client/a&#xA;      a href=&#34;#creazione-crt-client&#34;Creazione crt client/a&#xA;   a href=&#34;#riassunto&#34;Riassunto/a&#xA;a href=&#34;#creazione-ca-intermediate-certificati-client-server&#34;Caso 2: Creazione di CA, Intermediate CA e dei certificati client e server/a&#xA;&#xA;a id=&#34;cose-un-certificato-digitale&#34;Cos&#39;è un certificato digitale/a&#xA;&#xA;I certificati digitali sono degli oggetti utilizzati nelle comunicazioni per fornire un canale sicuro su un mezzo intrinsecamente insicuro (internet) e vengono costruiti secondo lo standard X.509, un protocollo che definisce il formato dei certificati e l&#39;insieme degli elementi di contorno come elementi autoritativi, elenchi di revoca e modalità di convalida.&#xA;&#xA;Un certificato digitale è un oggetto crittografico costituito da una parte pubblica (chiave pubblica + informazioni personali del possessore del certificato) e una parte privata (la chiave privata).&#xA;&#xA;La parte pubblica viene firmata digitalmente per garantire autenticazione e integrità e può essere eseguita:&#xA;&#xA;da se stesso (i cosiddetti certificati self-signed, una sorta di autocertificazione)&#xA;da una CA, un&#39;autorità superiore che garantisce che le informazioni del certificato sono di chi dice di essere.&#xA;&#xA;a id=&#34;openssl&#34;OpenSSL/a&#xA;&#xA;Per la costruzione di un certificato digitale, uso OpenSSL che è sia una libreria crittografica che implementa i protocolli TLS e SSL (deprecato), sia un toolkit professionale per l&#39;utilizzo di funzionalità di crittografia.,&#xA;&#xA;OpenSSL ha una cli molto complessa che può essere in parte configurata, soprattutto nella parte di definizione dei certificati, da un file di configurazione, openssl.cnf, un file .ini diviso in sezioni, contenenti una lista di coppie (chiave,valore).&#xA;&#xA;Nel proseguio, invece che usare un unico file omnicomprensivo, ne verrà usato solo un frammento in base al contesto (certificato o csr di ca, intermediate, server o client). Ho trovato questo approccio più flessibile e più chiaro.&#xA;&#xA;Negli esempi, userò questi file che avranno dei nomi &#34;parlanti&#34; in base a ciò che dovrò definire. Non mi dilungherò nella spiegazione delle singole opzioni perché:&#xA;&#xA;non le conosco tutte&#xA;approfondire ogni singola direttiva di openssl.cnf richiederebbe un blog dedicato.&#xA;&#xA;Si potrebbe ancora dissertare sulle versioni di openssl da usare, sui suoi fork ma non è questo il momento, forse in altri post (che sono in fase di preparazione.)&#xA;&#xA;a id=&#34;struttura-laboratorio&#34;Struttura del &#34;laboratorio&#34;/a&#xA;&#xA;Data la destinazione dei certificati (test o uso strettamente interno), i certificati vengono generati nella maniera più compatta possibile.&#xA;&#xA;Per le root CA, gli unici certificati self-signed, viene generato il csr e il crt in un&#39;unica soluzione.&#xA;Le intermediate CA vengono generate, come ogni altro certificato server / client, producendo prima un csr che viene firmato dalla root CA per produrre il crt.&#xA;Anche i certificati server e client, come detto, vengono generati come l&#39;intermediate CA, producendo prima il csr che verrà firmato da una CA.&#xA;Ogni fase (per &#34;fase&#34; intendo produzione di un csr o di un crt) sarà &#34;guidata&#34; dal frammento di file di configurazione avente quel famoso nome &#34;parlante&#34;.&#xA;Prenderò in considerazione due casistiche. In una i certificati server e client verranno validati da una root CA. Nell&#39;altro, la root CA delegherà ad una intermediate CA la validazione di certificati server / client.&#xA;Nella prima casistica, per rendere la soluzione più generica, supporrò di avere una CA distinta per i certificati client e una per i server.&#xA;&#xA;⚠️ ⚠️ ⚠️ SUPER WARNING⚠️ ⚠️ ⚠️ &#xA;Non è questo il modo formalmente corretto per la creazione di un certificato, almeno non quello da usare per un ambiente di produzione.&#xA;&#xA;Non farò nessuna assunzione sulle modalità di cifratura, né sul tuning di openssl.&#xA;&#xA;Il mio obiettivo qui è: voglio ottenere un certificato nella maniera più rapida possibile (e per rapido intendo senza troppi sbattimenti nelle configurazioni perché, alla fine, tutto può essere scriptabile e quindi veloce).&#xA;&#xA;I file di configurazione saranno composti da massimo due sezioni contenenti le extensions X.509  necessarie per la creazione dei certificati. Il resto dei parametri sarà  passato nella cli piuttosto che nel file di configurazione.&#xA;&#xA;Mi rendo conto che per una buona comprensione occorrerebbe avere un po&#39; di confidenza con: &#xA;&#xA;i concetti di cifratura asimmetrica e di certificato digitale&#xA;configurazione e utilizzo base di openssl&#xA;conoscenza di openssl.cnf e extensions X.509&#xA;&#xA;a id=&#34;creazione-ca-certificati-client-server&#34;Caso 1: Creazione delle CA e dei certificati client e server/a&#xA;&#xA;La creazione della CA consisterà in un certificato self-signed ottenuto con un unico comando. Per i certificati invece verrà prodotto prima il csr che poi sarà firmato dalla CA al fine di ottenere il certificato vero e proprio.&#xA;&#xA;Piccola nota: gli attributi basicConstraints, nsCertType, extendedKeyUsage saranno determinanti per battezzare una CA piuttosto che un certificato client/server&#xA;&#xA;a id=&#34;creazione-ca-certificati-server&#34;Creazione della CA per i certificati server/a&#xA;&#xA;La creazione del certificato sarà one-shot.&#xA;&#xA;a id=&#34;configurazione-ca-server&#34;Configurazione CA server/a&#xA;&#xA;Il file di configurazione sarà cacert.cnf&#xA;###############&#xA;cacert.cnf &#xA;###############&#xA;[ v3ca ]&#xA;basicConstraints        = critical,CA:true&#xA;keyUsage                = critical, cRLSign, keyCertSign&#xA;subjectKeyIdentifier    = hash&#xA;authorityKeyIdentifier  = keyid:always,issuer&#xA;nsCertType              = sslCA&#xA;nsComment               = &#34;OpenSSL Generated CA Root Certificate&#34;&#xA;&#xA;a id=&#34;creazione-ca-server&#34;Creazione CA server/a&#xA;OUTPUTCERT=pathoutputcert&#xA;CONFCERT=pathfilecnf&#xA;&#xA;CA SERVER &#xA;openssl req \&#xA;-config $CONFCERT/cacert.cnf \&#xA;-extensions v3ca \ &#xA;-x509 \&#xA;-days 1825 -sha512 \&#xA;-nodes -newkey rsa:4096 \&#xA;-subj &#34;/C=IT/ST=Italia/CN=GlobalSign Test RootCA&#34; \&#xA;-keyout $OUTPUTCERT/caserver.key \&#xA;-out $OUTPUTCERT/caserver.crt&#xA;&#xA;openssl req : crea un csr&#xA;-config $CONFCERT/cacert.cnf : path del file di configurazione&#xA;-extensions v3ca : sezione del file da prendere come riferimento (in questo caso per le sole extensions X.509)&#xA;-x509 : fa in modo che l&#39;output sia direttamente un certificato piuttosto che un csr&#xA;-days 1825 -sha512 : fissa la scadenza a 5 anni e setta sha512 come algoritmo di digest.&#xA;-nodes -newkey rsa:4096 : crea una chiave rsa non cifrata a 4096 bit&#xA;-subj &#34;/C=IT/ST=Roma&#34;/CN=GlobalSign Test RootCA&#34; : distinguishedname passato da cli&#xA;-keyout $OUTPUTCERT/caserver.key : path di destinazione della chiave&#xA;-out $OUTPUTCERT/caserver.crt : path di destinazione del certificato CA.&#xA;&#xA;a id=&#34;creazione-certificato-server&#34;Creazione certificato server/a&#xA;&#xA;Verrà generato prima il csr (certificate signing request) che sarà firmato dalla CA per generare il certificato effettivo.&#xA;&#xA;a id=&#34;configurazione-csr-server&#34;Configurazione csr server/a&#xA;&#xA;Il file di configurazione per il csr sarà serverreq.cnf, diviso in 3 sezioni.&#xA;&#xA;La prima, req, è quella usata da openssl req (in assenza della quale verrà sollevata un&#39;eccezione) e contiene solo il rimando alle extensions X.509 da usare.&#xA;&#xA;Come prima, le extensions X.509 (v3serverreq) definiscono i giusti attributi per questo tipo di certificato.&#xA;&#xA;N.B.&#xA;L&#39;ultima sezione è quella degli alternativesname, la cui assenza non comporta problemi nella creazione del certificato ma potrebbe far fallire i check effettuati da alcuni prodotti, ad es. i webserver che pur riconoscendo nel Common Name il nome del servizio (es. la url) scatenano ugualmente l&#39;eccezione BADCERTDOMAIN&#xA;##################&#xA;serverreq.cnf &#xA;##################&#xA;[ req ]&#xA;reqextensions = v3serverreq&#xA;&#xA;[ v3serverreq ]&#xA;basicConstraints        = CA:FALSE&#xA;subjectKeyIdentifier    = hash&#xA;keyUsage                = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment&#xA;extendedKeyUsage        = serverAuth&#xA;nsCertType              = server&#xA;nsComment               = &#34;OpenSSL Generated Server Request Certificate&#34;&#xA;subjectAltName          = @altnames&#xA;&#xA;[ altnames ]&#xA;DNS.1 = server.web.local&#xA;&#xA;a id=&#34;creazione-csr-server&#34;Creazione csr server/a&#xA;&#xA;La sintassi è sostanzialmente uguale a quanto visto per la CA con la sola eccezione del parametro -x509 (che creava un crt invece che un csr). Il csr creerà il certificato e la coppia di chiavi pubblica e privata.&#xA;OUTPUTCERT=pathoutputcert&#xA;CONFCERT=pathfilecnf&#xA;&#xA;CSR Server &#xA;openssl req \&#xA;-config $CONFCERT/serverreq.cnf \&#xA;-nodes -newkey rsa:2048 \&#xA;-subj &#34;/C=IT/ST=Italia/L=Roma/O=My Company Ltd/OU=Divisione IT web/CN=server.web.local/emailAddress=it@web.local&#34; \&#xA;-keyout $OUTPUTCERT/server.key \&#xA;-out $OUTPUTCERT/server.csr&#xA;&#xA;openssl req : crea un csr&#xA;-config $CONFCERT/serverreq.cnf : path del file di configurazione&#xA;-nodes -newkey rsa:2048 : crea una chiave rsa non cifrata a 2048 bit&#xA;-subj &#34;/C=IT/ST=Italia/L=Roma/O=My Company Ltd/OU=Divisione IT web/CN=server.web.local/emailAddress=it@web.local&#34; : distinguishedname passato da cli&#xA;-keyout $OUTPUTCERT/server.key : path di destinazione della chiave &#xA;-out $OUTPUTCERT/server.csr : path di destinazione del csr&#xA;&#xA;a id=&#34;configurazione-crt-server&#34;Configurazione crt server/a&#xA;&#xA;A differenza di prima, non ho un file di configurazioni vero e proprio. Non usando il file generico openssl.cnf, l&#39;unico file che si può invocare dal comando openssl x509 è un elenco di attributi che il certificato dovrà possedere e questo ci è sufficiente per creare certificati client o server.&#xA;&#xA;Il file di configurazione per il crt sarà servercert.cnf.&#xA;###################&#xA;servercert.cnf &#xA;###################&#xA;basicConstraints        = CA:FALSE&#xA;subjectKeyIdentifier    = hash&#xA;keyUsage                = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment&#xA;extendedKeyUsage        = serverAuth&#xA;nsCertType              = server&#xA;nsComment               = &#34;OpenSSL Generated Server Certificate&#34;&#xA;subjectAltName          = @altnames&#xA;&#xA;[ altnames ]&#xA;DNS.1 = server.web.local&#xA;&#xA;a id=&#34;creazione-crt-server&#34;Creazione crt server/a&#xA;&#xA;Di fatto, le uniche operazione che compio sono quelle di firmare con la CA il certificato, di fissare una scadenza e l&#39;algoritmo di digest (altrimenti verrà applicato un default). Il resto, lo eredito dal csr (potrei sovrascrivere alcune cose come il subj) o dalle estensioni presenti nel file.&#xA;OUTPUTCERT=pathoutputcert&#xA;CONFCERT=pathfilecnf&#xA;&#xA;CRT Server &#xA;openssl x509 \&#xA;-extfile $CONFCERT/servercert.cnf \&#xA;-req -in $OUTPUTCERT/server.csr \&#xA;-days 365 -sha384 \&#xA;-CA $OUTPUTCERT/caserver.crt \&#xA;-CAkey $OUTPUTCERT/caserver.key \&#xA;-CAcreateserial \&#xA;-out $OUTPUTCERT/server.crt&#xA;&#xA;openssl x509 : crea un certificato x509&#xA;-extfile $CONFCERT/servercert.cnf : path del file di estensioni&#xA;-req : acquisizione del file csr da far firmare&#xA;-CA $OUTPUTCERT/caserver.crt : acquisizione del certificato della CA&#xA;-CAkey $OUTPUTCERT/caserver.key : acquisizione della chiave privata della CA che firmerà il certificato&#xA;-CAcreateserial : crea un seriale randomico per il certificato da creare. Normalmente una CA crea dei seriali progressivi memorizzandoli in un database interno.&#xA;-days 365 -sha384 : fissa ad un anno la scadenza del certificato e usa sha384 come algoritmo di digest&#xA;-out $OUTPUTCERT/server.crt : path di destinazione del certificato server.&#xA;&#xA;a id=&#34;creazione-ca-certificati-client&#34;Creazione della CA per i certificati client/a&#xA;&#xA;È praticamente la stessa cosa. La differenza è solo formale visto che, tecnicamente, i comandi che andrò ad eseguire saranno praticamente gli stessi (l&#39;unica differenza di rilievo riguarderà i certificati client a cui corrisponderanno estensioni X.509 leggermente differenti)&#xA;&#xA;a id=&#34;configurazione-ca-client&#34;Configurazione della CA client/a&#xA;&#xA;È lo stesso file visto a href=&#34;#configurazione-ca-server&#34;prima/a.&#xA;&#xA;a id=&#34;creazione-ca-client&#34;Creazione della CA client/a&#xA;&#xA;OUTPUTCERT=pathoutputcert&#xA;CONFCERT=pathfilecnf&#xA;&#xA;CA Client &#xA;openssl req \&#xA;-config $CONFCERT/cacert.cnf \&#xA;-extensions v3ca \ &#xA;-x509 \&#xA;-days 1825 -sha512 \&#xA;-nodes -newkey rsa:4096 \&#xA;-subj &#34;/C=IT/ST=Italia/CN=Digicert Test RootCA&#34; \&#xA;-keyout $OUTPUTCERT/caclient.key \&#xA;-out $OUTPUTCERT/caclient.crt&#xA;C&#39;è poco da dire. L&#39;unica variazione degna di nota è il subj, per il resto è identico alla a href=&#34;#creazione-ca-server&#34;creazione della ca server/a.&#xA;&#xA;a id=&#34;creazione-certificato-client&#34;Creazione certificato client/a&#xA;&#xA;Come fatto per il server, verrà generato prima il csr che sarà firmato dalla CA per generare il certificato effettivo.&#xA;&#xA;a id=&#34;configurazione-csr-client&#34;Configurazione csr client/a&#xA;&#xA;Come per i a href=&#34;#configurazione-csr-server&#34;certificati server/a, extendedKeyUsage e nsCertType stabiliscono come viene battezzato il certificato.&#xA;##################&#xA;clientreq.cnf &#xA;##################&#xA;[ req ]&#xA;reqextensions = v3clientreq&#xA;&#xA;[ v3clientreq ]&#xA;basicConstraints        = CA:FALSE&#xA;subjectKeyIdentifier    = hash&#xA;keyUsage                = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment&#xA;extendedKeyUsage        = clientAuth&#xA;nsCertType              = client&#xA;nsComment               = &#34;OpenSSL Generated Client Request Certificate&#34;&#xA;&#xA;a id=&#34;creazione-csr-client&#34;Creazione csr client/a&#xA;&#xA;Nel csr, viene creata la chiave, rsa a 2048 bit, e viene settato il subj.&#xA;OUTPUTCERT=pathoutputcert&#xA;CONFCERT=pathfilecnf&#xA;&#xA;CSR Client &#xA;openssl req \&#xA;-config $CONFCERT/clientreq.cnf \&#xA;-nodes -newkey rsa:2048 \&#xA;-subj &#34;/C=IT/ST=Italia/L=Roma/O=Another Company Ltd/OU=User WebApp/CN=John Doe/emailAddress=john.doe@unknown.local&#34; \&#xA;-keyout $OUTPUTCERT/johndoe.key \&#xA;-out $OUTPUTCERT/johndoe.csr&#xA;&#xA;a id=&#34;configurazione-crt-client&#34;Configurazione crt client/a&#xA;&#xA;Il file di configurazione raccoglie solo le estensioni con cui il certificato viene istanziato&#xA;###################&#xA;clientcert.cnf &#xA;###################&#xA;basicConstraints        = CA:FALSE&#xA;subjectKeyIdentifier    = hash&#xA;keyUsage                = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment&#xA;extendedKeyUsage        = clientAuth&#xA;nsCertType              = client&#xA;nsComment               = &#34;OpenSSL Generated Client Certificate&#34;&#xA;&#xA;a id=&#34;creazione-crt-client&#34;Creazione crt client/a&#xA;&#xA;Come per il certificato server, il certificato client verrà creato in seguito alla firma del csr dalla CA e aver settato scadenza a algoritmo di digest (altrimenti verrà applicato un default). Essendo un certificato client, viene mostrato anche come costruire un p12, utile nei casi in cui l&#39;autenticazione  del client viene fatta dal browser.&#xA;OUTPUTCERT=pathoutputcert&#xA;CONFCERT=pathfilecnf&#xA;&#xA;CRT Client &#xA;openssl x509 \&#xA;-extfile $CONFCERT/clientcert.cnf \&#xA;-req -in $OUTPUTCERT/johndoe.csr \&#xA;-days 365 -sha384 \&#xA;-CA $OUTPUTCERT/caclient.crt \&#xA;-CAkey $OUTPUTCERT/caclient.key \&#xA;-CAcreateserial \&#xA;-out $OUTPUTCERT/johndoe.crt&#xA;&#xA;Creazione p12 &#xA;openssl pkcs12 -export -inkey $OUTPUTCERT/johndoe.key -in $OUTPUTCERT/johndoe.crt -out $OUTPUTCERT/johndoe.p12&#xA;&#xA;a id=&#34;riassunto&#34;Riassunto/a&#xA;&#xA;Di seguito, la raccolta di quanto visto finora, allo scopo di rendere un colpo d&#39;occhio immediato e di dare una base per rendere scriptabile il processo che già così, tenendo presente i file di configurazione così composti e raccogliendo in uno script i comandi proposti, crea 2 CA, un certificato server e uno client (a meno dei subj di fantasia)&#xA;&#xA;File di configurazione:&#xA;###############&#xA;cacert.cnf &#xA;###############&#xA;[ v3ca ]&#xA;basicConstraints        = critical,CA:true&#xA;keyUsage                = critical, cRLSign, keyCertSign&#xA;subjectKeyIdentifier    = hash&#xA;authorityKeyIdentifier  = keyid:always,issuer&#xA;nsCertType              = sslCA&#xA;nsComment               = &#34;OpenSSL Generated CA Root Certificate&#34;&#xA;&#xA;##################&#xA;serverreq.cnf &#xA;##################&#xA;[ req ]&#xA;reqextensions = v3serverreq&#xA; &#xA;[ v3serverreq ]&#xA;basicConstraints        = CA:FALSE&#xA;subjectKeyIdentifier    = hash&#xA;keyUsage                = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment&#xA;extendedKeyUsage        = serverAuth&#xA;nsCertType              = server&#xA;nsComment               = &#34;OpenSSL Generated Server Request Certificate&#34;&#xA;subjectAltName          = @altnames&#xA;&#xA;[ altnames ]&#xA;DNS.1 = server.web.local&#xA;&#xA;###################&#xA;servercert.cnf &#xA;###################&#xA;basicConstraints        = CA:FALSE&#xA;subjectKeyIdentifier    = hash&#xA;keyUsage                = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment&#xA;extendedKeyUsage        = serverAuth&#xA;nsCertType              = server&#xA;nsComment               = &#34;OpenSSL Generated Server Certificate&#34;&#xA;subjectAltName          = @altnames&#xA; &#xA;[ altnames ]&#xA;DNS.1 = server.web.local&#xA;&#xA;##################&#xA;clientreq.cnf &#xA;##################&#xA;[ req ]&#xA;reqextensions = v3clientreq&#xA; &#xA;[ v3clientreq ]&#xA;basicConstraints        = CA:FALSE&#xA;subjectKeyIdentifier    = hash&#xA;keyUsage                = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment&#xA;extendedKeyUsage        = clientAuth&#xA;nsCertType              = client&#xA;nsComment               = &#34;OpenSSL Generated Client Request Certificate&#34;&#xA;&#xA;###################&#xA;clientcert.cnf &#xA;###################&#xA;basicConstraints        = CA:FALSE&#xA;subjectKeyIdentifier    = hash&#xA;keyUsage                = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment&#xA;extendedKeyUsage        = clientAuth&#xA;nsCertType              = client&#xA;nsComment               = &#34;OpenSSL Generated Client Certificate&#34;&#xA;Script di creazione certificati:&#xA;OUTPUTCERT=pathoutputcert&#xA;CONFCERT=pathfilecnf&#xA;&#xA;#############&#xA;CA SERVER &#xA;#############&#xA;openssl req -config $CONFCERT/cacert.cnf -extensions v3ca -x509 -days 1825 -sha512 -nodes -newkey rsa:4096 -subj &#34;/C=IT/ST=Italia/CN=GlobalSign Test RootCA&#34; -keyout $OUTPUTCERT/caserver.key -out $OUTPUTCERT/caserver.crt&#xA;&#xA;##########&#xA;Server &#xA;##########&#xA;csr&#xA;openssl req -config $CONFCERT/serverreq.cnf -nodes -newkey rsa:2048 -subj &#34;/C=IT/ST=Italia/L=Roma/O=My Company Ltd/OU=Divisione IT web/CN=server.web.local/emailAddress=it@web.local&#34; -keyout $OUTPUTCERT/server.key -out $OUTPUTCERT/server.csr&#xA;crt&#xA;openssl x509 -extfile $CONFCERT/servercert.cnf &#xA;-req -in $OUTPUTCERT/server.csr -days 365 -sha384 -CA $OUTPUTCERT/caserver.crt -CAkey $OUTPUTCERT/caserver.key -CAcreateserial -out $OUTPUTCERT/server.crt&#xA;&#xA;#############&#xA;CA Client &#xA;#############&#xA;openssl req -config $CONFCERT/cacert.cnf -extensions v3ca -x509 -days 1825 -sha512 -nodes -newkey rsa:4096 -subj &#34;/C=IT/ST=Italia/CN=Digicert Test RootCA&#34; -keyout $OUTPUTCERT/caclient.key -out $OUTPUTCERT/caclient.crt&#xA;&#xA;##########&#xA;Client &#xA;##########&#xA;csr&#xA;openssl req -config $CONFCERT/clientreq.cnf -nodes -newkey rsa:2048 -subj &#34;/C=IT/ST=Italia/L=Roma/O=Another Company Ltd/OU=User WebApp/CN=John Doe/emailAddress=john.doe@unknown.local&#34; -keyout $OUTPUTCERT/johndoe.key -out $OUTPUTCERT/johndoe.csr&#xA;crt &#xA;openssl x509 -extfile $CONFCERT/clientcert.cnf -req -in $OUTPUTCERT/johndoe.csr -days 365 -sha384 -CA $OUTPUTCERT/caclient.crt -CAkey $OUTPUTCERT/caclient.key -CAcreateserial -out $OUTPUTCERT/johndoe.crt&#xA;p12&#xA;openssl pkcs12 -export -inkey $OUTPUTCERT/johndoe.key -in $OUTPUTCERT/johndoe.crt -out $OUTPUTCERT/johndoe.p12&#xA;&#xA;a id=&#34;creazione-ca-intermediate-certificati-client-server&#34;Caso 2. Creazione di CA, Intermediate CA e dei certificati client e server/a&#xA;&#xA;Gli esempi che seguiranno saranno presentati sullo stile del riassunto perché fondamentalmente molto simili al caso 1. Si aggiunge un punto di intermediazione in più dato dal fatto che la CA delega ad una CA &#34;subalterna&#34; l&#39;incarico di verificare le paternità di coloro i quali richiedono certificati.&#xA;&#xA;Quindi avremo:&#xA;&#xA;una root CA, certificato self-signed&#xA;un&#39;Intermediate CA, validata dalla root CA (csr + crt firmato dalla root CA)&#xA; n certificati client/server validati dall&#39;Intermediate CA (csr + crt firmato dall&#39;Intermediate CA)&#xA;&#xA;Creazione certificati&#xA;OUTPUTCERT=$HOME/stunnel/build-cert&#xA;CONFCERT=$HOME/stunnel/conf.d&#xA;&#xA;###########&#xA;Root CA &#xA;###########&#xA;openssl req -config $CONFCERT/cacert.cnf -extensions v3ca -x509 -days 1825 -sha512 -nodes -newkey rsa:4096 -subj &#34;/C=IT/ST=Italia/CN=GlobalSign Test RootCA&#34; -keyout $OUTPUTCERT/rootca.key -out $OUTPUTCERT/rootca.crt&#xA;&#xA;###################&#xA;INTERMEDIATE CA &#xA;###################&#xA;csr&#xA;openssl req -config $CONFCERT/intermediatereq.cnf -nodes -newkey rsa:2048 -subj &#34;/C=IT/ST=Italia/CN=GlobalSign Test IntermediateCA&#34; -keyout $OUTPUTCERT/intermediate.key -out $OUTPUTCERT/intermediate.csr&#xA;crt&#xA;openssl x509 -extfile $CONFCERT/intermediatecert.cnf -req -in $OUTPUTCERT/intermediate.csr -days 365 -sha384 -CA $OUTPUTCERT/rootca.crt -CAkey $OUTPUTCERT/rootca.key -CAcreateserial -out $OUTPUTCERT/intermediate.crt&#xA;&#xA;##########&#xA;Server &#xA;##########&#xA;csr&#xA;openssl req -config $CONFCERT/serverreq.cnf -nodes -newkey rsa:2048 -subj &#34;/C=IT/ST=Italia/L=Roma/O=My Company Ltd/OU=Divisione IT web/CN=server.web.local/emailAddress=it@web.local&#34; -keyout $OUTPUTCERT/server.key -out $OUTPUTCERT/server.csr&#xA;crt&#xA;openssl x509 -extfile $CONFCERT/servercert.cnf &#xA;-req -in $OUTPUTCERT/server.csr -CA $OUTPUTCERT/intermediate.crt -CAkey $OUTPUTCERT/intermediate.key -CAcreateserial -days 365 -sha384 -out $OUTPUTCERT/server.crt&#xA;&#xA;##########&#xA;Client &#xA;##########&#xA;csr&#xA;openssl req -config $CONFCERT/clientreq.cnf -nodes -newkey rsa:2048 -subj &#34;/C=IT/ST=Italia/L=Roma/O=Another Company Ltd/OU=User WebApp/CN=John Doe/emailAddress=john.doe@unknown.local&#34; -keyout $OUTPUTCERT/johndoe.key -out $OUTPUTCERT/johndoe.csr&#xA;crt&#xA;openssl x509 -extfile $CONFCERT/clientcert.cnf -req -in $OUTPUTCERT/johndoe.csr -days 365 -sha384 -CA $OUTPUTCERT/intermediate.crt -CAkey $OUTPUTCERT/intermediate.key -CAcreateserial -out $OUTPUTCERT/johndoe.crt&#xA;p12&#xA;openssl pkcs12 -export -inkey $OUTPUTCERT/johndoe.key -in $OUTPUTCERT/legs.crt -out $OUTPUT_CERT/johndoe.p12&#xA;&#xA;#DigitalCertificate #x509 #csr #openssl #ca #cryptography #AsymmetricEncryption #SymmetricEncryption #DigitalSignature]]&gt;</description>
      <content:encoded><![CDATA[<p><strong><em>(pubblicato il 30 gennaio 2023)</em></strong>
<img src="https://pixelfed.uno/storage/m/_v2/489827599091373610/42a8ecf32-5a8865/nGFJPa2PgFzw/Iofc5MJiTuOUqX8BRjCeTXxgRR5HVNb0aJIcgoFR.jpg" alt="certificati digitali">
<small><i>Fonte:  Foto di <a href="https://pixabay.com/it/users/skylarvision-2957633/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=3344700" rel="nofollow">skylarvision</a> da <a href="https://pixabay.com/it//?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=3344700" rel="nofollow">Pixabay</a></i></small></p>

<p>A volte, per ragioni di test o per uso domestico (configurazione di una vpn con openvpn per es. o di un <a href="https://noblogo.org/aytin/stunnel-cose-e-come-si-configura" rel="nofollow">server stunnel</a>), mi càpita di avere bisogno di un certificato digitale.</p>

<p>Finché il certificato è self-signed, l&#39;operazione richiede veramente pochissimo sforzo. Ma quando devo far riferimento ad una CA interna, la questione si complica.

Proverò adesso a descrivere le operazioni che compio quando devo creare:</p>
<ol><li>CA + certificati server +  certificati client</li>
<li>CA + Intermediate CA + certificati server + certificati client</li></ol>

<h2 id="indice">Indice</h2>
<ol><li><a href="#cose-un-certificato-digitale" rel="nofollow">Cos’è un certificato digitale</a></li>
<li><a href="#openssl" rel="nofollow">OpenSSL</a></li>
<li><a href="#struttura-laboratorio" rel="nofollow">Struttura del “laboratorio”</a></li>
<li><a href="#creazione-ca-certificati-client-server" rel="nofollow">Caso 1: Creazione delle CA e dei certificati client e server</a>
<ol><li><a href="#creazione-ca-certificati-server" rel="nofollow">Creazione della CA per i certificati server</a>
<ol><li><a href="#configurazione-ca-server" rel="nofollow">Configurazione CA server</a></li>
<li><a href="#creazione-ca-server" rel="nofollow">Creazione CA server</a></li></ol></li>
<li><a href="#creazione-certificato-server" rel="nofollow">Creazione del certificato server</a>
<ol><li><a href="#configurazione-csr-server" rel="nofollow">Configurazione csr server</a></li>
<li><a href="#creazione-csr-server" rel="nofollow">Creazione csr server</a></li>
<li><a href="#configurazione-crt-server" rel="nofollow">Configurazione crt server</a></li>
<li><a href="#creazione-crt-server" rel="nofollow">Creazione crt server</a></li></ol></li>
<li><a href="#creazione-ca-certificati-client" rel="nofollow">Creazione della CA per i certificati client</a>
<ol><li><a href="#configurazione-ca-client" rel="nofollow">Configurazione CA client</a></li>
<li><a href="#creazione-ca-client" rel="nofollow">Creazione CA client</a></li></ol></li>
<li><a href="#creazione-certificato-client" rel="nofollow">Creazione del certificato client</a>
<ol><li><a href="#configurazione-csr-client" rel="nofollow">Configurazione csr client</a></li>
<li><a href="#creazione-csr-client" rel="nofollow">Creazione csr client</a></li>
<li><a href="#configurazione-crt-client" rel="nofollow">Configurazione crt client</a></li>
<li><a href="#creazione-crt-client" rel="nofollow">Creazione crt client</a></li></ol></li>
<li><a href="#riassunto" rel="nofollow">Riassunto</a></li></ol></li>
<li><a href="#creazione-ca-intermediate-certificati-client-server" rel="nofollow">Caso 2: Creazione di CA, Intermediate CA e dei certificati client e server</a></li></ol>

<h2 id="a-id-cose-un-certificato-digitale-cos-è-un-certificato-digitale-a"><a id="cose-un-certificato-digitale">Cos&#39;è un certificato digitale</a></h2>

<p>I certificati digitali sono degli oggetti utilizzati nelle comunicazioni per fornire un canale sicuro su un mezzo intrinsecamente insicuro (internet) e vengono costruiti secondo lo standard <a href="https://en.wikipedia.org/wiki/X.509" rel="nofollow">X.509</a>, un protocollo che definisce il formato dei certificati e l&#39;insieme degli elementi di contorno come elementi autoritativi, elenchi di revoca e modalità di convalida.</p>

<p>Un certificato digitale è un oggetto crittografico costituito da una parte pubblica (chiave pubblica + informazioni personali del possessore del certificato) e una parte privata (la chiave privata).</p>

<p>La parte pubblica viene firmata digitalmente per garantire autenticazione e integrità e può essere eseguita:</p>
<ul><li>da se stesso (i cosiddetti certificati self-signed, una sorta di autocertificazione)</li>
<li>da una CA, un&#39;autorità superiore che garantisce che le informazioni del certificato sono di chi dice di essere.</li></ul>

<h2 id="a-id-openssl-openssl-a"><a id="openssl">OpenSSL</a></h2>

<p>Per la costruzione di un certificato digitale, uso <strong><a href="https://www.openssl.org/" rel="nofollow">OpenSSL</a></strong> che è sia una libreria crittografica che implementa i protocolli TLS e SSL (deprecato), sia un toolkit professionale per l&#39;utilizzo di funzionalità di crittografia.,</p>

<p>OpenSSL ha una cli molto complessa che può essere in parte configurata, soprattutto nella parte di definizione dei certificati, da un file di configurazione, <strong>openssl.cnf</strong>, un file <em>.ini</em> diviso in sezioni, contenenti una lista di coppie (chiave,valore).</p>

<p>Nel proseguio, invece che usare un unico file omnicomprensivo, ne verrà usato solo un frammento in base al contesto (certificato o csr di ca, intermediate, server o client). Ho trovato questo approccio più flessibile e più chiaro.</p>

<p>Negli esempi, userò questi file che avranno dei nomi “parlanti” in base a ciò che dovrò definire. Non mi dilungherò nella spiegazione delle singole opzioni perché:</p>
<ol><li>non le conosco tutte</li>
<li>approfondire ogni singola direttiva di openssl.cnf richiederebbe un blog dedicato.</li></ol>

<p>Si potrebbe ancora dissertare sulle versioni di openssl da usare, sui suoi fork ma non è questo il momento, forse in altri post (che sono in fase di preparazione.)</p>

<h2 id="a-id-struttura-laboratorio-struttura-del-laboratorio-a"><a id="struttura-laboratorio">Struttura del “laboratorio”</a></h2>

<p>Data la destinazione dei certificati (test o uso strettamente interno), i certificati vengono generati nella maniera più compatta possibile.</p>
<ul><li>Per le root CA, gli unici certificati self-signed, viene generato il csr e il crt in un&#39;unica soluzione.</li>
<li>Le intermediate CA vengono generate, come ogni altro certificato server / client, producendo prima un csr che viene firmato dalla root CA per produrre il crt.</li>
<li>Anche i certificati server e client, come detto, vengono generati come l&#39;intermediate CA, producendo prima il csr che verrà firmato da una CA.</li>
<li>Ogni fase (per “fase” intendo produzione di un csr o di un crt) sarà “guidata” dal frammento di file di configurazione avente quel famoso nome “parlante”.</li>
<li>Prenderò in considerazione due casistiche. In una i certificati server e client verranno validati da una root CA. Nell&#39;altro, la root CA delegherà ad una intermediate CA la validazione di certificati server / client.</li>
<li>Nella prima casistica, per rendere la soluzione più generica, supporrò di avere una CA distinta per i certificati client e una per i server.</li></ul>

<p>⚠️ ⚠️ ⚠️ <strong>SUPER WARNING</strong>⚠️ ⚠️ ⚠️
Non è questo il modo formalmente corretto per la creazione di un certificato, almeno non quello da usare per un ambiente di produzione.</p>

<p>Non farò nessuna assunzione sulle modalità di cifratura, né sul tuning di openssl.</p>

<p>Il mio obiettivo qui è: voglio ottenere un certificato nella maniera più rapida possibile (e per rapido intendo senza troppi sbattimenti nelle configurazioni perché, alla fine, tutto può essere scriptabile e quindi veloce).</p>

<p>I file di configurazione saranno composti da massimo due sezioni contenenti le extensions X.509  necessarie per la creazione dei certificati. Il resto dei parametri sarà  passato nella cli piuttosto che nel file di configurazione.</p>

<p>Mi rendo conto che per una buona comprensione occorrerebbe avere un po&#39; di confidenza con:</p>
<ul><li>i concetti di cifratura asimmetrica e di certificato digitale</li>
<li>configurazione e utilizzo base di openssl</li>
<li>conoscenza di openssl.cnf e extensions X.509</li></ul>

<h2 id="a-id-creazione-ca-certificati-client-server-caso-1-creazione-delle-ca-e-dei-certificati-client-e-server-a"><a id="creazione-ca-certificati-client-server">Caso 1: Creazione delle CA e dei certificati client e server</a></h2>

<p>La creazione della CA consisterà in un certificato self-signed ottenuto con un unico comando. Per i certificati invece verrà prodotto prima il csr che poi sarà firmato dalla CA al fine di ottenere il certificato vero e proprio.</p>

<p>Piccola nota: gli attributi <strong>basicConstraints</strong>, <strong>nsCertType</strong>, <strong>extendedKeyUsage</strong> saranno determinanti per battezzare una CA piuttosto che un certificato client/server</p>

<h3 id="a-id-creazione-ca-certificati-server-creazione-della-ca-per-i-certificati-server-a"><a id="creazione-ca-certificati-server">Creazione della CA per i certificati server</a></h3>

<p>La creazione del certificato sarà one-shot.</p>

<h4 id="a-id-configurazione-ca-server-configurazione-ca-server-a"><a id="configurazione-ca-server">Configurazione CA server</a></h4>

<p>Il file di configurazione sarà <strong>ca_cert.cnf</strong></p>

<pre><code>###############
# ca_cert.cnf #
###############
[ v3_ca ]
basicConstraints        = critical,CA:true
keyUsage                = critical, cRLSign, keyCertSign
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid:always,issuer
nsCertType              = sslCA
nsComment               = &#34;OpenSSL Generated CA Root Certificate&#34;
</code></pre>

<h4 id="a-id-creazione-ca-server-creazione-ca-server-a"><a id="creazione-ca-server">Creazione CA server</a></h4>

<pre><code>OUTPUT_CERT=&lt;path_output_cert&gt;
CONF_CERT=&lt;path_file_cnf&gt;

### CA SERVER ###
openssl req \
-config $CONF_CERT/ca_cert.cnf \
-extensions v3_ca \ 
-x509 \
-days 1825 -sha512 \
-nodes -newkey rsa:4096 \
-subj &#34;/C=IT/ST=Italia/CN=GlobalSign Test RootCA&#34; \
-keyout $OUTPUT_CERT/ca_server.key \
-out $OUTPUT_CERT/ca_server.crt
</code></pre>
<ul><li><strong>openssl req</strong> : crea un csr</li>
<li><strong>-config $CONF<em>CERT/ca</em>cert.cnf</strong> : path del file di configurazione</li>
<li><strong>-extensions v3_ca</strong> : sezione del file da prendere come riferimento (in questo caso per le sole extensions X.509)</li>
<li><strong>-x509</strong> : fa in modo che l&#39;output sia direttamente un certificato piuttosto che un csr</li>
<li><strong>-days 1825 -sha512</strong> : fissa la scadenza a 5 anni e setta sha512 come algoritmo di digest.</li>
<li><strong>-nodes -newkey rsa:4096</strong> : crea una chiave rsa non cifrata a 4096 bit</li>
<li><strong>-subj “/C=IT/ST=Roma”/CN=GlobalSign Test RootCA”</strong> : distinguished_name passato da cli</li>
<li><strong>-keyout $OUTPUT<em>CERT/ca</em>server.key</strong> : path di destinazione della chiave</li>
<li><strong>-out $OUTPUT<em>CERT/ca</em>server.crt</strong> : path di destinazione del certificato CA.</li></ul>

<h3 id="a-id-creazione-certificato-server-creazione-certificato-server-a"><a id="creazione-certificato-server">Creazione certificato server</a></h3>

<p>Verrà generato prima il csr (certificate signing request) che sarà firmato dalla CA per generare il certificato effettivo.</p>

<h4 id="a-id-configurazione-csr-server-configurazione-csr-server-a"><a id="configurazione-csr-server">Configurazione csr server</a></h4>

<p>Il file di configurazione per il csr sarà <strong>server_req.cnf</strong>, diviso in 3 sezioni.</p>

<p>La prima, <strong>req</strong>, è quella usata da <strong>openssl req</strong> (in assenza della quale verrà sollevata un&#39;eccezione) e contiene solo il rimando alle extensions X.509 da usare.</p>

<p>Come prima, le extensions X.509 (<strong>v3<em>server</em>req</strong>) definiscono i giusti attributi per questo tipo di certificato.</p>

<p><strong>N.B.</strong>
L&#39;ultima sezione è quella degli <strong>alternatives_name</strong>, la cui assenza non comporta problemi nella creazione del certificato ma potrebbe far fallire i check effettuati da alcuni prodotti, ad es. i webserver che pur riconoscendo nel <strong>Common Name</strong> il nome del servizio (es. la url) scatenano ugualmente l&#39;eccezione <strong>BAD<em>CERT</em>DOMAIN</strong></p>

<pre><code>##################
# server_req.cnf #
##################
[ req ]
req_extensions = v3_server_req

[ v3_server_req ]
basicConstraints        = CA:FALSE
subjectKeyIdentifier    = hash
keyUsage                = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage        = serverAuth
nsCertType              = server
nsComment               = &#34;OpenSSL Generated Server Request Certificate&#34;
subjectAltName          = @alt_names

[ alt_names ]
DNS.1 = server.web.local
</code></pre>

<h4 id="a-id-creazione-csr-server-creazione-csr-server-a"><a id="creazione-csr-server">Creazione csr server</a></h4>

<p>La sintassi è sostanzialmente uguale a quanto visto per la CA con la sola eccezione del parametro <strong>-x509</strong> (che creava un crt invece che un csr). Il csr creerà il certificato e la coppia di chiavi pubblica e privata.</p>

<pre><code>OUTPUT_CERT=&lt;path_output_cert&gt;
CONF_CERT=&lt;path_file_cnf&gt;

### CSR Server ###
openssl req \
-config $CONF_CERT/server_req.cnf \
-nodes -newkey rsa:2048 \
-subj &#34;/C=IT/ST=Italia/L=Roma/O=My Company Ltd/OU=Divisione IT web/CN=server.web.local/emailAddress=it@web.local&#34; \
-keyout $OUTPUT_CERT/server.key \
-out $OUTPUT_CERT/server.csr
</code></pre>
<ul><li><strong>openssl req</strong> : crea un csr</li>
<li><strong>-config $CONF<em>CERT/server</em>req.cnf</strong> : path del file di configurazione</li>
<li><strong>-nodes -newkey rsa:2048</strong> : crea una chiave rsa non cifrata a 2048 bit</li>
<li><strong>-subj “/C=IT/ST=Italia/L=Roma/O=My Company Ltd/OU=Divisione IT web/CN=server.web.local/emailAddress=it@web.local”</strong> : distinguished_name passato da cli</li>
<li><strong>-keyout $OUTPUT_CERT/server.key</strong> : path di destinazione della chiave</li>
<li><strong>-out $OUTPUT_CERT/server.csr</strong> : path di destinazione del csr</li></ul>

<h4 id="a-id-configurazione-crt-server-configurazione-crt-server-a"><a id="configurazione-crt-server">Configurazione crt server</a></h4>

<p>A differenza di prima, non ho un file di configurazioni vero e proprio. Non usando il file generico <strong>openssl.cnf</strong>, l&#39;unico file che si può invocare dal comando <code>openssl x509</code> è un elenco di attributi che il certificato dovrà possedere e questo ci è sufficiente per creare certificati client o server.</p>

<p>Il file di configurazione per il crt sarà <strong>server_cert.cnf</strong>.</p>

<pre><code>###################
# server_cert.cnf #
###################
basicConstraints        = CA:FALSE
subjectKeyIdentifier    = hash
keyUsage                = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage        = serverAuth
nsCertType              = server
nsComment               = &#34;OpenSSL Generated Server Certificate&#34;
subjectAltName          = @alt_names

[ alt_names ]
DNS.1 = server.web.local
</code></pre>

<h4 id="a-id-creazione-crt-server-creazione-crt-server-a"><a id="creazione-crt-server">Creazione crt server</a></h4>

<p>Di fatto, le uniche operazione che compio sono quelle di firmare con la CA il certificato, di fissare una scadenza e l&#39;algoritmo di digest (altrimenti verrà applicato un default). Il resto, lo eredito dal csr (potrei sovrascrivere alcune cose come il <strong>subj</strong>) o dalle estensioni presenti nel file.</p>

<pre><code>OUTPUT_CERT=&lt;path_output_cert&gt;
CONF_CERT=&lt;path_file_cnf&gt;

### CRT Server ###
openssl x509 \
-extfile $CONF_CERT/server_cert.cnf \
-req -in $OUTPUT_CERT/server.csr \
-days 365 -sha384 \
-CA $OUTPUT_CERT/ca_server.crt \
-CAkey $OUTPUT_CERT/ca_server.key \
-CAcreateserial \
-out $OUTPUT_CERT/server.crt
</code></pre>
<ul><li><strong>openssl x509</strong> : crea un certificato x509</li>
<li><strong>-extfile $CONF<em>CERT/server</em>cert.cnf</strong> : path del file di estensioni</li>
<li><strong>-req</strong> : acquisizione del file csr da far firmare</li>
<li><strong>-CA $OUTPUT<em>CERT/ca</em>server.crt</strong> : acquisizione del certificato della CA</li>
<li><strong>-CAkey $OUTPUT<em>CERT/ca</em>server.key</strong> : acquisizione della chiave privata della CA che firmerà il certificato</li>
<li><strong>-CAcreateserial</strong> : crea un seriale randomico per il certificato da creare. Normalmente una CA crea dei seriali progressivi memorizzandoli in un database interno.</li>
<li><strong>-days 365 -sha384</strong> : fissa ad un anno la scadenza del certificato e usa sha384 come algoritmo di digest</li>
<li><strong>-out $OUTPUT_CERT/server.crt</strong> : path di destinazione del certificato server.</li></ul>

<h3 id="a-id-creazione-ca-certificati-client-creazione-della-ca-per-i-certificati-client-a"><a id="creazione-ca-certificati-client">Creazione della CA per i certificati client</a></h3>

<p>È praticamente la stessa cosa. La differenza è solo formale visto che, tecnicamente, i comandi che andrò ad eseguire saranno praticamente gli stessi (l&#39;unica differenza di rilievo riguarderà i certificati client a cui corrisponderanno estensioni X.509 leggermente differenti)</p>

<h4 id="a-id-configurazione-ca-client-configurazione-della-ca-client-a"><a id="configurazione-ca-client">Configurazione della CA client</a></h4>

<p>È lo stesso file visto <a href="#configurazione-ca-server" rel="nofollow">prima</a>.</p>

<h4 id="a-id-creazione-ca-client-creazione-della-ca-client-a"><a id="creazione-ca-client">Creazione della CA client</a></h4>

<pre><code>OUTPUT_CERT=&lt;path_output_cert&gt;
CONF_CERT=&lt;path_file_cnf&gt;

### CA Client ###
openssl req \
-config $CONF_CERT/ca_cert.cnf \
-extensions v3_ca \ 
-x509 \
-days 1825 -sha512 \
-nodes -newkey rsa:4096 \
-subj &#34;/C=IT/ST=Italia/CN=Digicert Test RootCA&#34; \
-keyout $OUTPUT_CERT/ca_client.key \
-out $OUTPUT_CERT/ca_client.crt
</code></pre>

<p>C&#39;è poco da dire. L&#39;unica variazione degna di nota è il subj, per il resto è identico alla <a href="#creazione-ca-server" rel="nofollow">creazione della ca server</a>.</p>

<h3 id="a-id-creazione-certificato-client-creazione-certificato-client-a"><a id="creazione-certificato-client">Creazione certificato client</a></h3>

<p>Come fatto per il server, verrà generato prima il csr che sarà firmato dalla CA per generare il certificato effettivo.</p>

<h4 id="a-id-configurazione-csr-client-configurazione-csr-client-a"><a id="configurazione-csr-client">Configurazione csr client</a></h4>

<p>Come per i <a href="#configurazione-csr-server" rel="nofollow">certificati server</a>, <strong>extendedKeyUsage</strong> e <strong>nsCertType</strong> stabiliscono come viene battezzato il certificato.</p>

<pre><code>##################
# client_req.cnf #
##################
[ req ]
req_extensions = v3_client_req

[ v3_client_req ]
basicConstraints        = CA:FALSE
subjectKeyIdentifier    = hash
keyUsage                = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage        = clientAuth
nsCertType              = client
nsComment               = &#34;OpenSSL Generated Client Request Certificate&#34;
</code></pre>

<h4 id="a-id-creazione-csr-client-creazione-csr-client-a"><a id="creazione-csr-client">Creazione csr client</a></h4>

<p>Nel csr, viene creata la chiave, rsa a 2048 bit, e viene settato il subj.</p>

<pre><code>OUTPUT_CERT=&lt;path_output_cert&gt;
CONF_CERT=&lt;path_file_cnf&gt;

### CSR Client ###
openssl req \
-config $CONF_CERT/client_req.cnf \
-nodes -newkey rsa:2048 \
-subj &#34;/C=IT/ST=Italia/L=Roma/O=Another Company Ltd/OU=User WebApp/CN=John Doe/emailAddress=john.doe@unknown.local&#34; \
-keyout $OUTPUT_CERT/johndoe.key \
-out $OUTPUT_CERT/johndoe.csr
</code></pre>

<h4 id="a-id-configurazione-crt-client-configurazione-crt-client-a"><a id="configurazione-crt-client">Configurazione crt client</a></h4>

<p>Il file di configurazione raccoglie solo le estensioni con cui il certificato viene istanziato</p>

<pre><code>###################
# client_cert.cnf #
###################
basicConstraints        = CA:FALSE
subjectKeyIdentifier    = hash
keyUsage                = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage        = clientAuth
nsCertType              = client
nsComment               = &#34;OpenSSL Generated Client Certificate&#34;
</code></pre>

<h4 id="a-id-creazione-crt-client-creazione-crt-client-a"><a id="creazione-crt-client">Creazione crt client</a></h4>

<p>Come per il certificato server, il certificato client verrà creato in seguito alla firma del csr dalla CA e aver settato scadenza a algoritmo di digest (altrimenti verrà applicato un default). Essendo un certificato client, viene mostrato anche come costruire un p12, utile nei casi in cui l&#39;autenticazione  del client viene fatta dal browser.</p>

<pre><code>OUTPUT_CERT=&lt;path_output_cert&gt;
CONF_CERT=&lt;path_file_cnf&gt;

### CRT Client ###
openssl x509 \
-extfile $CONF_CERT/client_cert.cnf \
-req -in $OUTPUT_CERT/johndoe.csr \
-days 365 -sha384 \
-CA $OUTPUT_CERT/ca_client.crt \
-CAkey $OUTPUT_CERT/ca_client.key \
-CAcreateserial \
-out $OUTPUT_CERT/johndoe.crt

### Creazione p12 ###
openssl pkcs12 -export -inkey $OUTPUT_CERT/johndoe.key -in $OUTPUT_CERT/johndoe.crt -out $OUTPUT_CERT/johndoe.p12
</code></pre>

<h3 id="a-id-riassunto-riassunto-a"><a id="riassunto">Riassunto</a></h3>

<p>Di seguito, la raccolta di quanto visto finora, allo scopo di rendere un colpo d&#39;occhio immediato e di dare una base per rendere scriptabile il processo che già così, tenendo presente i file di configurazione così composti e raccogliendo in uno script i comandi proposti, crea 2 CA, un certificato server e uno client (a meno dei subj di fantasia)</p>

<p><strong>File di configurazione:</strong></p>

<pre><code>###############
# ca_cert.cnf #
###############
[ v3_ca ]
basicConstraints        = critical,CA:true
keyUsage                = critical, cRLSign, keyCertSign
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid:always,issuer
nsCertType              = sslCA
nsComment               = &#34;OpenSSL Generated CA Root Certificate&#34;

##################
# server_req.cnf #
##################
[ req ]
req_extensions = v3_server_req
 
[ v3_server_req ]
basicConstraints        = CA:FALSE
subjectKeyIdentifier    = hash
keyUsage                = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage        = serverAuth
nsCertType              = server
nsComment               = &#34;OpenSSL Generated Server Request Certificate&#34;
subjectAltName          = @alt_names

[ alt_names ]
DNS.1 = server.web.local

###################
# server_cert.cnf #
###################
basicConstraints        = CA:FALSE
subjectKeyIdentifier    = hash
keyUsage                = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage        = serverAuth
nsCertType              = server
nsComment               = &#34;OpenSSL Generated Server Certificate&#34;
subjectAltName          = @alt_names
 
[ alt_names ]
DNS.1 = server.web.local

##################
# client_req.cnf #
##################
[ req ]
req_extensions = v3_client_req
 
[ v3_client_req ]
basicConstraints        = CA:FALSE
subjectKeyIdentifier    = hash
keyUsage                = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage        = clientAuth
nsCertType              = client
nsComment               = &#34;OpenSSL Generated Client Request Certificate&#34;

###################
# client_cert.cnf #
###################
basicConstraints        = CA:FALSE
subjectKeyIdentifier    = hash
keyUsage                = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage        = clientAuth
nsCertType              = client
nsComment               = &#34;OpenSSL Generated Client Certificate&#34;
</code></pre>

<p><strong>Script di creazione certificati:</strong></p>

<pre><code>OUTPUT_CERT=&lt;path_output_cert&gt;
CONF_CERT=&lt;path_file_cnf&gt;

#############
# CA SERVER #
#############
openssl req -config $CONF_CERT/ca_cert.cnf -extensions v3_ca -x509 -days 1825 -sha512 -nodes -newkey rsa:4096 -subj &#34;/C=IT/ST=Italia/CN=GlobalSign Test RootCA&#34; -keyout $OUTPUT_CERT/ca_server.key -out $OUTPUT_CERT/ca_server.crt

##########
# Server #
##########
# csr
openssl req -config $CONF_CERT/server_req.cnf -nodes -newkey rsa:2048 -subj &#34;/C=IT/ST=Italia/L=Roma/O=My Company Ltd/OU=Divisione IT web/CN=server.web.local/emailAddress=it@web.local&#34; -keyout $OUTPUT_CERT/server.key -out $OUTPUT_CERT/server.csr
# crt
openssl x509 -extfile $CONF_CERT/server_cert.cnf 
-req -in $OUTPUT_CERT/server.csr -days 365 -sha384 -CA $OUTPUT_CERT/ca_server.crt -CAkey $OUTPUT_CERT/ca_server.key -CAcreateserial -out $OUTPUT_CERT/server.crt

#############
# CA Client #
#############
openssl req -config $CONF_CERT/ca_cert.cnf -extensions v3_ca -x509 -days 1825 -sha512 -nodes -newkey rsa:4096 -subj &#34;/C=IT/ST=Italia/CN=Digicert Test RootCA&#34; -keyout $OUTPUT_CERT/ca_client.key -out $OUTPUT_CERT/ca_client.crt

##########
# Client #
##########
# csr
openssl req -config $CONF_CERT/client_req.cnf -nodes -newkey rsa:2048 -subj &#34;/C=IT/ST=Italia/L=Roma/O=Another Company Ltd/OU=User WebApp/CN=John Doe/emailAddress=john.doe@unknown.local&#34; -keyout $OUTPUT_CERT/johndoe.key -out $OUTPUT_CERT/johndoe.csr
# crt 
openssl x509 -extfile $CONF_CERT/client_cert.cnf -req -in $OUTPUT_CERT/johndoe.csr -days 365 -sha384 -CA $OUTPUT_CERT/ca_client.crt -CAkey $OUTPUT_CERT/ca_client.key -CAcreateserial -out $OUTPUT_CERT/johndoe.crt
# p12
openssl pkcs12 -export -inkey $OUTPUT_CERT/johndoe.key -in $OUTPUT_CERT/johndoe.crt -out $OUTPUT_CERT/johndoe.p12
</code></pre>

<h2 id="a-id-creazione-ca-intermediate-certificati-client-server-caso-2-creazione-di-ca-intermediate-ca-e-dei-certificati-client-e-server-a"><a id="creazione-ca-intermediate-certificati-client-server">Caso 2. Creazione di CA, Intermediate CA e dei certificati client e server</a></h2>

<p>Gli esempi che seguiranno saranno presentati sullo stile del riassunto perché fondamentalmente molto simili al caso 1. Si aggiunge un punto di intermediazione in più dato dal fatto che la CA delega ad una CA “subalterna” l&#39;incarico di verificare le paternità di coloro i quali richiedono certificati.</p>

<p>Quindi avremo:</p>
<ul><li>una root CA, certificato self-signed</li>
<li>un&#39;Intermediate CA, validata dalla root CA (csr + crt firmato dalla root CA)
<em>n</em> certificati client/server validati dall&#39;Intermediate CA (csr + crt firmato dall&#39;Intermediate CA)</li></ul>

<p><strong>Creazione certificati</strong></p>

<pre><code>OUTPUT_CERT=$HOME/stunnel/build-cert
CONF_CERT=$HOME/stunnel/conf.d

###########
# Root CA #
###########
openssl req -config $CONF_CERT/ca_cert.cnf -extensions v3_ca -x509 -days 1825 -sha512 -nodes -newkey rsa:4096 -subj &#34;/C=IT/ST=Italia/CN=GlobalSign Test RootCA&#34; -keyout $OUTPUT_CERT/rootca.key -out $OUTPUT_CERT/rootca.crt

###################
# INTERMEDIATE CA #
###################
# csr
openssl req -config $CONF_CERT/intermediate_req.cnf -nodes -newkey rsa:2048 -subj &#34;/C=IT/ST=Italia/CN=GlobalSign Test IntermediateCA&#34; -keyout $OUTPUT_CERT/intermediate.key -out $OUTPUT_CERT/intermediate.csr
# crt
openssl x509 -extfile $CONF_CERT/intermediate_cert.cnf -req -in $OUTPUT_CERT/intermediate.csr -days 365 -sha384 -CA $OUTPUT_CERT/rootca.crt -CAkey $OUTPUT_CERT/rootca.key -CAcreateserial -out $OUTPUT_CERT/intermediate.crt

##########
# Server #
##########
# csr
openssl req -config $CONF_CERT/server_req.cnf -nodes -newkey rsa:2048 -subj &#34;/C=IT/ST=Italia/L=Roma/O=My Company Ltd/OU=Divisione IT web/CN=server.web.local/emailAddress=it@web.local&#34; -keyout $OUTPUT_CERT/server.key -out $OUTPUT_CERT/server.csr
# crt
openssl x509 -extfile $CONF_CERT/server_cert.cnf 
-req -in $OUTPUT_CERT/server.csr -CA $OUTPUT_CERT/intermediate.crt -CAkey $OUTPUT_CERT/intermediate.key -CAcreateserial -days 365 -sha384 -out $OUTPUT_CERT/server.crt

##########
# Client #
##########
# csr
openssl req -config $CONF_CERT/client_req.cnf -nodes -newkey rsa:2048 -subj &#34;/C=IT/ST=Italia/L=Roma/O=Another Company Ltd/OU=User WebApp/CN=John Doe/emailAddress=john.doe@unknown.local&#34; -keyout $OUTPUT_CERT/johndoe.key -out $OUTPUT_CERT/johndoe.csr
# crt
openssl x509 -extfile $CONF_CERT/client_cert.cnf -req -in $OUTPUT_CERT/johndoe.csr -days 365 -sha384 -CA $OUTPUT_CERT/intermediate.crt -CAkey $OUTPUT_CERT/intermediate.key -CAcreateserial -out $OUTPUT_CERT/johndoe.crt
# p12
openssl pkcs12 -export -inkey $OUTPUT_CERT/johndoe.key -in $OUTPUT_CERT/legs.crt -out $OUTPUT_CERT/johndoe.p12
</code></pre>

<p><a href="/aytin/tag:DigitalCertificate" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">DigitalCertificate</span></a> <a href="/aytin/tag:x509" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">x509</span></a> <a href="/aytin/tag:csr" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">csr</span></a> <a href="/aytin/tag:openssl" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">openssl</span></a> <a href="/aytin/tag:ca" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">ca</span></a> <a href="/aytin/tag:cryptography" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">cryptography</span></a> <a href="/aytin/tag:AsymmetricEncryption" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">AsymmetricEncryption</span></a> <a href="/aytin/tag:SymmetricEncryption" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">SymmetricEncryption</span></a> <a href="/aytin/tag:DigitalSignature" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">DigitalSignature</span></a></p>
]]></content:encoded>
      <guid>https://noblogo.org/aytin/come-creare-un-certificato-digitale</guid>
      <pubDate>Sun, 05 Mar 2023 21:38:23 +0000</pubDate>
    </item>
    <item>
      <title>Stunnel: Cos&#39;è e come si configura</title>
      <link>https://noblogo.org/aytin/stunnel-cose-e-come-si-configura</link>
      <description>&lt;![CDATA[(pubblicato il 7 gennaio 2023)&#xA;Stunnel&#xA;smalliFonte: a href=&#34;https://www.stunnel.org&#34;stunnel.org/a/i/small&#xA;&#xA;Introduzione&#xA;&#xA;Da documentazione, Stunnel agisce come un proxy per aggiungere crittografia TLS a server e/o a client già esistenti.&#xA;&#xA;Risulta quindi molto comodo quando si vuole aggiungere, attraverso un tunnel TLS, quella crittografia che manca alle nostre applicazioni per rendere le comunicazioni più sicure.&#xA;!--more--&#xA;Come in ssh, è possibile “tunnellizzare” solo connessioni TCP.&#xA;&#xA;Di stunnel esaminerò in particolare la fase di autenticazione attraverso certificato o PSK.&#xA;&#xA;Gli scenari possibili, in base alle combinazioni, sono tre:&#xA;&#xA;client tls – server in chiaro&#xA;client in chiaro – server tls&#xA;client e server in chiaro&#xA;&#xA;Nel primo caso, occorre configurare stunnel in server mode, definendo un receiver ssl, a cui il client potrà connettersi.&#xA;stunel server&#xA;smalliScenario 1: stunnel server/i/small&#xA;&#xA;Nel secondo caso, occorre configurare stunnel in client mode, definendo un sender ssl che il client userà per connettersi al server&#xA;stunnel client&#xA;smalliScenario 2: stunnel client/i/small&#xA;&#xA;Nel terzo caso, occorre configurare uno stunnel client e uno server&#xA;stunnel client e server&#xA;smalliScenario 3: stunnel client e server/i/small&#xA;&#xA;Come ulteriore evoluzione per quel che riguarda le tratte in chiaro (e non solo) di client-stunnel client, stunnel client-stunnel server, stunnel server-server, sarebbe buona norma limitarne l’accesso con un firewall.&#xA;stunnel client e server e firewall&#xA;smalliStunnel e firewall/i/small&#xA;&#xA;Se invece stunnel fosse locale (installato direttamente sul client e/o sul server), la richiesta del client o il listener del servizio, avverrebbero sull’interfaccia di loopback.&#xA;Altrimenti, in assenza di firewall, sarebbe consigliabile accertare che la rete di collegamento fra i proxy stunnel e i rispettivi client/server, sia almeno di tipo “trusted”.&#xA;&#xA;Stunnel consente un certo tuning sulla parte tls, potendo discriminare fra le cipher suites da abilitare, specificare comandi o configurazioni specifiche per openssl.&#xA;Quando si deve incapsulare una connessione in chiaro in un tunnel ssl bisogna tenere presente gli scenari menzionati prima.&#xA;Convenzioni&#xA;&#xA;Per non perdere generalità, negli esempi che seguiranno, immaginiamo che stunnel non sia locale (anche se spesso lo è).&#xA;   se stunnel in client mode è locale ⇒ accept avverrà sull’interfaccia di loopback, da dove avviene effettivamente la richiesta&#xA;   se stunnel in server mode è locale ⇒ connect avverrà sull’interfaccia di loopback, dove viene erogato effettivamente il servizio&#xA;Non faremo assunzioni sulla creazione dei certificati. Possono anche essere self-signed.&#xA;&#xA;Configurazione stunnel&#xA;&#xA;Verranno mostrate le configurazioni relative agli scenari mostrati nel modo più semplice possibile.&#xA;&#xA;accept: host e porta che ricevono la richiesta&#xA;connect: host e porta a cui girare l’accept&#xA;cert: normalmente conterrebbe il certificato pubblico che stunnel espone per autenticarsi (la parte privata viene specificata con key). Può però anche essere costituito da tutta i certificati della chain fino alla root CA, in formato pem o p12, iniziando dalla chiave privata, proseguendo con la chiave pubblica, fino a tutte le CA della chain.&#xA;key: chiave privata del certificato&#xA;client: stabilisce se stunnel funzioni in server mode oppure no&#xA;&#xA;Primo scenario&#xA;&#xA;Nel primo caso, una configurazione minimale lato server, ha bisogno:&#xA;&#xA;host e porta dello stunnel server che riceve il traffico cifrato&#xA;host e porta del server su cui girare il traffico in chiaro&#xA;certificato pubblico e chiave privata necessari per la cifratura&#xA;&#xA;Stunnel Server:&#xA;[myService]&#xA;accept = ipstunnelserver:portsts&#xA;connect = ipserver:portserver&#xA;cert = /servercrt.pembr&#xA;key = /serverkey.pem&#xA;Secondo scenario&#xA;&#xA;Nel secondo caso, una configurazione minimale lato client, ha bisogno:&#xA;&#xA;host e porta dello stunnel client da cui partirà la richiesta&#xA;host e porta del server da cui stunnel client avvierà la sessione tls&#xA;&#xA;Stunnel Client:&#xA;[myService]&#xA;client = yes&#xA;accept = ipstunnelclient:portstc&#xA;connect = ipserver:portserver&#xA;Terzo Scenario&#xA;&#xA;Il terzo caso, è una fusione dei primi due. Sono gli stunnel a gestire il grosso della comunicazione. E nei casi in cui lo stunnel viene installato sulle macchine di servizio, client e server reali usano solo l’interfaccia di loopback.&#xA;&#xA;Una configurazione minimale per il client ha bisogno di:&#xA;&#xA;host e porta dello stunnel client da cui partirà la richiesta&#xA;host e porta del server da cui stunnel client avvierà la sessione tls&#xA;&#xA;Stunnel Client:&#xA;[myService]&#xA;client = yes&#xA;accept = ipstunnelclient:portstc&#xA;connect = ipstunnelserver:portserver&#xA;Una configurazione minimale per il server ha bisogno:&#xA;&#xA;host e porta dello stunnel server che riceve il traffico cifrato&#xA;host e porta del server su cui girare il traffico in chiaro&#xA;    certificato pubblicato e chiave privata necessari per la cifratura&#xA;&#xA;Stunnel Server:&#xA;[myService]&#xA;accept = ipstunnelserver:portsts&#xA;connect = ipserver:portserver&#xA;cert = /servercrt.pem&#xA;key = /serverkey.pem&#xA;smallPiccola nota: se client e/o server dovessero supportare chiavi PSK, si potrebbe usare PSK in luogo dei certificati. Ci ritornerò più avanti./small&#xA;&#xA;Autenticazione dei client&#xA;&#xA;Le impostazioni viste finora, mostrano come incapsulare un traffico in chiaro all’interno di un tunnel tls e si basano sulla sola autenticazione lato server.&#xA;&#xA;Per migliorare la configurazione possiamo ricorrere alla mutua autenticazione facendo in modo che anche i client si autentichino.&#xA;&#xA;Una mutua autenticazione, con tutte le verifiche del caso, aumenta il grado di sicurezza comunicazione TLS ed è un efficace deterrente contro eventuali attacchi MITM. L’autenticazione sul client si può ottenere sempre con i certificati oppure, più semplicemente, con chiavi PSK.&#xA;&#xA;Autenticazione dei client con certificato&#xA;&#xA;Ad una configurazione minimale per una mutua autenticazione, lato server, si richiede che i client esibiscano il certificato e i client (che sia stunnel in client mode, un browser o qualunque altra cosa) dovranno essere in grado di esibire i certificati nel momento in cui il server li richiederà.&#xA;&#xA;Su stunnel server, alle configurazioni viste in precedenza, si aggiunge requireCert impostato a yes, che richiede ai client l’esibizione di un certificato. Se, durante l’handshake TLS, il client non esibisce un certificato, l’handshake fallisce e la connessione viene rifiutata.&#xA;&#xA;Stunnel Server:&#xA;[myService]&#xA;…&#xA;requireCert = yes&#xA;…&#xA;È sufficiente questo se i client possono manipolare i propri certificati. Altrimenti, se c’è uno stunnel client che intermedia le richieste dei client effettivi, si deve aggiungere alla sua configurazione la chiave e il certificato del client&#xA;&#xA;Stunnel Client:&#xA;[myService]&#xA;…&#xA;cert = /servercrt.pem&#xA;key = /serverkey.pem&#xA;…&#xA;Autenticazione dei client con PSK&#xA;&#xA;Per l’autenticazione PSK bisogna disporre della lista di utenti abilitati con relative chiavi esadecimali di almeno 16 bytes (ossia almeno 32 caratteri esadecimali visto che con un byte si rappresentano due esadecimali).&#xA;&#xA;La creazione di una chiave esadecimale può essere fatta velocemente con openssl.&#xA;Supponiamo di creare due utenze per Frodo e Gandalf con chiavi da 20 e 42 bytes.&#xA;echo -e &#34;frodo:&#34;$(openssl rand -hex 20)   frodopsk.txt&#xA;echo -e &#34;gandalf:&#34;$(openssl rand -hex 42)   gandalfpsk.txt&#xA;Tutte le identità dovranno confluire nel file che, nella configurazione stunnel, sarà indicato da PSKsecrets. Ad es:&#xA;cat frodopsk.txt gandalfpsk.txt   psksecrets.txt&#xA;…&#xA;frodo:84196825fab3389624dcc83eb61189e5b21099febrgandalf:5daf128419855993a2d95ba0a337c51b3f373df88e594441f2979eba6461f25676f1f825b0c76df392f2&#xA;…&#xA;E, come detto, nella configurazione dello stunnel server dovrò indicare il file delle identità:&#xA;&#xA;Stunnel Server&#xA;[myServer]&#xA;accept = ipstunnelserver:portsts&#xA;connect = ipserver:portserver&#xA;PSKsecrets = pathidentityfile/psksecrets.txt&#xA;Se il client supporta PSK, dovrà fornire identità e password. Ad es. facendo un test con openssl:&#xA;openssl sclient -port porta -pskidentity frodo -psk 84196825fab3389624dcc83eb61189e5b21099fe -tls12 -connect host&#xA;Altrimenti si configura stunnel client indicando sia l’identità, sia il file individuate da PSKsecrets.&#xA;&#xA;Stunnel Client&#xA;[myClient]&#xA;client = yes&#xA;accept = ipstunnelclient:portstc&#xA;connect = ipstunnelserver:portserver&#xA;PSKidentity = frodo&#xA;PSKsecrets = pathidentityfile/psksecrets.txt&#xA;In alternativa, per non far viaggiare il file delle identità fra tutti i client, possiamo sfruttare a nostro vantaggio il default delle identità.&#xA;&#xA;In assenza della direttiva PSKidentity, viene assunta come identità di default la prima riga del file indicato da PSKSecrets. Basta quindi creare un file con la sola identità che mi occorre, ad es. frodopsk.txt&#xA;frodo:84196825fab3389624dcc83eb61189e5b21099fe&#xA;e indicare quello in PSKSecrets. La configurazione che segue è equivalente alla precedente.&#xA;&#xA;Stunnel Client&#xA;[myClient]&#xA;client = yes&#xA;accept = ipstunnelclient:portstc&#xA;connect = ipstunnelserver:portserver&#xA;PSKsecrets = pathidentityfile/frodopsk.txt&#xA;Ulteriori considerazioni di sicurezza&#xA;Autenticazione con certificato&#xA;&#xA;La richiesta di autenticazione dei client mediante certificato è una buona misura preventiva ma può essere resa migliore.&#xA;&#xA;Nelle configurazioni viste finora, stunnel server autorizza l’accesso al servizio solo se il client si presenta con un certificato ma non c’è nulla che vincoli il certificato al servizio. In altre parole, un client in possesso di un qualunque certificato, può accedere alla risorsa.&#xA;&#xA;È possibile rafforzare il trusting fra le parti facendo in modo che stunnel autorizzi solo alcuni certificati invece che qualunque. Questo tipo di controllo può essere fatto sia su stunnel sia in client mode che in server mode.&#xA;&#xA;verifyChain = yes | no&#xA;checkEmail = email del certificato&#xA;checkHost = CN del certificato&#xA;checkIP = IP del peer che presenta il certificato&#xA;verifyPeer = yes | no&#xA;CApath =  path CA &#xA;CAfile =  file pem contenente la fullchain del certificato presentato a stunnel &#xA;&#xA;verifyChain&#xA;Impone che si possa verificare la fullchain del certificato presentato. Se uno degli issuer non viene trovato, la connessione fallisce. La fullchain viene indicata attraverso CAfile o CApath.&#xA;&#xA;CAfile&#xA;È il file contenente le CA con cui stunnel valida i certificati che gli vengono presentati. Se stunnel è in server mode, sarà la fullchain relativa ai client. Altrimenti sarà la fullchain relativa al server.&#xA;&#xA;CApath&#xA;È il path in cui si trovano le CA con cui stunnel valida i certificati che gli vengono presentati. Se lo stunnel è in server mode, sarà la fullchain realtiva ai client. Altrimenti sarà la fullchain relativa al server.&#xA;&#xA;verifyPeer&#xA;Se con CAfile e CApath si verifica la fullchain dei certificati presentati a stunnel, con verifyPeer = yes si verifica che questi certificati siano presenti anche nel suo keystore (CAfile o CApath). Permette di “legare” a stunnel i certificati che dovrà validare.&#xA;&#xA;checkEmail, checkHost, checkIP&#xA;Come per verifyPeer, sono direttive che permettono di stringere il legame fra il certificato che viene presentato e stunnel.&#xA;Verifica che l’email, il CN o l’IP del certificato che viene presentato corrispondano a quelli presenti nella configurazione.&#xA;In una configurazione lato server posso avere molteplici occorrenze di questi due campi relative ad altrettanti certificati. Il caso in cui ad es. si debbano autenticare n client.&#xA;&#xA;Se i certificati sono self-signed, verifyChain perde di significato (coinciderebbe con verifyPeer). La possibilità di generare i certificati attraverso una CA, anche interna, renderebbe il processo più strutturato anche se più complesso (si dovrà trovare un modo per distribuire la CA, se interna). Si tratta di trovare il giusto compromesso fra ampiezza del servizio (quanti utenti coinvolge?) e complessità richiesta.&#xA;&#xA;Se si tratta di realizzare un canale sicuro fra due apparati per una comunicazione server-2-server, vanno bene anche i certificati self-signed o un’autenticazione PSK. Altrimenti, soprattutto nel caso in cui l’autenticazione dei client è una fase critica, conviene valutare l’uso di una CA e una validazione il più possibile restrittiva sui client e sul server.&#xA;&#xA;Autenticazione PSK&#xA;&#xA;A differenza dell’autenticazione con certificato, l’autenticazione con PSK non prevede enhancement di sicurezza ulteriori a meno di specificare opportuni algoritmi di cifratura (cfr. paragrafo successivo).&#xA;&#xA;Cifratura&#xA;&#xA;Come sempre succede, negli scenari di cifratura asimmetrica orientatia alla connessione, l’autenticazione, per quanto possa essere accurata, costituisce solo uno degli aspetti di cui tenere conto nella fase di messa in sicurezza del servizio.&#xA;&#xA;Avere un’autenticazione con certificato generato con tutti i crismi da una CA, è certamente una gran cosa ma mette in sicurezza solo l’aspetto dell’autenticazione, per l’appunto.&#xA;&#xA;l transito dei dati è affidato alle suite di cifratura supportati da openssl e, volendo lavorare di fino, bisognerebbe stringere anche su quelli.&#xA;&#xA;Altrimenti, paradossalmente, si avrà un’autenticazione robustissima ma con algoritmi colabrodo di cifratura dei dati che, ad attaccanti ben motivati, lascerebbero delle porte spalancate per sferrare attacchi MITM per intercettare direttamente i dati, piuttosto che provare il furto di identità.&#xA;&#xA;Ad ogni modo, il default, per ragioni di compatibilità legacy, dei parametri di cifratura (da settare eventualmente su client e sul server) è il seguente:&#xA;ciphers: HIGH:!aNULL:!SSLv2:!DH:!kDHEPSK&#xA;ciphersuites: TLSAES256GCMSHA384:TLSAES128GCMSHA256:TLSCHACHA20POLY1305SHA256&#xA;dove ciphers raggruppa le suite di cifratura relativi a TLS1.2 in giù e ciphersuites riguarda invece TLS1.3.&#xA;&#xA;Configurazione servizio&#xA;&#xA;Ulteriori restrizioni possono essere aggiunte a livelllo di sistema:&#xA;&#xA;regolando i bit di setuid e setgid se stunnel gira come root&#xA;delimitando l’esecuzione del processo in una gabbia chroot&#xA;disabilitando le regonetiation (se supportato dalla versione di openssl in uso) che mitiga in parte attacchi dos di tipo CPU-exhaustion&#xA;ecc..&#xA;&#xA;Caso reale: tunnelizzare Transmission.&#xA;&#xA;Transmission è un client bittorrent che può essere controllato da remoto via rpc attraverso un’interfaccia web o altri client.&#xA;&#xA;Transmission, nelle sue varie declinazioni, non incapsula il traffico rpc in una connessione tls.&#xA;&#xA;Se si vuole che il dato in transito sia cifrato e che la connessione sia autenticata, si può ricorrere:&#xA;&#xA;ad un tunnel ssh;&#xA;all’intermediazione di un webserver come apache o nginx ;&#xA;ad uno stunnel server&#xA;&#xA;Sugli ultimi due punti si può configurare una mutua autenticazione con certificato (complessa e articolata e, per tanti client, ha un impatto significativo). Scegliamo il 3° caso.&#xA;&#xA;Esposizione dell’interfaccia web&#xA;&#xA;Scenario: abbiamo una macchina (il nostro serverino di fiducia o un nas) con transmission a bordo, presumibilmente dietro un firewall che natta il suo indirizzo privato (192.168.70.15).&#xA;&#xA;Transmission Stunnel&#xA;smallEsposizione transmission attraverso stunnel server/small&#xA;&#xA;Obiettivo: Vogliamo poter raggiungere transmission e vogliamo che i client autentichino la loro connessione prima di accedere al servizio. In base allo scenario, sappiamo che dovremmo configurare un port forwarding dall’AP fino allo stunnel server. Il server transmission rimarrà confinato nella nostra rete locale.&#xA;&#xA;Utilizzo di un client esterno&#xA;&#xA;Supponiamo di utilizzare diversi client: il browser, transmission-remote-gtk o transmission-remote-gui (transgui), tremotesf (mobile).&#xA;&#xA;La prima cosa da fare è configurare un port forwarding sul nostro AP&#xA;&#xA;    Rulesub1/sub: 9091 ⇒ 192.168.70.10:9091&#xA;&#xA;Dopodichè, configureremo stunnel in server mode davanti a transmission&#xA;[transmission]&#xA;accept = 192.168.70.10:9091&#xA;connect = 192.168.70.15:9091&#xA;cert = path stunnel conf/ssl/server/certs/stunnelcrt.pem&#xA;key = path stunnel conf/ssl/server/private/stunnelkey.pem&#xA;requireCert = yes&#xA;verifyChain = yes&#xA;verifyPeer = yes&#xA;CApath = path stunnel conf/ssl/client/certs&#xA;Alcune note sulla configurazione:&#xA;&#xA;devo disporre di un certificato per stunnel&#xA;devo disporre dei certificati per i client che si connetteranno, le cui fullchain dovranno essere localizzate nel path indicato da CApath&#xA;&#xA;A questo punto basta che i web-client puntino all’host pubblico (151.25.77.205) sulla porta 9091 e il gioco è fatto.&#xA;&#xA;Per gli altri client come transmission-remote-gtk, transgui o tremotesf, per i quali al massimo posso chiedere di usare TLS (ma non sono riuscito ad usare un’autenticazione lato client), conviene installare stunnel in client mode :&#xA;[transmission-client]&#xA;accept = localhost:9091&#xA;connect = 151.25.77.205:9091&#xA;client = yes&#xA;cert = path stunnel conf/ssl/client/laptopcrt.pem&#xA;key = path stunnel conf/ssl/client/laptopkey.pem&#xA;verifyChain = yes&#xA;verifyPeer = yes&#xA;CAPath = path stunnel conf/ssl/server/certs&#xA;Il client (ad es. sul laptop) si connetterà su localhost:9091 e dovrà disporre anche del certificato del server e della sua fullchain affinchè i check su verifyChain e verifyPeer non falliscano.&#xA;&#xA;#ca #DigitalCertificate #AsymmetricEncryption #SymmetricEncryption #cryptography #fullchain #psk #ssh #ssl #stunnel #tls #x509 #openssl]]&gt;</description>
      <content:encoded><![CDATA[<p><strong><em>(pubblicato il 7 gennaio 2023)</em></strong>
<img src="https://pixelfed.uno/storage/m/_v2/489827599091373610/42a8ecf32-5a8865/eZP2oeutp2KD/bkO44BGcvLFyYYUeCuYKXlSQzvBqgKi0Uh4fYtYk.jpg" alt="Stunnel">
<small><i>Fonte: <a href="https://www.stunnel.org" rel="nofollow">stunnel.org</a></i></small></p>

<h2 id="introduzione">Introduzione</h2>

<p>Da documentazione, Stunnel agisce come un proxy per aggiungere crittografia TLS a server e/o a client già esistenti.</p>

<p>Risulta quindi molto comodo quando si vuole aggiungere, attraverso un tunnel TLS, quella crittografia che manca alle nostre applicazioni per rendere le comunicazioni più sicure.

Come in ssh, è possibile “tunnellizzare” solo connessioni TCP.</p>

<p>Di stunnel esaminerò in particolare la fase di autenticazione attraverso certificato o PSK.</p>

<p>Gli scenari possibili, in base alle combinazioni, sono tre:</p>
<ul><li>client tls – server in chiaro</li>
<li>client in chiaro – server tls</li>
<li>client e server in chiaro</li></ul>

<p>Nel primo caso, occorre configurare stunnel in server mode, definendo un receiver ssl, a cui il client potrà connettersi.
<img src="https://pixelfed.uno/storage/m/_v2/489827599091373610/42a8ecf32-5a8865/sQc861Pfxyfg/39Yo5lasr3nAebRZbrzqtoQOskqS62aHW9fwwyo0.png" alt="stunel server">
<small><i>Scenario 1: stunnel server</i></small></p>

<p>Nel secondo caso, occorre configurare stunnel in client mode, definendo un sender ssl che il client userà per connettersi al server
<img src="https://pixelfed.uno/storage/m/_v2/489827599091373610/42a8ecf32-5a8865/nz4fkq8opVoD/HcocxYf4VwB0UtihuwlQ6pOo2CKxDCTU4X5v1UNB.png" alt="stunnel client">
<small><i>Scenario 2: stunnel client</i></small></p>

<p>Nel terzo caso, occorre configurare uno stunnel client e uno server
<img src="https://pixelfed.uno/storage/m/_v2/489827599091373610/42a8ecf32-5a8865/zxIg9C1izJPj/DqgYaAezDIab01XBFMHiDRjUqEDWxNqxMJOGDSvs.png" alt="stunnel client e server">
<small><i>Scenario 3: stunnel client e server</i></small></p>

<p>Come ulteriore evoluzione per quel che riguarda le tratte in chiaro (e non solo) di client-stunnel client, stunnel client-stunnel server, stunnel server-server, sarebbe buona norma limitarne l’accesso con un firewall.
<img src="https://pixelfed.uno/storage/m/_v2/489827599091373610/42a8ecf32-5a8865/j9Qbwy74AkaE/igU7qV0XI8gVIASWfDufiBvWeAf0OgnQ24ZLwC42.png" alt="stunnel client e server e firewall">
<small><i>Stunnel e firewall</i></small></p>

<p>Se invece stunnel fosse locale (installato direttamente sul client e/o sul server), la richiesta del client o il listener del servizio, avverrebbero sull’interfaccia di loopback.
Altrimenti, in assenza di firewall, sarebbe consigliabile accertare che la rete di collegamento fra i proxy stunnel e i rispettivi client/server, sia almeno di tipo “trusted”.</p>

<p>Stunnel consente un certo tuning sulla parte tls, potendo discriminare fra le cipher suites da abilitare, specificare comandi o configurazioni specifiche per openssl.
Quando si deve incapsulare una connessione in chiaro in un tunnel ssl bisogna tenere presente gli scenari menzionati prima.</p>

<h2 id="convenzioni">Convenzioni</h2>
<ul><li>Per non perdere generalità, negli esempi che seguiranno, immaginiamo che stunnel non sia locale (anche se spesso lo è).
<ul><li>se stunnel in <em>client mode</em> è locale ⇒ <strong>accept</strong> avverrà sull’interfaccia di loopback, da dove avviene effettivamente la richiesta</li>
<li>se stunnel in <em>server mode</em> è locale ⇒ <strong>connect</strong> avverrà sull’interfaccia di loopback, dove viene erogato effettivamente il servizio</li></ul></li>
<li>Non faremo assunzioni sulla creazione dei certificati. Possono anche essere self-signed.</li></ul>

<h2 id="configurazione-stunnel">Configurazione stunnel</h2>

<p>Verranno mostrate le configurazioni relative agli scenari mostrati nel modo più semplice possibile.</p>
<ul><li><strong>accept</strong>: host e porta che ricevono la richiesta</li>
<li><strong>connect</strong>: host e porta a cui girare l’accept</li>
<li><strong>cert</strong>: normalmente conterrebbe il certificato pubblico che stunnel espone per autenticarsi (la parte privata viene specificata con <strong>key</strong>). Può però anche essere costituito da tutta i certificati della chain fino alla root CA, in formato pem o p12, iniziando dalla chiave privata, proseguendo con la chiave pubblica, fino a tutte le CA della chain.</li>
<li><strong>key</strong>: chiave privata del certificato</li>
<li><strong>client</strong>: stabilisce se stunnel funzioni in server mode oppure no</li></ul>

<h3 id="primo-scenario">Primo scenario</h3>

<p>Nel primo caso, una configurazione minimale lato server, ha bisogno:</p>
<ol><li>host e porta dello stunnel server che riceve il traffico cifrato</li>
<li>host e porta del server su cui girare il traffico in chiaro</li>
<li>certificato pubblico e chiave privata necessari per la cifratura</li></ol>

<p><strong>Stunnel Server:</strong></p>

<pre><code>[myService]
accept = ip_stunnel_server:port_sts
connect = ip_server:port_server
cert = /server_crt.pem&lt;br&gt;
key = /server_key.pem
</code></pre>

<h3 id="secondo-scenario">Secondo scenario</h3>

<p>Nel secondo caso, una configurazione minimale lato client, ha bisogno:</p>
<ol><li>host e porta dello stunnel client da cui partirà la richiesta</li>
<li>host e porta del server da cui stunnel client avvierà la sessione tls</li></ol>

<p><strong>Stunnel Client:</strong></p>

<pre><code>[myService]
client = yes
accept = ip_stunnel_client:port_stc
connect = ip_server:port_server
</code></pre>

<h3 id="terzo-scenario">Terzo Scenario</h3>

<p>Il terzo caso, è una fusione dei primi due. Sono gli stunnel a gestire il grosso della comunicazione. E nei casi in cui lo stunnel viene installato sulle macchine di servizio, client e server reali usano solo l’interfaccia di loopback.</p>

<p>Una configurazione minimale per il client ha bisogno di:</p>
<ol><li>host e porta dello stunnel client da cui partirà la richiesta</li>
<li>host e porta del server da cui stunnel client avvierà la sessione tls</li></ol>

<p><strong>Stunnel Client:</strong></p>

<pre><code>[myService]
client = yes
accept = ip_stunnel_client:port_stc
connect = ip_stunnel_server:port_server
</code></pre>

<p>Una configurazione minimale per il server ha bisogno:</p>
<ol><li>host e porta dello stunnel server che riceve il traffico cifrato</li>
<li>host e porta del server su cui girare il traffico in chiaro
certificato pubblicato e chiave privata necessari per la cifratura</li></ol>

<p><strong>Stunnel Server:</strong></p>

<pre><code>[myService]
accept = ip_stunnel_server:port_sts
connect = ip_server:port_server
cert = /server_crt.pem
key = /server_key.pem
</code></pre>

<p><small><strong>Piccola nota</strong>: se client e/o server dovessero supportare chiavi PSK, si potrebbe usare PSK in luogo dei certificati. Ci ritornerò più avanti.</small></p>

<h2 id="autenticazione-dei-client">Autenticazione dei client</h2>

<p>Le impostazioni viste finora, mostrano come incapsulare un traffico in chiaro all’interno di un tunnel tls e si basano sulla sola autenticazione lato server.</p>

<p>Per migliorare la configurazione possiamo ricorrere alla <a href="https://noblogo.org/aytin/mutua-autenticazione" rel="nofollow"><strong>mutua autenticazione</strong></a> facendo in modo che anche i client si autentichino.</p>

<p>Una mutua autenticazione, con tutte le verifiche del caso, aumenta il grado di sicurezza comunicazione TLS ed è un efficace deterrente contro eventuali attacchi MITM. L’autenticazione sul client si può ottenere sempre con i certificati oppure, più semplicemente, con chiavi PSK.</p>

<h3 id="autenticazione-dei-client-con-certificato">Autenticazione dei client con certificato</h3>

<p>Ad una configurazione minimale per una mutua autenticazione, lato server, si richiede che i client esibiscano il certificato e i client (che sia stunnel in <em>client mode</em>, un browser o qualunque altra cosa) dovranno essere in grado di esibire i certificati nel momento in cui il server li richiederà.</p>

<p>Su stunnel server, alle configurazioni viste in precedenza, si aggiunge <strong>requireCert</strong> impostato a <strong>yes</strong>, che richiede ai client l’esibizione di un certificato. Se, durante l’handshake TLS, il client non esibisce un certificato, l’handshake fallisce e la connessione viene rifiutata.</p>

<p><strong>Stunnel Server:</strong></p>

<pre><code>[myService]
…
requireCert = yes
…
</code></pre>

<p>È sufficiente questo se i client possono manipolare i propri certificati. Altrimenti, se c’è uno stunnel client che intermedia le richieste dei client effettivi, si deve aggiungere alla sua configurazione la chiave e il certificato del client</p>

<p><strong>Stunnel Client:</strong></p>

<pre><code>[myService]
…
cert = /server_crt.pem
key = /server_key.pem
…
</code></pre>

<h3 id="autenticazione-dei-client-con-psk">Autenticazione dei client con PSK</h3>

<p>Per l’autenticazione PSK bisogna disporre della lista di utenti abilitati con relative chiavi esadecimali di almeno 16 bytes (ossia almeno 32 caratteri esadecimali visto che con un byte si rappresentano due esadecimali).</p>

<p>La creazione di una chiave esadecimale può essere fatta velocemente con openssl.
Supponiamo di creare due utenze per <em>Frodo</em> e <em>Gandalf</em> con chiavi da 20 e 42 bytes.</p>

<pre><code class="language-bash">echo -e &#34;frodo:&#34;$(openssl rand -hex 20) &gt; frodo_psk.txt
echo -e &#34;gandalf:&#34;$(openssl rand -hex 42) &gt; gandalf_psk.txt
</code></pre>

<p>Tutte le identità dovranno confluire nel file che, nella configurazione stunnel, sarà indicato da <strong>PSKsecrets</strong>. Ad es:</p>

<pre><code class="language-bash">cat frodo_psk.txt gandalf_psk.txt &gt; psksecrets.txt
…
frodo:84196825fab3389624dcc83eb61189e5b21099fe&lt;br&gt;gandalf:5daf128419855993a2d95ba0a337c51b3f373df88e594441f2979eba6461f25676f1f825b0c76df392f2
…
</code></pre>

<p>E, come detto, nella configurazione dello stunnel server dovrò indicare il file delle identità:</p>

<p><strong>Stunnel Server</strong></p>

<pre><code>[myServer]
accept = ip_stunnel_server:port_sts
connect = ip_server:port_server
PSKsecrets = &lt;path_identity_file&gt;/psksecrets.txt
</code></pre>

<p>Se il client supporta PSK, dovrà fornire identità e password. Ad es. facendo un test con <code>openssl</code>:</p>

<pre><code class="language-bash">openssl s_client -port &lt;porta&gt; -psk_identity frodo -psk 84196825fab3389624dcc83eb61189e5b21099fe -tls1_2 -connect &lt;host&gt;
</code></pre>

<p>Altrimenti si configura stunnel client indicando sia l’identità, sia il file individuate da <strong>PSKsecrets</strong>.</p>

<p><strong>Stunnel Client</strong></p>

<pre><code>[myClient]
client = yes
accept = ip_stunnel_client:port_stc
connect = ip_stunnel_server:port_server
PSKidentity = frodo
PSKsecrets = &lt;path_identity_file&gt;/psksecrets.txt
</code></pre>

<p>In alternativa, per non far viaggiare il file delle identità fra tutti i client, possiamo sfruttare a nostro vantaggio il default delle identità.</p>

<p>In assenza della direttiva <strong>PSKidentity</strong>, viene assunta come identità di default la prima riga del file indicato da <strong>PSKSecrets</strong>. Basta quindi creare un file con la sola identità che mi occorre, ad es. <strong>frodo_psk.txt</strong></p>

<pre><code>frodo:84196825fab3389624dcc83eb61189e5b21099fe
</code></pre>

<p>e indicare quello in <strong>PSKSecrets</strong>. La configurazione che segue è equivalente alla precedente.</p>

<p><strong>Stunnel Client</strong></p>

<pre><code>[myClient]
client = yes
accept = ip_stunnel_client:port_stc
connect = ip_stunnel_server:port_server
PSKsecrets = &lt;path_identity_file&gt;/frodo_psk.txt
</code></pre>

<h2 id="ulteriori-considerazioni-di-sicurezza">Ulteriori considerazioni di sicurezza</h2>

<h3 id="autenticazione-con-certificato">Autenticazione con certificato</h3>

<p>La richiesta di autenticazione dei client mediante certificato è una buona misura preventiva ma può essere resa migliore.</p>

<p>Nelle configurazioni viste finora, stunnel server autorizza l’accesso al servizio solo se il client si presenta con un certificato ma non c’è nulla che vincoli il certificato al servizio. In altre parole, un client in possesso di un qualunque certificato, può accedere alla risorsa.</p>

<p>È possibile rafforzare il trusting fra le parti facendo in modo che stunnel autorizzi solo alcuni certificati invece che qualunque. Questo tipo di controllo può essere fatto sia su stunnel sia in <em>client mode</em> che in <em>server mode</em>.</p>
<ul><li><strong>verifyChain</strong> = yes | no</li>
<li><strong>checkEmail</strong> = email del certificato</li>
<li><strong>checkHost</strong> = CN del certificato</li>
<li><strong>checkIP</strong> = IP del peer che presenta il certificato</li>
<li><strong>verifyPeer</strong> = yes | no</li>
<li><strong>CApath</strong> = &lt; path CA &gt;</li>
<li><strong>CAfile</strong> = &lt; file <strong>pem</strong> contenente la fullchain del certificato presentato a stunnel &gt;</li></ul>

<p><strong>verifyChain</strong>
Impone che si possa verificare la fullchain del certificato presentato. Se uno degli issuer non viene trovato, la connessione fallisce. La fullchain viene indicata attraverso <strong>CAfile</strong> o <strong>CApath</strong>.</p>

<p><strong>CAfile</strong>
È il <strong>file</strong> contenente le CA con cui stunnel valida i certificati che gli vengono presentati. Se stunnel è in <em>server mode</em>, sarà la fullchain relativa ai client. Altrimenti sarà la fullchain relativa al server.</p>

<p><strong>CApath</strong>
È il path in cui si trovano le CA con cui stunnel valida i certificati che gli vengono presentati. Se lo stunnel è in server mode, sarà la fullchain realtiva ai client. Altrimenti sarà la fullchain relativa al server.</p>

<p><strong>verifyPeer</strong>
Se con <strong>CAfile</strong> e <strong>CApath</strong> si verifica la fullchain dei certificati presentati a stunnel, con <strong>verifyPeer = yes</strong> si verifica che questi certificati siano presenti anche nel suo keystore (<strong>CAfile</strong> o <strong>CApath</strong>). Permette di “legare” a stunnel i certificati che dovrà validare.</p>

<p><strong>checkEmail</strong>, <strong>checkHost</strong>, <strong>checkIP</strong>
Come per <strong>verifyPeer</strong>, sono direttive che permettono di stringere il legame fra il certificato che viene presentato e stunnel.
Verifica che l’email, il CN o l’IP del certificato che viene presentato corrispondano a quelli presenti nella configurazione.
In una configurazione lato server posso avere molteplici occorrenze di questi due campi relative ad altrettanti certificati. Il caso in cui ad es. si debbano autenticare n client.</p>

<p>Se i certificati sono self-signed, <strong>verifyChain</strong> perde di significato (coinciderebbe con <strong>verifyPeer</strong>). La possibilità di generare i certificati attraverso una CA, anche interna, renderebbe il processo più strutturato anche se più complesso (si dovrà trovare un modo per distribuire la CA, se interna). Si tratta di trovare il giusto compromesso fra ampiezza del servizio (quanti utenti coinvolge?) e complessità richiesta.</p>

<p>Se si tratta di realizzare un canale sicuro fra due apparati per una comunicazione server-2-server, vanno bene anche i certificati self-signed o un’autenticazione PSK. Altrimenti, soprattutto nel caso in cui l’autenticazione dei client è una fase critica, conviene valutare l’uso di una CA e una validazione il più possibile restrittiva sui client e sul server.</p>

<h3 id="autenticazione-psk">Autenticazione PSK</h3>

<p>A differenza dell’autenticazione con certificato, l’autenticazione con PSK non prevede enhancement di sicurezza ulteriori a meno di specificare opportuni algoritmi di cifratura (cfr. paragrafo successivo).</p>

<h3 id="cifratura">Cifratura</h3>

<p>Come sempre succede, negli scenari di cifratura asimmetrica orientatia alla connessione, l’autenticazione, per quanto possa essere accurata, costituisce solo uno degli aspetti di cui tenere conto nella fase di messa in sicurezza del servizio.</p>

<p>Avere un’autenticazione con certificato generato con tutti i crismi da una CA, è certamente una gran cosa ma mette in sicurezza solo l’aspetto dell’autenticazione, per l’appunto.</p>

<p>l transito dei dati è affidato alle suite di cifratura supportati da openssl e, volendo lavorare di fino, bisognerebbe stringere anche su quelli.</p>

<p>Altrimenti, paradossalmente, si avrà un’autenticazione robustissima ma con algoritmi colabrodo di cifratura dei dati che, ad attaccanti ben motivati, lascerebbero delle porte spalancate per sferrare attacchi MITM per intercettare direttamente i dati, piuttosto che provare il furto di identità.</p>

<p>Ad ogni modo, il default, per ragioni di compatibilità legacy, dei parametri di cifratura (da settare eventualmente su client e sul server) è il seguente:</p>

<pre><code>ciphers: HIGH:!aNULL:!SSLv2:!DH:!kDHEPSK
ciphersuites: TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256
</code></pre>

<p>dove <strong>ciphers</strong> raggruppa le suite di cifratura relativi a TLS1.2 in giù e <strong>ciphersuites</strong> riguarda invece TLS1.3.</p>

<h3 id="configurazione-servizio">Configurazione servizio</h3>

<p>Ulteriori restrizioni possono essere aggiunte a livelllo di sistema:</p>
<ol><li>regolando i bit di <strong>setuid</strong> e <strong>setgid</strong> se stunnel gira come root</li>
<li>delimitando l’esecuzione del processo in una gabbia <strong>chroot</strong></li>
<li>disabilitando le <em>regonetiation</em> (se supportato dalla versione di openssl in uso) che mitiga in parte attacchi dos di tipo CPU-exhaustion</li>
<li>ecc..</li></ol>

<h2 id="caso-reale-tunnelizzare-transmission">Caso reale: tunnelizzare Transmission.</h2>

<p><strong>Transmission</strong> è un client <strong>bittorrent</strong> che può essere controllato da remoto via <strong>rpc</strong> attraverso un’interfaccia web o altri client.</p>

<p>Transmission, nelle sue varie declinazioni, non incapsula il traffico rpc in una connessione tls.</p>

<p>Se si vuole che il dato in transito sia cifrato e che la connessione sia autenticata, si può ricorrere:</p>
<ol><li>ad un tunnel ssh;</li>
<li>all’intermediazione di un webserver come apache o nginx ;</li>
<li>ad uno stunnel server</li></ol>

<p>Sugli ultimi due punti si può configurare una mutua autenticazione con certificato (complessa e articolata e, per tanti client, ha un impatto significativo). Scegliamo il 3° caso.</p>

<h2 id="esposizione-dell-interfaccia-web">Esposizione dell’interfaccia web</h2>

<p><strong>Scenario</strong>: abbiamo una macchina (il nostro serverino di fiducia o un nas) con transmission a bordo, presumibilmente dietro un firewall che natta il suo indirizzo privato (192.168.70.15).</p>

<p><img src="https://pixelfed.uno/storage/m/_v2/489827599091373610/42a8ecf32-5a8865/xP2UKA4QZMkK/OVVicpId7etadviBPtZUKHscaXrzy4vlaZAWAoBn.png" alt="Transmission Stunnel">
<small>Esposizione transmission attraverso stunnel server</small></p>

<p><strong>Obiettivo</strong>: Vogliamo poter raggiungere transmission e vogliamo che i client autentichino la loro connessione prima di accedere al servizio. In base allo scenario, sappiamo che dovremmo configurare un port forwarding dall’AP fino allo stunnel server. Il server transmission rimarrà confinato nella nostra rete locale.</p>

<h2 id="utilizzo-di-un-client-esterno">Utilizzo di un client esterno</h2>

<p>Supponiamo di utilizzare diversi client: il browser, <strong>transmission-remote-gtk</strong> o <strong>transmission-remote-gui</strong> (<em>transgui</em>), <strong>tremotesf</strong> (mobile).</p>

<p>La prima cosa da fare è configurare un port forwarding sul nostro AP</p>

<p>    Rule<sub>1</sub>: <strong>9091</strong> ⇒ 192.168.70.10:<strong>9091</strong></p>

<p>Dopodichè, configureremo stunnel in server mode davanti a transmission</p>

<pre><code>[transmission]
accept = 192.168.70.10:9091
connect = 192.168.70.15:9091
cert = &lt;path stunnel conf&gt;/ssl/server/certs/stunnel_crt.pem
key = &lt;path stunnel conf&gt;/ssl/server/private/stunnel_key.pem
requireCert = yes
verifyChain = yes
verifyPeer = yes
CApath = &lt;path stunnel conf&gt;/ssl/client/certs
</code></pre>

<p><strong>Alcune note sulla configurazione</strong>:</p>
<ul><li>devo disporre di un certificato per stunnel</li>
<li>devo disporre dei certificati per i client che si connetteranno, le cui fullchain dovranno essere localizzate nel path indicato da CApath</li></ul>

<p>A questo punto basta che i web-client puntino all’host pubblico (151.25.77.205) sulla porta 9091 e il gioco è fatto.</p>

<p>Per gli altri client come <strong>transmission-remote-gtk</strong>, <strong>transgui</strong> o <strong>tremotesf</strong>, per i quali al massimo posso chiedere di usare TLS (ma non sono riuscito ad usare un’autenticazione lato client), conviene installare stunnel in client mode :</p>

<pre><code>[transmission-client]
accept = localhost:9091
connect = 151.25.77.205:9091
client = yes
cert = &lt;path stunnel conf&gt;/ssl/client/laptop_crt.pem
key = &lt;path stunnel conf&gt;/ssl/client/laptop_key.pem
verifyChain = yes
verifyPeer = yes
CAPath = &lt;path stunnel conf&gt;/ssl/server/certs
</code></pre>

<p>Il client (ad es. sul laptop) si connetterà su localhost:9091 e dovrà disporre anche del certificato del server e della sua fullchain affinchè i check su <strong>verifyChain</strong> e <strong>verifyPeer</strong> non falliscano.</p>

<p><a href="/aytin/tag:ca" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">ca</span></a> <a href="/aytin/tag:DigitalCertificate" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">DigitalCertificate</span></a> <a href="/aytin/tag:AsymmetricEncryption" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">AsymmetricEncryption</span></a> <a href="/aytin/tag:SymmetricEncryption" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">SymmetricEncryption</span></a> <a href="/aytin/tag:cryptography" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">cryptography</span></a> <a href="/aytin/tag:fullchain" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">fullchain</span></a> <a href="/aytin/tag:psk" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">psk</span></a> <a href="/aytin/tag:ssh" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">ssh</span></a> <a href="/aytin/tag:ssl" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">ssl</span></a> <a href="/aytin/tag:stunnel" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">stunnel</span></a> <a href="/aytin/tag:tls" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">tls</span></a> <a href="/aytin/tag:x509" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">x509</span></a> <a href="/aytin/tag:openssl" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">openssl</span></a></p>
]]></content:encoded>
      <guid>https://noblogo.org/aytin/stunnel-cose-e-come-si-configura</guid>
      <pubDate>Sat, 04 Mar 2023 15:16:09 +0000</pubDate>
    </item>
  </channel>
</rss>