Panoramica della crittografia, delle firme digitali e degli algoritmi hash in .NET

Questo articolo offre una panoramica dei metodi e delle procedure di crittografia supportati da .NET, inclusi i manifesti ClickOnce.

Introduzione alla crittografia

Le reti pubbliche, ad esempio Internet, non offrono comunicazioni sicure tra entità. Le comunicazioni su tali reti possono essere lette o addirittura modificate da terze parti non autorizzate. La crittografia aiuta a proteggere i dati dalla visualizzazione, offre modalità per rilevare se i dati sono stati modificati e aiuta a offrire una modalità di comunicazione sicura su canali altrimenti non sicuri. Ad esempio, è possibile crittografare i dati usando un algoritmo di crittografia, trasmesso in stato crittografato e quindi decrittografato dal destinatario designato. Se una terza parte intercetta i dati crittografati, la decrittografia risulterà difficile.

In .NET le classi dello spazio dei nomi System.Security.Cryptography gestiscono automaticamente molti dettagli della crittografia. Alcuni sono wrapper per implementazioni del sistema operativo, mentre altri sono implementazioni puramente gestite. Non è necessario essere esperti di crittografia per usare queste classi. Quando si crea una nuova istanza di una delle classi dell'algoritmo di crittografia, le chiavi vengono generate automaticamente per semplificare l'uso e le proprietà predefinite offrono la maggiore sicurezza possibile.

Primitive di crittografia

In una situazione tipica in cui si usa la crittografia, due parti (Alice e Bob) comunicano su un canale non sicuro. Alice e Bob vogliono assicurare che le comunicazioni risultino incomprensibili a eventuali ascoltatori. Poiché, inoltre, Alice e Bob si trovano in posizioni remote, Alice deve assicurarsi che le informazioni ricevute da Bob non siano state modificate da altri durante la trasmissione. Deve inoltre assicurarsi che le informazioni provengano effettivamente da Bob e non da qualcuno che lo sta rappresentando.

La crittografia permette di realizzare gli obiettivi seguenti:

  • Riservatezza: aiuta a proteggere l'identità o i dati di un utente dalla lettura.

  • Integrità dei dati: aiuta a proteggere i dati dalla modifica.

  • Autenticazione: assicura che i dati provengano da una parte specifica.

  • Non ripudio: impedisce a una parte specifica di negare di avere inviato un messaggio.

Per realizzare questi obiettivi, è possibile usare una combinazione di algoritmi e procedure nota come primitive di crittografia per creare uno schema crittografico. La tabella seguente elenca le primitive di crittografia e il rispettivo uso.

Primitiva di crittografia Utilizzo
Crittografia a chiave segreta (crittografia simmetrica) Esegue una trasformazione sui dati per impedirne la lettura da terze parti. Questo tipo di crittografia usa una singola chiave segreta condivisa per crittografare e decrittografare i dati.
Crittografia a chiave pubblica (crittografia asimmetrica) Esegue una trasformazione sui dati per impedirne la lettura da terze parti. Questo tipo di crittografia usa una coppia di chiavi pubblica/privata per crittografare e decrittografare i dati.
Firme di crittografia Aiuta a verificare che i dati provengano da una parte specifica, tramite la creazione di una firma digitale univoca per tale parte. Questo processo usa anche funzioni hash.
Hash di crittografia Mappa i dati di qualsiasi lunghezza a una sequenza di byte a lunghezza fissa. Gli hash sono statisticamente univoci. Una sequenza di due byte diversa non avrà come risultato hash con lo stesso valore.

Crittografia a chiave segreta

Gli algoritmi di crittografia a chiave segreta usano una singola chiave segreta per crittografare e decrittografare i dati. È necessario proteggere la chiave dall'accesso da parte di agenti non autorizzati, poiché chiunque sia in possesso della chiave la potrà usare per decrittografare i dati o crittografare i propri dati, affermando che sono stati originati dall'utente.

La crittografia a chiave segreta è definita anche crittografia simmetrica, poiché si usa la stessa chiave per la crittografia e la decrittografia. Gli algoritmi di crittografia a chiave segreta sono molto veloci, rispetto agli algoritmi a chiave pubblica e sono ottimali per l'esecuzione di trasformazioni crittografiche su grandi flussi di dati. Gli algoritmi di crittografia asimmetrica, ad esempio RSA, sono matematicamente limitati a livello di quantità di dati che sono in grado di crittografare. Gli algoritmi di crittografia simmetrica non presentano in genere questi problemi.

Un tipo di algoritmo a chiave segreta, denominato crittografia a blocchi, viene usato per crittografare un blocco di dati alla volta. Tramite la crittografia a blocchi, come Data Encryption Standard (DES), TripleDES e Advanced Encryption Standard (AES), un blocco di input di n byte viene trasformato a livello di crittografia in un blocco di output di byte crittografati. Se si vuole crittografare o decrittografare una sequenza di byte, è necessario eseguire tale operazione blocco per blocco. Poiché la dimensione di n è limitata (8 byte per DES e TripleDES, 16 byte come valore predefinito, 24 o 32 byte per AES), i valori di dati superiori a n devono essere crittografati un blocco alla volta. I valori di dati inferiori a n devono essere espansi a n per essere elaborati.

Una forma semplice di crittografia a blocchi viene definita modalità ECB (Electronic Codebook). La modalità ECB non è considerata sicura, poiché non usa un vettore di inizializzazione per inizializzare il primo blocco di testo normale. Per una determinata chiave segreta k, tramite una semplice crittografia a blocchi in cui non viene usato un vettore di inizializzazione lo stesso blocco di input di testo non crittografato verrà crittografato nello stesso blocco di output di testo crittografato. Se sono quindi presenti blocchi duplicati nel flusso di testo normale di input, saranno presenti blocchi duplicati nel flusso di testo crittografato di output. Questi blocchi di output duplicati segnalano agli utenti non autorizzati la crittografia debole usata, gli algoritmi usati e le possibili modalità di attacco. La modalità ECB è quindi abbastanza vulnerabile all'analisi e quindi all'individuazione delle chiavi.

Le classi d crittografia a blocchi fornite nella libreria di classi base usano una modalità di concatenamento predefinita denominata CBC (Cipher-Block Chaining), ma se si vuole è possibile modificare questa impostazione predefinita.

La crittografia CBC supera i problemi associati alla crittografia ECB usando un vettore di inizializzazione per crittografare il primo blocco di testo normale. Ogni blocco successivo di testo normale viene sottoposto a un'operazione Bitwise-OR esclusiva (XOR) con il blocco di testo crittografato precedente prima della crittografia. Ogni blocco di testo crittografato dipende prima da tutti i blocchi precedenti. Quando viene usato questo sistema, le intestazioni di messaggio comuni che potrebbero essere note a un utente non autorizzato non potranno essere usate per decodificare una chiave.

Un modo per compromettere dati crittografati con la modalità CBC consiste nell'eseguire una ricerca completa di ogni chiave possibile. In base alle dimensioni della chiave usata per eseguire la crittografia, questo tipo di ricerca richiede molto tempo anche nei computer più veloci ed è quindi irrealizzabile. Le dimensioni di chiave più elevate sono più difficili da decifrare. Benché la crittografia non renda teoricamente impossibile a un utente malintenzionato il recupero di dati crittografati, aumenta il costo di tale operazione. Se sono necessari tre mesi per eseguire una ricerca completa per recuperare dati che risultano significativi solo per alcuni giorni, il metodo di ricerca completa risulta poco pratico.

Lo svantaggio della crittografia a chiave segreta consiste nel fatto che presuppone che le due parti si siano accordate su una chiave e un vettore di inizializzazione e che abbiano comunicato i rispettivi valori. Il vettore di inizializzazione non è considerato segreto e può essere trasmesso in testo normale con il messaggio. La chiave deve essere tuttavia mantenuta segreta agli utenti non autorizzati. A causa di questi problemi, la crittografia a chiave segreta viene usata spesso insieme alla crittografia a chiave pubblica per comunicare in modo privato i valori della chiave e del vettore di inizializzazione.

Supponendo che Alice e Bob siano due parti che vogliono comunicare su un canale non sicuro, potranno usare la crittografia a chiave segreta nel modo seguente: Alice e Bob decidono di usare un algoritmo specifico, ad esempio AES, con una chiave e un vettore di inizializzazione specifici. Alice compone un messaggio e crea un flusso di rete (forse una named pipe o un’e-mail di rete) su cui inviare il messaggio. In seguito crittografa il testo usando la chiave e il vettore di inizializzazione e invia il messaggio crittografato e il vettore di inizializzazione a Bob tramite Intranet. Bob riceve il testo crittografato e lo decrittografa usando il vettore di inizializzazione e la chiave precedentemente concordata. Se la trasmissione viene intercettata, l'intercettore non conosce la chiave e non può recuperare il messaggio originale. In questo scenario, solo la chiave deve rimanere segreta. In uno scenario reale Alice o Bob genera una chiave segreta e usa la crittografia a chiave pubblica (asimmetrica) per trasferire la chiave segreta (simmetrica) all'altra parte. Per altre informazioni sulla crittografia a chiave pubblica, vedere la sezione successiva.

.NET fornisce le classi seguenti che implementano algoritmi di crittografia a chiave privata:

  • Aes

  • HMACSHA256, HMACSHA384 e HMACSHA512. (Tecnicamente, si tratta di algoritmi a chiave privata perché rappresentano codici di autenticazione dei messaggi calcolati usando una funzione hash crittografica combinata con una chiave privata. Vedere Valori hash più avanti in questo argomento).

Crittografia a chiave pubblica

a crittografia a chiave pubblica usa una chiave privata che deve essere tenuta segreta agli utenti non autorizzati e una chiave pubblica che può essere resa pubblica a tutti. La chiave pubblica e la chiave privata sono collegate matematicamente. I dati crittografati con la chiave pubblica possono essere decrittografati solo con la chiave privata e i dati firmati con la chiave privata possono essere verificati solo con la chiave pubblica. La chiave pubblica può essere distribuita a tutti in quanto viene usata per crittografare i dati da inviare a chi detiene la chiave privata. Gli algoritmi di crittografia a chiave pubblica sono noti anche come algoritmi asimmetrici, poiché per crittografare e successivamente decrittografare i dati è necessario usare due chiavi diverse. Una regola di crittografia di base proibisce il riutilizzo di chiavi ed entrambe le chiavi devono essere univoche per ogni sessione di comunicazione. In pratica, tuttavia, le chiavi asimmetriche sono in genere di lunga durata.

Le due parti, Alice e Bob, possono usare la crittografia a chiave pubblica come illustrato di seguito. Alice genera prima di tutto una coppia di chiavi pubblica/privata. Se Bob vuole inviare ad Alice un messaggio crittografato, le chiede la chiave pubblica. Alice invia a Bob la chiave pubblica su una rete non sicura e Bob la usa per crittografare un messaggio. Bob invia il messaggio crittografato ad Alice, che lo decrittografa usando la sua chiave privata. Se Bob ha ricevuto la chiave di Alice su un canale non sicuro, ad esempio una rete pubblica, sarà esposto a un attacco di tipo man-in-the-middle. Bob deve quindi verificare con Alice che la copia a sua disposizione della chiave pubblica sia corretta.

Durante la trasmissione della chiave pubblica di Alice, una persona non autorizzata potrebbe intercettare la chiave. La stessa persona potrebbe intercettare anche il messaggio crittografato da Bob. Tale persona, tuttavia, non potrà decrittografare il messaggio con la chiave pubblica. Il messaggio può essere decrittografato solo con la chiave privata di Alice che non è stata trasmessa. Alice non usa la propria chiave privata per crittografare un messaggio di risposta a Bob, poiché chiunque sia in possesso della chiave pubblica potrebbe decrittografare il messaggio. Se Alice vuole inviare un messaggio a Bob, gli chiede la chiave pubblica e crittografa il suo messaggio usando quella chiave. Bob decrittografa quindi il messaggio usando la sua chiave privata associata.

In questo scenario Alice e Bob usano la crittografia a chiave pubblica (asimmetrica) per trasferire una chiave segreta (simmetrica) e usano la crittografia a chiave segreta per il resto della sessione.

L'elenco seguente offre un confronto tra algoritmi di crittografia a chiave pubblica e a chiave segreta:

  • Gli algoritmi di crittografia a chiave pubblica usano una dimensione fissa del buffer, mentre gli algoritmi di crittografia a chiave segreta usano un buffer a lunghezza variabile.

  • Gli algoritmi di crittografia a chiave pubblica non possono essere usati per il concatenamento di dati in flussi come gli algoritmi di crittografia a chiave segreta, poiché è possibile crittografare solo piccole quantità di dati. Le operazioni asimmetriche non usano quindi lo stesso modello di streaming delle operazioni simmetriche.

  • La crittografia a chiave pubblica ha uno spazio delle chiavi (intervallo dei valori possibili) molto più ampio rispetto alla crittografia a chiave segreta, pertanto è meno esposta a tecniche esaustive per scoprire la chiave.

  • Le chiavi pubbliche possono essere distribuite in modo semplice poiché non è necessario proteggerle, purché sia possibile verificare l'identità del mittente.

  • Alcuni algoritmi a chiave pubblica, ad esempio RSA e DSA, ma non Diffie-Hellman, possono essere usati per creare firme digitali per la verifica dell'identità del mittente dei dati.

  • Gli algoritmi a chiave pubblica sono molto lenti rispetto a quelli a chiave segreta e non sono destinati alla crittografia di grandi quantità di dati. Risultano utili solo per il trasferimento di piccole quantità di dati. Generalmente la crittografia a chiave pubblica viene usata per crittografare una chiave e un vettore di inizializzazione utilizzabili da un algoritmo a chiave segreta. Dopo il trasferimento della chiave e del vettore di inizializzazione, la crittografia a chiave segreta viene usata per il resto della sessione.

.NET offre le classi seguenti che implementano gli algoritmi della a chiave pubblica:

RSA consente sia la crittografia sia la firma, ma DSA può essere usata solo per la firma. DSA non offre la stessa sicurezza di RSA, quindi si consiglia RSA. Il protocollo Diffie-Hellman può essere usato solo per la generazione di chiavi. In genere, gli algoritmi a chiave pubblica sono più limitati nell'utilizzo rispetto a quelli a chiave privata.

Firme digitali

Gli algoritmi a chiave pubblica possono essere usati per formare firme digitali, il cui obiettivo è l'autenticazione dell'identità di un mittente, se la chiave pubblica di quest'ultimo viene considerata attendibile, e la protezione dell'integrità dei dati. Attraverso una chiave pubblica generata da Alice, il destinatario dei suoi dati può verificare che siano stati inviati effettivamente da lei confrontando la firma digitale sui dati e la chiave pubblica di Alice.

Per apporre una firma digitale a un messaggio usando la crittografia a chiave pubblica, Alice applica prima di tutto un algoritmo hash al messaggio per creare un digest del messaggio. Il digest è una rappresentazione dei dati compatta e univoca. Alice quindi crittografa il digest del messaggio con la sua chiave privata per creare la sua firma personale. Alla ricezione del messaggio e della firma, Bob decrittografa la firma con la chiave pubblica di Alice per recuperare il digest del messaggio e genera un hash mediante lo stesso algoritmo hash inviato da Alice. Se il digest del messaggio calcolato da Bob corrisponde esattamente al digest del messaggio ricevuto da Alice, Bob ha la certezza che il messaggio provenga dal possessore della chiave privata e che i dati non siano stati modificati. Se Bob ha la certezza che Alice sia il possessore della chiave privata, saprà che il messaggio proviene solo da Alice.

Nota

Chiunque può verificare una firma, poiché la chiave pubblica del mittente è di pubblico dominio e generalmente viene inclusa nel formato della firma digitale. Questo metodo non mantiene la segretezza del messaggio. Perché possa essere segreto, anche il messaggio deve essere crittografato.

.NET offre le classi seguenti che implementano gli algoritmi della firma digitale:

Valore hash

Gli algoritmi hash associano valori binari di lunghezza arbitraria a piccoli valori binari di lunghezza fissa, noti come valori hash. Per valore hash si intende una rappresentazione numerica di una porzione di dati. Se si inserisce un hash in un paragrafo di testo non crittografato e si modifica anche una sola lettera del paragrafo, un hash successivo produrrà un valore diverso. Se l'hash è basato su una crittografia avanzata, il relativo valore verrà modificato in modo significativo. Se ad esempio viene modificato un singolo bit di un messaggio, una funzione hash sicura può produrre un output che si differenzia del 50%. Molti valori di input possono avere hash dello stesso valore di output. Dal punto di vista del calcolo, tuttavia, è impossibile trovare due input di versi che forniscono come risultato un hash con lo stesso valore.

Due parti (Alice e Bob) sono riuscite a usare una funzione hash per garantire l'integrità del messaggio. Hanno selezionato un algoritmo di hash per firmare i messaggi. Alice ha scritto un messaggio, quindi ha creato un hash di tale messaggio tramite l'algoritmo selezionato. Hanno quindi seguito uno dei metodi seguenti:

  • Alice invia il messaggio come testo normale e il messaggio con hash (firma digitale) a Bob. Bob riceve il messaggio, ne esegue l'hashing, quindi confronta il proprio valore hash con quello che ha ricevuto da Alice. Se i valori hash corrispondono, il messaggio non è stato alterato. Se invece i valori non corrispondono, il messaggio è stato alterato dopo essere stato scritto da Alice.

    Purtroppo, questo metodo non consente di stabilire l'autenticità del mittente. Chiunque può rappresentare Alice e inviare un messaggio a Bob. Possono usare lo stesso algoritmo hash per firmare il messaggio e tutto ciò che Bob è in grado di determinare è che il messaggio corrisponde alla relativa firma. Si tratta di una forma di attacco di tipo man-in-the-middle. Per altre informazioni, vedere l'esempio di comunicazioni protette con Cryptography Next Generation (CNG).

  • Alice invia il messaggio come testo normale a Bob tramite un canale pubblico non protetto. Invia il messaggio con hash a Bob su un canale privato protetto. Bob riceve il messaggio in testo normale, ne esegue l'hashing, quindi confronta il valore hash con quello scambiato privatamente. Se i valori corrispondono, Bob può accertare quanto segue:

    • Il messaggio non è stato modificato.

    • Il mittente del messaggio (Alice) è autentico.

    Perché il sistema funzioni, Alice deve nascondere il valore hash originale a tutte le parti ad eccezione di Bob.

  • Alice invia il messaggio in testo normale a Bob tramite un canale pubblico non protetto e inserisce il messaggio con hash sul proprio sito Web pubblico.

    Questo metodo consente di evitare la manomissione del messaggio impedendo a chiunque di modificare il valore hash. Anche se chiunque può leggere il messaggio e il relativo hash, il valore hash può essere modificato solo da Alice. Un utente non autorizzato che vuole rappresentare Alice necessita di accesso al sito Web di Alice.

Nessuno dei metodi precedenti impedisce la lettura dei messaggi di Alice, perché vengono trasmessi come testo normale. Una soluzione di sicurezza completa richiede le firme digitali (firma dei messaggi) e la crittografia.

.NET offre le classi seguenti che implementano gli algoritmi di hash:

.NET inoltre fornisce MD5 e SHA1. Tuttavia è stato rilevato che gli algoritmi MD5 e SHA-1 non sono sicuri e ora è consigliato SHA-2. SHA-2 include SHA256, SHA384 e SHA512.

generazione casuale di numeri

La generazione di numeri casuali è integrata in molte operazioni di crittografia. Le chiavi di crittografia, ad esempio, devono essere il più casuali possibile in modo che non sia possibile riprodurle. I generatori di numeri casuali di crittografia devono generare output che sia impossibile da prevedere con una probabilità superiore al 50%. Pertanto, qualsiasi metodo di previsione del bit di output successivo non deve avere una prestazione migliore della previsione casuale. Le classi in .NET usano i generatori di numeri casuali per generare le chiavi di crittografia.

La classe RandomNumberGenerator è un'implementazione di un algoritmo di generazione di numeri casuali.

Manifesti ClickOnce

Le classi di crittografia seguenti consentono di ottenere e verificare informazioni sulle firme del manifesto per applicazioni che sono state distribuite con tecnologia ClickOnce:

Le classi seguenti forniscono inoltre informazioni specifiche sulla firma:

Classi Cryptography Next Generation (CNG)

Le classi Cryptography Next Generation (CNG) forniscono un wrapper gestito intorno alle funzioni CNG native. (CNG sostituisce CryptoAPI). Il nome di queste classi contiene "Cng". Elemento centrale delle classi wrapper CNG è la classe del contenitore di chiavi CngKey che astrae l'archiviazione e l'utilizzo delle chiavi CNG. Questa classe consente di archiviare in modo sicuro una coppia di chiavi o una chiave pubblica e fare riferimento a tale chiave usando un semplice nome di stringa. La classe di firma basata su curva ellittica ECDsaCng e la classe di crittografia ECDiffieHellmanCng possono usare oggetti CngKey .

La classe CngKey viene usata per una varietà di operazioni aggiuntive, incluse l'apertura, la creazione, l'eliminazione e l'esportazione di chiavi. Fornisce inoltre l'accesso all'handle di chiave sottostante da usare quando le funzioni native vengono chiamate direttamente.

.NET include anche varie classi CNG di supporto, quali le seguenti:

Vedi anche