Szyfrowanie elementów XML przy użyciu kluczy asymetrycznych
Klasy w System.Security.Cryptography.Xml przestrzeni nazw umożliwiają szyfrowanie elementu w dokumencie XML. Szyfrowanie XML to standardowy sposób wymiany lub przechowywania zaszyfrowanych danych XML bez obaw o łatwe odczytywanie danych. Aby uzyskać więcej informacji na temat standardu szyfrowania XML, zobacz specyfikację World Wide Web Consortium (W3C) dotyczącą szyfrowania XML znajdującego się w https://www.w3.org/TR/xmldsig-core/lokalizacji .
Szyfrowanie XML umożliwia zastąpienie dowolnego elementu XML lub dokumentu elementem zawierającym <EncryptedData>
zaszyfrowane dane XML. Element <EncryptedData>
może również zawierać elementy podrzędne, które zawierają informacje o kluczach i procesach używanych podczas szyfrowania. Szyfrowanie XML umożliwia dokumentowi zawieranie wielu zaszyfrowanych elementów i umożliwia wielokrotne szyfrowanie elementu. W przykładzie kodu w tej procedurze pokazano, jak utworzyć <EncryptedData>
element wraz z kilkoma innymi elementami podrzędnymi, których można użyć później podczas odszyfrowywania.
W tym przykładzie szyfruje element XML przy użyciu dwóch kluczy. Generuje parę kluczy publicznych/prywatnych RSA i zapisuje parę kluczy w bezpiecznym kontenerze kluczy. W tym przykładzie zostanie utworzony oddzielny klucz sesji przy użyciu algorytmu Advanced Encryption Standard (AES). W przykładzie użyto klucza sesji AES do zaszyfrowania dokumentu XML, a następnie użyto klucza publicznego RSA do zaszyfrowania klucza sesji AES. Na koniec przykład zapisuje zaszyfrowany klucz sesji AES i zaszyfrowane dane XML do dokumentu XML w ramach nowego <EncryptedData
> elementu.
Aby odszyfrować element XML, należy pobrać klucz prywatny RSA z kontenera kluczy, użyć go do odszyfrowania klucza sesji, a następnie użyć klucza sesji do odszyfrowania dokumentu. Aby uzyskać więcej informacji na temat odszyfrowywania elementu XML zaszyfrowanego przy użyciu tej procedury, zobacz Jak odszyfrować elementy XML za pomocą kluczy asymetrycznych.
Ten przykład jest odpowiedni w sytuacjach, w których wiele aplikacji musi udostępniać zaszyfrowane dane lub gdzie aplikacja musi zapisywać zaszyfrowane dane między godzinami jej uruchomienia.
Aby zaszyfrować element XML przy użyciu klucza asymetrycznego
CspParameters Utwórz obiekt i określ nazwę kontenera kluczy.
CspParameters cspParams = new CspParameters(); cspParams.KeyContainerName = "XML_ENC_RSA_KEY";
Dim cspParams As New CspParameters() cspParams.KeyContainerName = "XML_ENC_RSA_KEY"
Generowanie klucza asymetrycznego RSACryptoServiceProvider przy użyciu klasy . Klucz jest automatycznie zapisywany w kontenerze kluczy podczas przekazywania CspParameters obiektu do konstruktora RSACryptoServiceProvider klasy. Ten klucz będzie używany do szyfrowania klucza sesji AES i można go pobrać później, aby go odszyfrować.
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
Dim rsaKey As New RSACryptoServiceProvider(cspParams)
XmlDocument Utwórz obiekt, ładując plik XML z dysku. Obiekt XmlDocument zawiera element XML do zaszyfrowania.
// 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
Znajdź określony element w XmlDocument obiekcie i utwórz nowy XmlElement obiekt reprezentujący element, który chcesz zaszyfrować. W tym przykładzie
"creditcard"
element jest zaszyfrowany.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
Utwórz nowy klucz sesji przy użyciu Aes klasy . Ten klucz zaszyfruje element XML, a następnie zostanie zaszyfrowany i umieszczony w dokumencie XML.
// Create an AES key. sessionKey = Aes.Create();
' Create an AES key. sessionKey = Aes.Create()
Utwórz nowe wystąpienie EncryptedXml klasy i użyj go do zaszyfrowania określonego elementu przy użyciu klucza sesji. Metoda EncryptData zwraca zaszyfrowany element jako tablicę zaszyfrowanych bajtów.
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)
Skonstruuj EncryptedData obiekt i wypełnij go identyfikatorem URL zaszyfrowanego elementu XML. Ten identyfikator adresu URL umożliwia osobie odszyfrowywania informacje o tym, że kod XML zawiera zaszyfrowany element. Możesz użyć XmlEncElementUrl pola , aby określić identyfikator adresu URL. Element XML w postaci zwykłego
<EncryptedData>
tekstu zostanie zastąpiony przez element hermetyzowany przez ten EncryptedData obiekt.EncryptedData edElement = new EncryptedData(); edElement.Type = EncryptedXml.XmlEncElementUrl; edElement.Id = EncryptionElementID;
Dim edElement As New EncryptedData() edElement.Type = EncryptedXml.XmlEncElementUrl edElement.Id = EncryptionElementID
EncryptionMethod Utwórz obiekt zainicjowany do identyfikatora adresu URL algorytmu kryptograficznego użytego do wygenerowania klucza sesji. EncryptionMethod Przekaż obiekt do EncryptionMethod właściwości .
edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
edElement.EncryptionMethod = New EncryptionMethod(EncryptedXml.XmlEncAES256Url)
Utwórz obiekt zawierający EncryptedKey zaszyfrowany klucz sesji. Szyfruj klucz sesji, dodaj go do EncryptedKey obiektu i wprowadź nazwę klucza sesji i adres URL identyfikatora klucza.
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)
Utwórz nowy DataReference obiekt, który mapuje zaszyfrowane dane na określony klucz sesji. Ten opcjonalny krok umożliwia łatwe określenie, że wiele części dokumentu XML zostało zaszyfrowanych za pomocą jednego klucza.
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)
Dodaj zaszyfrowany klucz do EncryptedData obiektu.
edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));
edElement.KeyInfo.AddClause(New KeyInfoEncryptedKey(ek))
Utwórz nowy KeyInfo obiekt, aby określić nazwę klucza RSA. Dodaj go do EncryptedData obiektu . Pomaga to firmie odszyfrowywania zidentyfikować prawidłowy klucz asymetryczny do użycia podczas odszyfrowywania klucza sesji.
// 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)
Dodaj zaszyfrowane dane elementu do EncryptedData obiektu.
edElement.CipherData.CipherValue = encryptedElement;
edElement.CipherData.CipherValue = encryptedElement
Zastąp element z oryginalnego XmlDocument obiektu elementem EncryptedData .
EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
EncryptedXml.ReplaceElement(elementToEncrypt, edElement, False)
XmlDocument Zapisz obiekt.
xmlDoc.Save("test.xml");
xmlDoc.Save("test.xml")
Przykład
W tym przykładzie przyjęto założenie, że plik o nazwie "test.xml"
istnieje w tym samym katalogu co skompilowany program. Przyjęto również założenie, że "test.xml"
zawiera "creditcard"
element . Poniższy kod XML można umieścić w pliku o nazwie test.xml
i użyć go w tym przykładzie.
<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
Kompilowanie kodu
- W projekcie przeznaczonym dla programu .NET Framework dołącz odwołanie do
System.Security.dll
elementu . - W projekcie przeznaczonym dla platformy .NET Core lub .NET 5 zainstaluj pakiet NuGet System.Security.Cryptography.Xml.
- Uwzględnij następujące przestrzenie nazw: System.Xml, System.Security.Cryptographyi System.Security.Cryptography.Xml.
Zabezpieczenia platformy .NET
Nigdy nie przechowuj symetrycznego klucza kryptograficznego w postaci zwykłego tekstu lub przetransferuj klucz symetryczny między maszynami w postaci zwykłego tekstu. Ponadto nigdy nie przechowuj ani nie przesyłaj klucza prywatnego pary kluczy asymetrycznych w postaci zwykłego tekstu. Aby uzyskać więcej informacji na temat symetrycznych i asymetrycznych kluczy kryptograficznych, zobacz Generowanie kluczy szyfrowania i odszyfrowywania.
Napiwek
Na potrzeby programowania użyj programu Secret Manager do bezpiecznego magazynu wpisów tajnych. W środowisku produkcyjnym rozważ produkt podobny do usługi Azure Key Vault.
Nigdy nie osadzaj klucza bezpośrednio w kodzie źródłowym. Klucze osadzone można łatwo odczytać z zestawu przy użyciu Ildasm.exe (dezasembler IL) lub otwierając zestaw w edytorze tekstów, takim jak Notatnik.
Gdy skończysz używać klucza kryptograficznego, wyczyść go z pamięci, ustawiając każdy bajt na zero lub wywołując Clear metodę zarządzanej klasy kryptografii. Klucze kryptograficzne mogą być czasami odczytywane z pamięci przez debuger lub odczytywany z dysku twardego, jeśli lokalizacja pamięci jest stronicowana na dysk.