Поделиться через


Практическое руководство. Шифрование XML-элементов с помощью асимметричного ключа

Классы пространства имен 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-элемента с помощью асимметричного ключа

  1. Создайте объект CspParameters и укажите имя контейнера ключа.

    Dim cspParams As New CspParameters()
    cspParams.KeyContainerName = "XML_ENC_RSA_KEY"
    
    CspParameters cspParams = new CspParameters();
    cspParams.KeyContainerName = "XML_ENC_RSA_KEY";
    
  2. Создайте симметричный ключ, используя класс RSACryptoServiceProvider. Ключ автоматически сохраняется в контейнере ключа при передаче объекта CspParameters конструктору класса RSACryptoServiceProvider. Этот ключ будет использоваться для шифрования ключа сеанса AES и может быть извлечен в дальнейшем для его расшифровки.

    Dim rsaKey As New RSACryptoServiceProvider(cspParams)
    
    RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
    
  3. Создайте объект 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);
    }
    
  4. Найдите указанный элемент в объекте XmlDocument и создайте новый объект XmlElement, который будет представлять элемент, подлежащий шифрованию. В этом примере производится шифрование элемента "creditcard".

    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
    
    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");
    
    }
    
  5. Создайте новый ключ сеанса с помощью класса 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;
    
  6. Создайте новый экземпляр класса 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);
    
  7. Создайте объект EncryptedData и разместите в нем URL-идентификатор зашифрованного XML-элемента. URL-идентификатор сообщает расшифровывающей стороне о том, что XML-документ содержит зашифрованный элемент. Для указания URL-идентификатора может использоваться поле XmlEncElementUrl. XML-элемент в формате простого текста будет замещен элементом EncryptedData, инкапсулированным в объекте EncryptedData.

    Dim edElement As New EncryptedData()
    edElement.Type = EncryptedXml.XmlEncElementUrl
    edElement.Id = EncryptionElementID
    
    EncryptedData edElement = new EncryptedData();
    edElement.Type = EncryptedXml.XmlEncElementUrl;
    edElement.Id = EncryptionElementID;
    
  8. Создайте объект EncryptionMethod, инициализируемый с помощью URL-идентификатора криптографического алгоритма, использовавшегося для создания ключа сеанса. Передайте объект EncryptionMethod свойству EncryptionMethod.

    edElement.EncryptionMethod = New EncryptionMethod(EncryptedXml.XmlEncAES256Url)
    
    edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
    
  9. Создайте объект 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);
    
  10. Создайте новый объект 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);
    
  11. Добавьте зашифрованный ключ в объект EncryptedData.

    edElement.KeyInfo.AddClause(New KeyInfoEncryptedKey(ek))
    
    edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));
    
  12. Создайте новый объект KeyInfo, чтобы указать имя ключа RSA. Добавьте его в объект EncryptedData. Это позволяет расшифровывающей стороне правильно определить асимметричный ключ, который необходимо использовать для расшифровки ключа сеанса.

    ' 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 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);
    
  13. Добавьте зашифрованные данные элемента в объект EncryptedData.

    edElement.CipherData.CipherValue = encryptedElement
    
    edElement.CipherData.CipherValue = encryptedElement;
    
  14. Замените элемент из исходного объекта XmlDocument элементом EncryptedData.

    EncryptedXml.ReplaceElement(elementToEncrypt, edElement, False)
    
    EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
    
  15. Сохраните объект XmlDocument.

    xmlDoc.Save("test.xml")
    
    xmlDoc.Save("test.xml");
    

Пример

Imports System
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 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)
            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 'Main


    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.
        If Doc Is Nothing Then
            Throw New ArgumentNullException("Doc")
        End If
        If EncryptionElement Is Nothing Then
            Throw New ArgumentNullException("EncryptionElement")
        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(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 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 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 'Encrypt



    Public Shared Sub Decrypt(ByVal Doc As XmlDocument, ByVal Alg As RSA, ByVal KeyName As String)
        ' Check the arguments.  
        If Doc Is Nothing Then
            Throw New ArgumentNullException("Doc")
        End If
        If Alg Is Nothing Then
            Throw New ArgumentNullException("Alg")
        End If
        If KeyName Is Nothing Then
            Throw New ArgumentNullException("KeyName")
        End If
        ' 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 'Decrypt 
End Class 'Program



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);
            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 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 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();
            }

        }

    }

    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();

    }

}

В этом примере предполагается, что файл с именем "test.xml" существует в том же каталоге, что и скомпилированная программа. Также предполагается, что файл "test.xml" содержит элемент "creditcard". Можно поместить следующий XML-код в файл с именем test.xml и использовать его в данном примере.

<root>
    <creditcard>
        <number>19834209</number>
        <expiry>02/02/2002</expiry>
    </creditcard>
</root>

Компиляция кода

Безопасность

Не следует хранить симметричный криптографический ключ в виде простого текста или передавать его в таком формате между компьютерами. Кроме того, не следует хранить или передавать закрытый ключ асимметричной пары ключей в виде простого текста. Дополнительные сведения о симметричных и асимметричных криптографических ключах см. в разделе Создание ключей для шифрования и расшифровки.

Не следует внедрять ключ непосредственно в исходный код. Внедренные ключи могут быть легко прочитаны из сборки с помощью программы Ildasm.exe (дизассемблер MSIL) или шестнадцатеричного редактора, либо путем открытия сборки в текстовом редакторе, таком как "Блокнот".

По завершении использования криптографического ключа его следует удалить из памяти, обнулив каждый байт или вызвав метод Clear управляемого криптографического класса. Иногда криптографические ключи могут считываться из памяти отладчиком или считываться с жесткого диска, если данная область памяти выгружена на диск.

См. также

Задачи

Практическое руководство. Расшифровывание XML-элементов с помощью асимметричного ключа

Ссылки

System.Security.Cryptography.Xml

Другие ресурсы

Шифрование и цифровые подписи в XML