비대칭 키를 사용하여 XML 요소 암호화

System.Security.Cryptography.Xml 네임스페이스의 클래스를 사용하여 XML 문서 내의 요소를 암호화할 수 있습니다. XML 암호화는 데이터가 쉽게 읽혀질 염려 없이 암호화된 XML 데이터를 교환하거나 저장하는 표준 방법입니다. XML 암호화 표준에 대한 자세한 내용은 https://www.w3.org/TR/xmldsig-core/에 있는 XML 암호화에 대한 W3C(World Wide Web 컨소시엄) 사양을 참조하세요.

XML 암호화를 사용하여 암호화된 XML 데이터가 포함된 <EncryptedData> 요소의 문서나 XML 요소를 대체할 수 있습니다. <EncryptedData> 요소는 암호화 중에 사용된 키와 프로세스에 대한 정보가 들어 있는 하위 요소를 포함할 수도 있습니다. XML 암호화를 사용하면 문서에 암호화된 여러 요소가 포함될 수 있고 한 요소가 여러 번 암호화될 수 있습니다. 이 절차의 코드 예에서는 나중에 암호 해독 과정에서 사용할 수 있는 다른 여러 하위 요소와 함께 <EncryptedData> 요소를 생성하는 방법을 보여 줍니다.

이 예제에서는 두 키를 사용하여 XML 요소를 암호화합니다. RSA 퍼블릭/프라이빗 키 쌍을 생성하고 보안 키 컨테이너에 키 쌍을 저장합니다. 그런 다음 AES(Advanced Encryption Standard) 알고리즘을 사용하여 별도의 세션 키를 만듭니다. 이 예제에서는 AES 세션 키를 사용하여 XML 문서를 암호화한 다음 RSA 공개 키를 사용하여 AES 세션 키를 암호화합니다. 끝으로, 예에서는 암호화된 AES 세션 키와 암호화된 XML 데이터를 XML 문서의 새로운 <EncryptedData> 요소 내에 저장합니다.

XML 요소를 암호 해독하려면 키 컨테이너에서 RSA 프라이빗 키를 검색하고 세션 키를 암호 해독하는 데 사용한 다음 세션 키를 사용하여 문서를 암호 해독합니다. 이 절차를 사용하여 암호화된 XML 요소를 암호 해독하는 방법에 대한 자세한 내용은 방법: 비대칭 키를 사용하여 XML 요소 암호 해독을 참조하세요.

이 예제는 여러 애플리케이션이 암호화된 데이터를 공유해야 하거나 애플리케이션이 실행되는 시간 사이에 암호화된 데이터를 저장해야 경우에 적합합니다.

비대칭 키를 사용하여 XML 요소를 암호화하려면

  1. CspParameters 개체를 만들고 키 컨테이너의 이름을 지정합니다.

    CspParameters cspParams = new CspParameters();
    cspParams.KeyContainerName = "XML_ENC_RSA_KEY";
    
    Dim cspParams As New CspParameters()
    cspParams.KeyContainerName = "XML_ENC_RSA_KEY"
    
  2. RSACryptoServiceProvider 클래스를 사용하여 비대칭 키를 생성합니다. RSACryptoServiceProvider 클래스의 생성자에 CspParameters 개체를 전달하면 키가 자동으로 키 컨테이너에 저장됩니다. 이 키는 AES 세션 키를 암호화하는 데 사용되며, 나중에 검색하여 암호 해독할 수 있습니다.

    RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
    
    Dim rsaKey As New RSACryptoServiceProvider(cspParams)
    
  3. 디스크에서 XML 파일을 로드하여 XmlDocument 개체를 만듭니다. XmlDocument 개체는 암호화할 XML 요소를 포함합니다.

    // 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
    
  4. XmlDocument 개체에서 지정된 요소를 찾고 암호화할 요소를 나타내는 XmlElement 개체를 새로 만듭니다. 이 예제에서는 "creditcard" 요소가 암호화됩니다.

    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
    
  5. Aes 클래스를 사용하여 새 세션 키를 만듭니다. 이 키는 XML 요소를 암호화한 다음 자체 암호화되어 XML 문서에 배치됩니다.

    // Create an AES key.
    sessionKey = Aes.Create();
    
    ' Create an AES key.
    sessionKey = Aes.Create()
    
  6. EncryptedXml 클래스의 새 인스턴스를 만들고 세션 키를 사용하여 지정된 요소를 암호화하는 데 사용합니다. EncryptData 메서드는 암호화된 요소를 암호화된 바이트 배열로 반환합니다.

    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)
    
  7. EncryptedData 개체를 생성하고 암호화된 XML 요소의 URL 식별자로 채웁니다. 이 URL 식별자를 통해 암호 해독하는 당사자가 XML에 암호화된 요소가 들어 있음을 알 수 있습니다. XmlEncElementUrl 필드를 사용하여 URL 식별자를 지정할 수 있습니다. 일반 텍스트 XML 요소는 이 EncryptedData 개체로 캡슐화된 <EncryptedData> 요소로 바뀝니다.

    EncryptedData edElement = new EncryptedData();
    edElement.Type = EncryptedXml.XmlEncElementUrl;
    edElement.Id = EncryptionElementID;
    
    Dim edElement As New EncryptedData()
    edElement.Type = EncryptedXml.XmlEncElementUrl
    edElement.Id = EncryptionElementID
    
  8. 세션 키를 생성하는 데 사용되는 암호화 알고리즘의 URL 식별자로 초기화되는 EncryptionMethod 개체를 만듭니다. EncryptionMethod 개체를 EncryptionMethod 속성에 전달합니다.

    edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
    
    edElement.EncryptionMethod = New EncryptionMethod(EncryptedXml.XmlEncAES256Url)
    
  9. 암호화된 세션 키를 포함할 EncryptedKey 개체를 만듭니다. 세션 키를 암호화하고, EncryptedKey 개체에 추가하고, 세션 키 이름 및 키 식별자 URL을 입력합니다.

    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)
    
  10. 암호화된 데이터를 특정 세션 키로 매핑하는 새로운 DataReference 개체를 만듭니다. 이 선택적 단계를 사용하면 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)
    
  11. EncryptedData 개체에 암호화된 키를 추가합니다.

    edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));
    
    edElement.KeyInfo.AddClause(New KeyInfoEncryptedKey(ek))
    
  12. 새로운 KeyInfo 개체를 만들어 RSA 키의 이름을 지정합니다. EncryptedData 개체에 추가합니다. 이렇게 하면 암호 해독하는 당사자가 세션 키를 암호 해독할 때 사용할 올바른 비대칭 키를 식별하는 데 도움이 됩니다.

    
    // 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)
    
  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")
    

예시

이 예제에서는 "test.xml"이라는 파일이 컴파일된 프로그램과 동일한 디렉터리에 있다고 가정합니다. 또한 "test.xml""creditcard" 요소가 포함되어 있다고 가정합니다. test.xml이라는 파일에 다음 XML을 배치하고 이 예제에서 사용할 수 있습니다.

<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


코드 컴파일

.NET 보안

대칭 암호화 키를 일반 텍스트로 저장하거나 컴퓨터 간에 일반 텍스트로 대칭 키를 전송하지 마세요. 또한 비대칭 키 쌍의 프라이빗 키를 일반 텍스트로 저장하거나 전송하지 마세요. 대칭 및 비대칭 암호화 키에 대한 자세한 내용은 암호화 및 암호 해독용 키 생성을 참조하세요.

개발 시 보안 비밀 스토리지를 위해 Secret Manager를 사용합니다. 프로덕션에서는 Azure Key Vault와 같은 제품을 고려합니다.

소스 코드에 직접 키를 포함하지 마세요. 포함된 키는 Ildasm.exe(IL 디스어셈블러)를 사용하거나 메모장과 같은 텍스트 편집기에서 어셈블리를 열어 어셈블리에서 쉽게 읽을 수 있습니다.

암호화 키를 사용하여 작업이 완료되면 각 바이트를 0으로 설정하거나 관리되는 암호화 클래스의 Clear 메서드를 호출하여 메모리에서 지웁니다. 디버거가 메모리에서 암호화 키를 읽거나 메모리 위치가 디스크에 페이징된 경우 하드 드라이브에서 읽을 수 있는 경우도 있습니다.

참고 항목