Практическое руководство. Шифрование XML-элементов с помощью асимметричного ключа
Обновлен: Ноябрь 2007
Классы пространства имен System.Security.Cryptography.Xml могут использоваться для шифрования элементов в XML-документе. Шифрование XML-данных является стандартным способом обмена и хранения XML-данных, защищающим их от несанкционированного прочтения. Дополнительные сведения о стандарте шифрования XML-данных см. в спецификации консорциума W3C по шифрованию XML-данных, расположенной на веб-узле по адресу: http://www.w3.org/TR/xmldsig-core/.
С помощью шифрования XML-данных можно заменить любой XML-элемент или документ элементом EncryptedData, содержащим зашифрованные XML-данные. Элемент EncryptedData может также содержать вложенные элементы, содержащие сведения о ключах и операциях, использовавшихся при шифровании. Шифрование XML-данных позволяет производить шифрование нескольких элементов внутри одного документа, а также многократное шифрование одного элемента. В следующем примере кода демонстрируется создание элемента EncryptedData, а также нескольких вложенных элементов, которые могут быть использованы в дальнейшем при шифровании.
В этом примере шифрование XML-элемента производится с использованием двух ключей. Создается пара ключей, состоящая из открытого и закрытого ключа RSA, которая сохраняется в безопасный контейнер ключа. Далее в примере создается отдельный ключ сеанса с использованием алгоритма AES, также известного как алгоритм Rijndael. В этом примере ключ сеанса AES используется для шифрования XML-документа с последующим использованием открытого ключа RSA для шифрования ключа сеанса AES. Наконец зашифрованный ключ сеанса AES и зашифрованные XML-данные сохраняются в XML-документе в новом элементе EncryptedData.
Для расшифровки XML-элемента из контейнера ключа извлекается закрытый ключ RSA, который затем используется для шифрования ключа сеанса. Ключ сеанса далее используется для расшифровки документа. Дополнительные сведения о способах расшифровки XML-элементов, зашифрованных с помощью данной процедуры, см. в разделе Практическое руководство. Расшифровывание XML-элементов с помощью асимметричного ключа.
Этот пример применим в том случае, если необходимо обеспечить общий доступ к зашифрованным данным со стороны нескольких приложений, или если приложение должно сохранять зашифрованные данные между запусками.
Шифрование XML-элемента с помощью асимметричного ключа
Создайте объект CspParameters и укажите имя контейнера ключа.
Dim cspParams As New CspParameters() cspParams.KeyContainerName = "XML_ENC_RSA_KEY"
CspParameters cspParams = new CspParameters(); cspParams.KeyContainerName = "XML_ENC_RSA_KEY";
Создайте симметричный ключ, используя класс RSACryptoServiceProvider. Ключ автоматически сохраняется в контейнере ключа при передаче объекта CspParameters конструктору класса RSACryptoServiceProvider. Этот ключ будет использоваться для шифрования ключа сеанса AES и может быть извлечен в дальнейшем для его расшифровки.
Dim rsaKey As New RSACryptoServiceProvider(cspParams)
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
Создайте объект XmlDocument путем загрузки XML-файла с диска. Объект XmlDocument содержит XML-элемент, подлежащий шифрованию.
' 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 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); }
Найдите указанный элемент в объекте XmlDocument и создайте новый объект XmlElement, который будет представлять элемент, подлежащий шифрованию. В этом примере производится шифрование элемента "creditcard".
Dim elementToEncrypt As XmlElement = Doc.GetElementsByTagName(ElementToEncryptName)(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
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"); }
Создайте новый ключ сеанса с помощью класса RijndaelManaged. Этот ключ будет использоваться для шифрования XML-элемента, а затем будет сам зашифрован и помещен в XML-документ.
' Create a 256 bit Rijndael key. sessionKey = New RijndaelManaged() sessionKey.KeySize = 256
// Create a 256 bit Rijndael key. sessionKey = new RijndaelManaged(); sessionKey.KeySize = 256;
Создайте новый экземпляр класса EncryptedXml и используйте его для шифрования указанного элемента с помощью ключа сеанса. Метод EncryptData возвращает зашифрованный элемент в виде массива шифрованных байтов.
Dim eXml As New EncryptedXml() Dim encryptedElement As Byte() = eXml.EncryptData(elementToEncrypt, sessionKey, False)
EncryptedXml eXml = new EncryptedXml(); byte[] encryptedElement = eXml.EncryptData(elementToEncrypt, sessionKey, false);
Создайте объект EncryptedData и разместите в нем URL-идентификатор зашифрованного XML-элемента. URL-идентификатор сообщает расшифровывающей стороне о том, что XML-документ содержит зашифрованный элемент. Для указания URL-идентификатора может использоваться поле XmlEncElementUrl. XML-элемент в формате простого текста будет замещен элементом EncrypotedData, инкапсулированным в объекте EncryptedData.
Dim edElement As New EncryptedData() edElement.Type = EncryptedXml.XmlEncElementUrl edElement.Id = EncryptionElementID
EncryptedData edElement = new EncryptedData(); edElement.Type = EncryptedXml.XmlEncElementUrl; edElement.Id = EncryptionElementID;
Создайте объект EncryptionMethod, инициализируемый с помощью URL-идентификатора криптографического алгоритма, использовавшегося для создания ключа сеанса. Передайте объект EncryptionMethod свойству EncryptionMethod.
edElement.EncryptionMethod = New EncryptionMethod(EncryptedXml.XmlEncAES256Url)
edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
Создайте объект EncryptedKey, который будет содержать зашифрованный ключ сеанса. Зашифруйте ключ сеанса, добавьте его в объект EncryptedKey и введите имя ключа сеанса и URL-идентификатор ключа.
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)
EncryptedKey ek = new EncryptedKey(); byte[] encryptedKey = EncryptedXml.EncryptKey(sessionKey.Key, Alg, false); ek.CipherData = new CipherData(encryptedKey); ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url);
Создайте новый объект DataReference, с помощью которого зашифрованные данные будут сопоставляться с определенным ключом сеанса. Эта необязательная операция позволяет легко указать различные части XML-документа, зашифрованные с помощью одного ключа.
Dim dRef As New DataReference() ' Specify the EncryptedData URI. dRef.Uri = "#" + EncryptionElementID ' Add the DataReference to the EncryptedKey. ek.AddReference(dRef)
DataReference dRef = new DataReference(); // Specify the EncryptedData URI. dRef.Uri = "#" + EncryptionElementID; // Add the DataReference to the EncryptedKey. ek.AddReference(dRef);
Добавьте зашифрованный ключ в объект EncryptedData.
edElement.KeyInfo.AddClause(New KeyInfoEncryptedKey(ek))
edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));
Создайте новый объект KeyInfo, чтобы указать имя ключа RSA. Добавьте его в объект EncryptedData. Это позволяет расшифровывающей стороне правильно определить асимметричный ключ, который необходимо использовать для расшифровки ключа сеанса.
' Create a new KeyInfo element. edElement.KeyInfo = New KeyInfo() ' 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)
// Create a new KeyInfo element. edElement.KeyInfo = new KeyInfo(); // 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);
Добавьте зашифрованные данные элемента в объект EncryptedData.
edElement.CipherData.CipherValue = encryptedElement
edElement.CipherData.CipherValue = encryptedElement;
Замените элемент из исходного объекта XmlDocument элементом EncryptedData.
EncryptedXml.ReplaceElement(elementToEncrypt, edElement, False)
EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
Сохраните объект XmlDocument.
xmlDoc.Save("test.xml")
xmlDoc.Save("test.xml");
Пример
Imports System
Imports System.Xml
Imports System.Security.Cryptography
Imports System.Security.Cryptography.Xml
Module Program
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 encryped 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)
Catch e As Exception
Console.WriteLine(e.Message)
Finally
' Clear the RSA key.
rsaKey.Clear()
End Try
Console.ReadLine()
End Sub
Sub Encrypt(ByVal Doc As XmlDocument, ByVal ElementToEncryptName As String, ByVal EncryptionElementID As String, ByVal Alg As RSA, ByVal KeyName As String)
' Check the arguments.
If Doc Is Nothing Then
Throw New ArgumentNullException("Doc")
End If
If ElementToEncryptName Is Nothing Then
Throw New ArgumentNullException("ElementToEncrypt")
End If
If EncryptionElementID Is Nothing Then
Throw New ArgumentNullException("EncryptionElementID")
End If
If Alg Is Nothing Then
Throw New ArgumentNullException("Alg")
End If
If KeyName Is Nothing Then
Throw New ArgumentNullException("KeyName")
End If
''''''''''''''''''''''''''''''''''''''''''''''''''
' Find the specified element in the XmlDocument
' object and create a new XmlElemnt object.
''''''''''''''''''''''''''''''''''''''''''''''''''
Dim elementToEncrypt As XmlElement = Doc.GetElementsByTagName(ElementToEncryptName)(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 RijndaelManaged = 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 a 256 bit Rijndael key.
sessionKey = New RijndaelManaged()
sessionKey.KeySize = 256
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 KeyInfo element.
edElement.KeyInfo = New KeyInfo()
' 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 e
Finally
If Not (sessionKey Is Nothing) Then
sessionKey.Clear()
End If
End Try
End Sub
End Module
using System;
using System.Xml;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
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 encryped 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);
}
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 XmlElemnt 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");
}
RijndaelManaged 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 a 256 bit Rijndael key.
sessionKey = new RijndaelManaged();
sessionKey.KeySize = 256;
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 KeyInfo element.
edElement.KeyInfo = new KeyInfo();
// 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);
}
catch(Exception e)
{
// re-throw the exception.
throw e;
}
finally
{
if (sessionKey != null)
{
sessionKey.Clear();
}
}
}
}
В этом примере предполагается, что файл с именем "test.xml" существует в том же каталоге, что и скомпилированная программа. Также предполагается, что файл "test.xml" содержит элемент "creditcard". Можно поместить следующий XML-код в файл с именем test.xml и использовать его в данном примере.
<root>
<creditcard>
<number>19834209</number>
<expiry>02/02/2002</expiry>
</creditcard>
</root>
Компиляция кода
Для компиляции этого примера может потребоваться включить ссылку на System.Security.dll.
Включите следующие пространства имен: System.Xml, System.Security.Cryptography и System.Security.Cryptography.Xml.
Безопасность
Не следует хранить симметричный криптографический ключ в виде простого текста или передавать его в таком формате между компьютерами. Кроме того, не следует хранить или передавать закрытый ключ асимметричной пары ключей в виде простого текста. Дополнительные сведения о симметричных и асимметричных криптографических ключах см. в разделе Создание ключей для шифрования и расшифровки.
Не следует внедрять ключ непосредственно в исходный код. Внедренные ключи могут быть легко прочитаны из сборки с помощью программы Дизассемблер MSIL (Ildasm.exe) или шестнадцатеричного редактора, либо путем открытия сборки в текстовом редакторе, таком как "Блокнот".
По завершении использования криптографического ключа его следует удалить из памяти, обнулив каждый байт или вызвав метод Clear управляемого криптографического класса. Иногда криптографические ключи могут считываться из памяти отладчиком или считываться с жесткого диска, если данная область памяти выгружена на диск.
См. также
Задачи
Практическое руководство. Расшифровывание XML-элементов с помощью асимметричного ключа
Ссылки
System.Security.Cryptography.Xml