Šifrování elementů XML pomocí asymetrických klíčů
Třídy v System.Security.Cryptography.Xml oboru názvů můžete použít k šifrování elementu v dokumentu XML. Šifrování XML je standardní způsob výměny nebo ukládání šifrovaných dat XML, aniž byste se museli starat o snadno čitelná data. Další informace o standardu šifrování XML naleznete ve specifikaci W3C (World Wide Web Consortium) pro šifrování XML umístěné na adrese https://www.w3.org/TR/xmldsig-core/.
Šifrování XML můžete použít k nahrazení libovolného elementu XML nebo dokumentu elementem <EncryptedData>
, který obsahuje šifrovaná data XML. Prvek <EncryptedData>
může také obsahovat dílčí prvky, které obsahují informace o klíčích a procesech používaných při šifrování. Šifrování XML umožňuje dokumentu obsahovat více šifrovaných elementů a umožňuje, aby byl prvek vícekrát zašifrován. Příklad kódu v tomto postupu ukazuje, jak vytvořit <EncryptedData>
prvek spolu s několika dalšími dílčími prvky, které můžete použít později během dešifrování.
Tento příklad šifruje element XML pomocí dvou klíčů. Vygeneruje pár veřejného/privátního klíče RSA a uloží pár klíčů do zabezpečeného kontejneru klíčů. Příklad pak vytvoří samostatný klíč relace pomocí algoritmu AES (Advanced Encryption Standard). Příklad používá klíč relace AES k šifrování dokumentu XML a pak používá veřejný klíč RSA k šifrování klíče relace AES. Nakonec příklad uloží šifrovaný klíč relace AES a šifrovaná data XML do dokumentu XML v rámci nového <EncryptedData
> elementu.
K dešifrování elementu XML načtete privátní klíč RSA z kontejneru klíčů, použijete ho k dešifrování klíče relace a pak klíč relace použijete k dešifrování dokumentu. Další informace o dešifrování elementu XML, který byl zašifrován pomocí tohoto postupu, naleznete v tématu Postupy: Dešifrování elementů XML pomocí asymetrických klíčů.
Tento příklad je vhodný pro situace, kdy více aplikací potřebuje sdílet šifrovaná data nebo kde aplikace potřebuje uložit šifrovaná data mezi časy spuštění.
Šifrování elementu XML asymetrickým klíčem
Vytvořte CspParameters objekt a zadejte název kontejneru klíčů.
CspParameters cspParams = new CspParameters(); cspParams.KeyContainerName = "XML_ENC_RSA_KEY";
Dim cspParams As New CspParameters() cspParams.KeyContainerName = "XML_ENC_RSA_KEY"
Pomocí třídy vygenerujte asymetrický klíč RSACryptoServiceProvider . Klíč se automaticky uloží do kontejneru klíčů při předání CspParameters objektu konstruktoru RSACryptoServiceProvider třídy. Tento klíč se použije k šifrování klíče relace AES a později ho můžete načíst k jeho dešifrování.
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
Dim rsaKey As New RSACryptoServiceProvider(cspParams)
Vytvořte XmlDocument objekt načtením souboru XML z disku. Objekt XmlDocument obsahuje element XML, který se má zašifrovat.
// 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
Vyhledejte zadaný prvek v objektu XmlDocument a vytvořte nový XmlElement objekt představující prvek, který chcete zašifrovat. V tomto příkladu
"creditcard"
je prvek zašifrován.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
Vytvořte nový klíč relace pomocí Aes třídy. Tento klíč zašifruje element XML a pak se zašifruje a umístí do dokumentu XML.
// Create an AES key. sessionKey = Aes.Create();
' Create an AES key. sessionKey = Aes.Create()
Vytvořte novou instanci EncryptedXml třídy a použijte ji k šifrování zadaného elementu pomocí klíče relace. Metoda EncryptData vrátí šifrovaný prvek jako pole šifrovaných bajtů.
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)
EncryptedData Vytvořte objekt a naplňte ho identifikátorem ADRESY URL šifrovaného elementu XML. Tento identifikátor adresy URL umožňuje dešifrovací straně vědět, že XML obsahuje šifrovaný prvek. Pomocí pole můžete XmlEncElementUrl zadat identifikátor adresy URL. Element XML prostého
<EncryptedData>
textu bude nahrazen element zapouzdřeným tímto EncryptedData objektem.EncryptedData edElement = new EncryptedData(); edElement.Type = EncryptedXml.XmlEncElementUrl; edElement.Id = EncryptionElementID;
Dim edElement As New EncryptedData() edElement.Type = EncryptedXml.XmlEncElementUrl edElement.Id = EncryptionElementID
EncryptionMethod Vytvořte objekt, který se inicializuje na identifikátor adresy URL kryptografického algoritmu použitého k vygenerování klíče relace. EncryptionMethod Předejte objekt do EncryptionMethod vlastnosti.
edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
edElement.EncryptionMethod = New EncryptionMethod(EncryptedXml.XmlEncAES256Url)
EncryptedKey Vytvořte objekt, který bude obsahovat šifrovaný klíč relace. Zašifrujte klíč relace, přidejte ho do objektu EncryptedKey a zadejte název klíče relace a adresu URL identifikátoru klíče.
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)
Vytvořte nový DataReference objekt, který mapuje šifrovaná data na určitý klíč relace. Tento volitelný krok umožňuje snadno určit, že jeden klíč zašifroval více částí dokumentu XML.
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)
Přidejte k objektu EncryptedData šifrovaný klíč.
edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));
edElement.KeyInfo.AddClause(New KeyInfoEncryptedKey(ek))
Vytvořte nový KeyInfo objekt pro zadání názvu klíče RSA. Přidejte ho do objektu EncryptedData . To pomáhá dešifrovací straně identifikovat správný asymetrický klíč, který se má použít při dešifrování klíče relace.
// 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)
Přidejte do objektu šifrovaná data elementu EncryptedData .
edElement.CipherData.CipherValue = encryptedElement;
edElement.CipherData.CipherValue = encryptedElement
Nahraďte prvek z původního XmlDocument objektu elementem EncryptedData .
EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
EncryptedXml.ReplaceElement(elementToEncrypt, edElement, False)
XmlDocument Uložte objekt.
xmlDoc.Save("test.xml");
xmlDoc.Save("test.xml")
Příklad
Tento příklad předpokládá, že soubor s názvem "test.xml"
existuje ve stejném adresáři jako zkompilovaný program. Předpokládá se také, že "test.xml"
obsahuje "creditcard"
prvek. Následující kód XML můžete umístit do souboru volaný test.xml
a použít ho v tomto příkladu.
<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
Zkompilování kódu
- V projektu, který cílí na rozhraní .NET Framework, uveďte odkaz na
System.Security.dll
. - V projektu, který cílí na .NET Core nebo .NET 5, nainstalujte balíček NuGet System.Security.Cryptography.Xml.
- Zahrňte následující obory názvů: System.Xml, System.Security.Cryptographya System.Security.Cryptography.Xml.
Zabezpečení .NET
Nikdy neukládejte symetrický kryptografický klíč v prostém textu ani nepřeneste symetrický klíč mezi počítači v prostém textu. Kromě toho nikdy neukládáte ani nepřeneste privátní klíč asymetrického páru klíčů ve formátu prostého textu. Další informace o symetrických a asymetrických kryptografických klíčích najdete v tématu Generování klíčů pro šifrování a dešifrování.
Tip
Pro vývoj použijte Správce tajných kódů pro zabezpečené úložiště tajných kódů. V produkčním prostředí zvažte produkt, jako je Azure Key Vault.
Nikdy nevkládejte klíč přímo do zdrojového kódu. Vložené klíče lze snadno číst ze sestavení pomocí Ildasm.exe (IL Disassembler) nebo otevřením sestavení v textovém editoru, jako je Poznámkový blok.
Až budete hotovi pomocí kryptografického klíče, vymažte ho z paměti nastavením každého bajtu na nulu nebo voláním Clear metody spravované kryptografické třídy. Kryptografické klíče můžou někdy číst z paměti ladicí program nebo číst z pevného disku, pokud je umístění paměti stránkované na disk.