Crittografare gli elementi XML con chiavi asimmetriche
È possibile usare le classi dello spazio dei nomi System.Security.Cryptography.Xml per crittografare un elemento all'interno di un documento XML. La crittografia XML consente di scambiare o archiviare dati XML crittografati in modo standard, garantendo un'adeguata protezione dei dati da letture non autorizzate. Per altre informazioni sullo standard di crittografia XML, vedere la specifica World Wide Web Consortium (W3C) per la crittografia XML disponibile all'indirizzo https://www.w3.org/TR/xmldsig-core/.
È possibile usare la crittografia XML per sostituire qualsiasi elemento o documento XML con un elemento <EncryptedData>
contenente i dati XML crittografati. Nell'elemento <EncryptedData>
possono essere inclusi anche sottoelementi in cui sono incluse informazioni sulle chiavi e sui processi usati durante la crittografia. La crittografia XML consente a un documento di contenere più elementi crittografati e consente a un elemento di essere crittografato più volte. L'esempio di codice relativo a questa procedura illustra come creare un elemento <EncryptedData>
insieme a diversi altri sottoelementi utilizzabili in un secondo momento durante la decrittografia.
Questo esempio crittografa un elemento XML mediante due chiavi. L'esempio genera una coppia di chiavi pubblica/privata RSA e salva la coppia di chiavi in un contenitore di chiavi protetto. L'esempio crea quindi una chiave di sessione separata usando l'algoritmo AES (Advanced Encryption Standard). La chiave di sessione AES viene usata dall'esempio per crittografare il documento XML e la chiave pubblica RSA viene usata per crittografare la chiave di sessione AES. L'esempio salva infine la chiave di sessione AES crittografata e i dati XML crittografati nel documento XML entro un nuovo elemento <EncryptedData
>.
Per decrittografare l'elemento XML, recuperare la chiave privata RSA dal contenitore di chiavi, usarla per decrittografare la chiave di sessione e quindi usare la chiave di sessione per decrittografare il documento. Per altre informazioni su come decrittografare un elemento XML che era stato crittografato mediante questa procedura, vedere Procedura: Decrittografare gli elementi XML con chiavi asimmetriche.
Questo esempio è adatto per situazioni in cui più applicazioni devono condividere dati crittografati o in cui un'applicazione deve salvare dati crittografati tra un'esecuzione e l'altra.
Per crittografare un elemento XML con una chiave asimmetrica
Creare un oggetto CspParameters e specificare il nome del contenitore di chiavi.
CspParameters cspParams = new CspParameters(); cspParams.KeyContainerName = "XML_ENC_RSA_KEY";
Dim cspParams As New CspParameters() cspParams.KeyContainerName = "XML_ENC_RSA_KEY"
Generare una chiave asimmetrica usando la classe RSACryptoServiceProvider. La chiave viene salvata automaticamente nel contenitore di chiavi quando si passa l'oggetto CspParameters al costruttore della classe RSACryptoServiceProvider. Questa chiave verrà usata per crittografare la chiave di sessione AES e può essere recuperata in un secondo momento per decrittografarla.
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
Dim rsaKey As New RSACryptoServiceProvider(cspParams)
Creare un oggetto XmlDocument caricando un file XML dal disco. L'oggetto XmlDocument contiene l'elemento XML da crittografare.
// Create an XmlDocument object. XmlDocument xmlDoc = new XmlDocument(); // Load an XML file into the XmlDocument object. try { xmlDoc.PreserveWhitespace = true; xmlDoc.Load("test.xml"); } catch (Exception e) { Console.WriteLine(e.Message); }
' Create an XmlDocument object. Dim xmlDoc As New XmlDocument() ' Load an XML file into the XmlDocument object. Try xmlDoc.PreserveWhitespace = True xmlDoc.Load("test.xml") Catch e As Exception Console.WriteLine(e.Message) End Try
Individuare l'elemento specificato nell'oggetto XmlDocument e creare un nuovo oggetto XmlElement per rappresentare l'elemento che si vuole crittografare. In questo esempio l'elemento
"creditcard"
è crittografato.XmlElement? elementToEncrypt = Doc.GetElementsByTagName(ElementToEncrypt)[0] as XmlElement; // Throw an XmlException if the element was not found. if (elementToEncrypt == null) { throw new XmlException("The specified element was not found"); }
Dim elementToEncrypt As XmlElement = Doc.GetElementsByTagName(EncryptionElement)(0) ' Throw an XmlException if the element was not found. If elementToEncrypt Is Nothing Then Throw New XmlException("The specified element was not found") End If
Creare una nuova chiave di sessione usando la classe Aes. Questa chiave crittograferà l'elemento XML e quindi verrà crittografata a sua volta e inserita nel documento XML.
// Create an AES key. sessionKey = Aes.Create();
' Create an AES key. sessionKey = Aes.Create()
Creare una nuova istanza della classe EncryptedXml e usarla per crittografare l'elemento specificato mediante la chiave di sessione. Il metodo EncryptData restituisce l'elemento crittografato come una matrice di byte crittografati.
EncryptedXml eXml = new EncryptedXml(); byte[] encryptedElement = eXml.EncryptData(elementToEncrypt, sessionKey, false);
Dim eXml As New EncryptedXml() Dim encryptedElement As Byte() = eXml.EncryptData(elementToEncrypt, sessionKey, False)
Costruire un oggetto EncryptedData e popolarlo con l'identificatore dell'URL dell'elemento XML crittografato. Questo identificatore dell'URL comunica a chi esegue la decrittografia che l'elemento XML contiene un elemento crittografato. È possibile usare il campo XmlEncElementUrl per specificare l'identificatore dell'URL. L'elemento XML in testo non crittografato verrà sostituito da un elemento
<EncryptedData>
incapsulato da questo oggetto EncryptedData.EncryptedData edElement = new EncryptedData(); edElement.Type = EncryptedXml.XmlEncElementUrl; edElement.Id = EncryptionElementID;
Dim edElement As New EncryptedData() edElement.Type = EncryptedXml.XmlEncElementUrl edElement.Id = EncryptionElementID
Creare un oggetto EncryptionMethod inizializzato sull'identificatore dell'URL dell'algoritmo di crittografia usato per generare la chiave di sessione. Passare l'oggetto EncryptionMethod alla proprietà EncryptionMethod.
edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
edElement.EncryptionMethod = New EncryptionMethod(EncryptedXml.XmlEncAES256Url)
Creare un oggetto EncryptedKey in cui includere la chiave di sessione crittografata. Crittografare la chiave di sessione, aggiungerla all'oggetto EncryptedKey e immettere un nome di chiave di sessione e l'URL dell'identificatore della chiave.
EncryptedKey ek = new EncryptedKey(); byte[] encryptedKey = EncryptedXml.EncryptKey(sessionKey.Key, Alg, false); ek.CipherData = new CipherData(encryptedKey); ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url);
Dim ek As New EncryptedKey() Dim encryptedKey As Byte() = EncryptedXml.EncryptKey(sessionKey.Key, Alg, False) ek.CipherData = New CipherData(encryptedKey) ek.EncryptionMethod = New EncryptionMethod(EncryptedXml.XmlEncRSA15Url)
Creare un nuovo oggetto DataReference che esegue il mapping dei dati crittografati a una chiave di sessione specifica. Questo passaggio facoltativo permette di specificare con facilità che più parti di un documento XML sono state crittografate da una singola chiave.
DataReference dRef = new DataReference(); // Specify the EncryptedData URI. dRef.Uri = "#" + EncryptionElementID; // Add the DataReference to the EncryptedKey. ek.AddReference(dRef);
Dim dRef As New DataReference() ' Specify the EncryptedData URI. dRef.Uri = "#" + EncryptionElementID ' Add the DataReference to the EncryptedKey. ek.AddReference(dRef)
Aggiungere la chiave crittografata all'oggetto EncryptedData.
edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));
edElement.KeyInfo.AddClause(New KeyInfoEncryptedKey(ek))
Creare un nuovo oggetto KeyInfo per specificare il nome della chiave RSA. Aggiungerlo all'oggetto EncryptedData. Ciò permette a chi esegue la crittografia di identificare la chiave asimmetrica corretta da usare per decrittografare la chiave di sessione.
// Create a new KeyInfoName element. KeyInfoName kin = new KeyInfoName(); // Specify a name for the key. kin.Value = KeyName; // Add the KeyInfoName element to the // EncryptedKey object. ek.KeyInfo.AddClause(kin);
' Create a new KeyInfoName element. Dim kin As New KeyInfoName() ' Specify a name for the key. kin.Value = KeyName ' Add the KeyInfoName element to the ' EncryptedKey object. ek.KeyInfo.AddClause(kin)
Aggiungere i dati dell'elemento crittografato all'oggetto EncryptedData.
edElement.CipherData.CipherValue = encryptedElement;
edElement.CipherData.CipherValue = encryptedElement
Sostituire l'elemento dall'oggetto XmlDocument originale con l'elemento EncryptedData.
EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
EncryptedXml.ReplaceElement(elementToEncrypt, edElement, False)
Salvare l'oggetto XmlDocument.
xmlDoc.Save("test.xml");
xmlDoc.Save("test.xml")
Esempio
Questo esempio presuppone che sia presente un file denominato "test.xml"
nella stessa directory del programma compilato e che "test.xml"
contenga un elemento "creditcard"
. È possibile inserire il codice XML seguente in un file denominato test.xml
e usarlo con questo esempio.
<root>
<creditcard>
<number>19834209</number>
<expiry>02/02/2002</expiry>
</creditcard>
</root>
using System;
using System.Xml;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Runtime.Versioning;
[SupportedOSPlatform("windows")]
class Program
{
static void Main(string[] args)
{
// Create an XmlDocument object.
XmlDocument xmlDoc = new XmlDocument();
// Load an XML file into the XmlDocument object.
try
{
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load("test.xml");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
// Create a new CspParameters object to specify
// a key container.
CspParameters cspParams = new CspParameters();
cspParams.KeyContainerName = "XML_ENC_RSA_KEY";
// Create a new RSA key and save it in the container. This key will encrypt
// a symmetric key, which will then be encrypted in the XML document.
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
try
{
// Encrypt the "creditcard" element.
Encrypt(xmlDoc, "creditcard", "EncryptedElement1", rsaKey, "rsaKey");
// Save the XML document.
xmlDoc.Save("test.xml");
// Display the encrypted XML to the console.
Console.WriteLine("Encrypted XML:");
Console.WriteLine();
Console.WriteLine(xmlDoc.OuterXml);
Decrypt(xmlDoc, rsaKey, "rsaKey");
xmlDoc.Save("test.xml");
// Display the encrypted XML to the console.
Console.WriteLine();
Console.WriteLine("Decrypted XML:");
Console.WriteLine();
Console.WriteLine(xmlDoc.OuterXml);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
// Clear the RSA key.
rsaKey.Clear();
}
Console.ReadLine();
}
public static void Encrypt(XmlDocument Doc, string ElementToEncrypt, string EncryptionElementID, RSA Alg, string KeyName)
{
// Check the arguments.
if (Doc == null)
throw new ArgumentNullException("Doc");
if (ElementToEncrypt == null)
throw new ArgumentNullException("ElementToEncrypt");
if (EncryptionElementID == null)
throw new ArgumentNullException("EncryptionElementID");
if (Alg == null)
throw new ArgumentNullException("Alg");
if (KeyName == null)
throw new ArgumentNullException("KeyName");
////////////////////////////////////////////////
// Find the specified element in the XmlDocument
// object and create a new XmlElement object.
////////////////////////////////////////////////
XmlElement? elementToEncrypt = Doc.GetElementsByTagName(ElementToEncrypt)[0] as XmlElement;
// Throw an XmlException if the element was not found.
if (elementToEncrypt == null)
{
throw new XmlException("The specified element was not found");
}
Aes? sessionKey = null;
try
{
//////////////////////////////////////////////////
// Create a new instance of the EncryptedXml class
// and use it to encrypt the XmlElement with the
// a new random symmetric key.
//////////////////////////////////////////////////
// Create an AES key.
sessionKey = Aes.Create();
EncryptedXml eXml = new EncryptedXml();
byte[] encryptedElement = eXml.EncryptData(elementToEncrypt, sessionKey, false);
////////////////////////////////////////////////
// Construct an EncryptedData object and populate
// it with the desired encryption information.
////////////////////////////////////////////////
EncryptedData edElement = new EncryptedData();
edElement.Type = EncryptedXml.XmlEncElementUrl;
edElement.Id = EncryptionElementID;
// Create an EncryptionMethod element so that the
// receiver knows which algorithm to use for decryption.
edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
// Encrypt the session key and add it to an EncryptedKey element.
EncryptedKey ek = new EncryptedKey();
byte[] encryptedKey = EncryptedXml.EncryptKey(sessionKey.Key, Alg, false);
ek.CipherData = new CipherData(encryptedKey);
ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url);
// Create a new DataReference element
// for the KeyInfo element. This optional
// element specifies which EncryptedData
// uses this key. An XML document can have
// multiple EncryptedData elements that use
// different keys.
DataReference dRef = new DataReference();
// Specify the EncryptedData URI.
dRef.Uri = "#" + EncryptionElementID;
// Add the DataReference to the EncryptedKey.
ek.AddReference(dRef);
// Add the encrypted key to the
// EncryptedData object.
edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));
// Set the KeyInfo element to specify the
// name of the RSA key.
// Create a new KeyInfoName element.
KeyInfoName kin = new KeyInfoName();
// Specify a name for the key.
kin.Value = KeyName;
// Add the KeyInfoName element to the
// EncryptedKey object.
ek.KeyInfo.AddClause(kin);
// Add the encrypted element data to the
// EncryptedData object.
edElement.CipherData.CipherValue = encryptedElement;
////////////////////////////////////////////////////
// Replace the element from the original XmlDocument
// object with the EncryptedData element.
////////////////////////////////////////////////////
EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
}
finally
{
sessionKey?.Clear();
}
}
public static void Decrypt(XmlDocument Doc, RSA Alg, string KeyName)
{
// Check the arguments.
if (Doc == null)
throw new ArgumentNullException("Doc");
if (Alg == null)
throw new ArgumentNullException("Alg");
if (KeyName == null)
throw new ArgumentNullException("KeyName");
// Create a new EncryptedXml object.
EncryptedXml exml = new EncryptedXml(Doc);
// Add a key-name mapping.
// This method can only decrypt documents
// that present the specified key name.
exml.AddKeyNameMapping(KeyName, Alg);
// Decrypt the element.
exml.DecryptDocument();
}
}
Imports System.Xml
Imports System.Security.Cryptography
Imports System.Security.Cryptography.Xml
Class Program
Shared Sub Main(ByVal args() As String)
' Create an XmlDocument object.
Dim xmlDoc As New XmlDocument()
' Load an XML file into the XmlDocument object.
Try
xmlDoc.PreserveWhitespace = True
xmlDoc.Load("test.xml")
Catch e As Exception
Console.WriteLine(e.Message)
End Try
' Create a new CspParameters object to specify
' a key container.
Dim cspParams As New CspParameters()
cspParams.KeyContainerName = "XML_ENC_RSA_KEY"
' Create a new RSA key and save it in the container. This key will encrypt
' a symmetric key, which will then be encrypted in the XML document.
Dim rsaKey As New RSACryptoServiceProvider(cspParams)
Try
' Encrypt the "creditcard" element.
Encrypt(xmlDoc, "creditcard", "EncryptedElement1", rsaKey, "rsaKey")
' Save the XML document.
xmlDoc.Save("test.xml")
' Display the encrypted XML to the console.
Console.WriteLine("Encrypted XML:")
Console.WriteLine()
Console.WriteLine(xmlDoc.OuterXml)
Decrypt(xmlDoc, rsaKey, "rsaKey")
xmlDoc.Save("test.xml")
' Display the encrypted XML to the console.
Console.WriteLine()
Console.WriteLine("Decrypted XML:")
Console.WriteLine()
Console.WriteLine(xmlDoc.OuterXml)
Catch e As Exception
Console.WriteLine(e.Message)
Finally
' Clear the RSA key.
rsaKey.Clear()
End Try
Console.ReadLine()
End Sub
Public Shared Sub Encrypt(ByVal Doc As XmlDocument, ByVal EncryptionElement As String, ByVal EncryptionElementID As String, ByVal Alg As RSA, ByVal KeyName As String)
' Check the arguments.
ArgumentNullException.ThrowIfNull(Doc)
ArgumentNullException.ThrowIfNull(EncryptionElement)
ArgumentNullException.ThrowIfNull(EncryptionElementID)
ArgumentNullException.ThrowIfNull(Alg)
ArgumentNullException.ThrowIfNull(KeyName)
'//////////////////////////////////////////////
' Find the specified element in the XmlDocument
' object and create a new XmlElement object.
'//////////////////////////////////////////////
Dim elementToEncrypt As XmlElement = Doc.GetElementsByTagName(EncryptionElement)(0)
' Throw an XmlException if the element was not found.
If elementToEncrypt Is Nothing Then
Throw New XmlException("The specified element was not found")
End If
Dim sessionKey As Aes = Nothing
Try
'////////////////////////////////////////////////
' Create a new instance of the EncryptedXml class
' and use it to encrypt the XmlElement with the
' a new random symmetric key.
'////////////////////////////////////////////////
' Create an AES key.
sessionKey = Aes.Create()
Dim eXml As New EncryptedXml()
Dim encryptedElement As Byte() = eXml.EncryptData(elementToEncrypt, sessionKey, False)
'//////////////////////////////////////////////
' Construct an EncryptedData object and populate
' it with the desired encryption information.
'//////////////////////////////////////////////
Dim edElement As New EncryptedData()
edElement.Type = EncryptedXml.XmlEncElementUrl
edElement.Id = EncryptionElementID
' Create an EncryptionMethod element so that the
' receiver knows which algorithm to use for decryption.
edElement.EncryptionMethod = New EncryptionMethod(EncryptedXml.XmlEncAES256Url)
' Encrypt the session key and add it to an EncryptedKey element.
Dim ek As New EncryptedKey()
Dim encryptedKey As Byte() = EncryptedXml.EncryptKey(sessionKey.Key, Alg, False)
ek.CipherData = New CipherData(encryptedKey)
ek.EncryptionMethod = New EncryptionMethod(EncryptedXml.XmlEncRSA15Url)
' Create a new DataReference element
' for the KeyInfo element. This optional
' element specifies which EncryptedData
' uses this key. An XML document can have
' multiple EncryptedData elements that use
' different keys.
Dim dRef As New DataReference()
' Specify the EncryptedData URI.
dRef.Uri = "#" + EncryptionElementID
' Add the DataReference to the EncryptedKey.
ek.AddReference(dRef)
' Add the encrypted key to the
' EncryptedData object.
edElement.KeyInfo.AddClause(New KeyInfoEncryptedKey(ek))
' Set the KeyInfo element to specify the
' name of the RSA key.
' Create a new KeyInfoName element.
Dim kin As New KeyInfoName()
' Specify a name for the key.
kin.Value = KeyName
' Add the KeyInfoName element to the
' EncryptedKey object.
ek.KeyInfo.AddClause(kin)
' Add the encrypted element data to the
' EncryptedData object.
edElement.CipherData.CipherValue = encryptedElement
'//////////////////////////////////////////////////
' Replace the element from the original XmlDocument
' object with the EncryptedData element.
'//////////////////////////////////////////////////
EncryptedXml.ReplaceElement(elementToEncrypt, edElement, False)
Catch e As Exception
' re-throw the exception.
Throw
Finally
If Not (sessionKey Is Nothing) Then
sessionKey.Clear()
End If
End Try
End Sub
Public Shared Sub Decrypt(ByVal Doc As XmlDocument, ByVal Alg As RSA, ByVal KeyName As String)
' Check the arguments.
ArgumentNullException.ThrowIfNull(Doc)
ArgumentNullException.ThrowIfNull(Alg)
ArgumentNullException.ThrowIfNull(KeyName)
' Create a new EncryptedXml object.
Dim exml As New EncryptedXml(Doc)
' Add a key-name mapping.
' This method can only decrypt documents
' that present the specified key name.
exml.AddKeyNameMapping(KeyName, Alg)
' Decrypt the element.
exml.DecryptDocument()
End Sub
End Class
Compilazione del codice
- In un progetto destinato a .NET Framework includere un riferimento a
System.Security.dll
. - In un progetto destinato a .NET Core o .NET 5 installare il pacchetto NuGet System.Security.Cryptography.Xml.
- Includere gli spazi dei nomi seguenti: System.Xml, System.Security.Cryptography e System.Security.Cryptography.Xml.
Protezione .NET
Non archiviare mai una chiave crittografica in testo non crittografato e non trasferire mai in testo non crittografato una chiave simmetrica tra computer. Non archiviare né trasferire mai in testo non crittografato, inoltre, la chiave privata di una coppia di chiavi asimmetriche. Per altre informazioni sulle chiavi crittografiche simmetriche e asimmetriche, vedere Generazione di chiavi per crittografia e decrittografia.
Suggerimento
Per lo sviluppo, usare Gestione segreti per l'archiviazione segreti sicura. Nell'ambiente di produzione prendere in considerazione un prodotto come Azure Key Vault.
Non incorporare mai una chiave direttamente nel codice sorgente. Le chiavi incorporate possono essere lette facilmente da un assembly mediante Ildasm.exe (IL Disassembler) oppure aprendo l'assembly in un editor di testo quale Blocco note.
Dopo avere usato una chiave crittografica, cancellarla dalla memoria impostando ogni byte su zero oppure chiamando il metodo Clear della classe di crittografia gestita. Le chiavi crittografiche possono essere a volte lette dalla memoria da un debugger oppure possono essere lette da un disco rigido, se viene eseguito il paging su disco della posizione di memoria.