Share via


HOW TO:使用非對稱金鑰加密 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 公開/私密金鑰組,並將此金鑰組儲存在安全的金鑰容器內。 接下來,這個範例會使用 Advanced Encryption Standard (AES) 演算法 (也稱為 Rijndael 演算法),另外建立一個工作階段金鑰。 此範例會使用 AES 工作階段金鑰加密 XML 文件,然後使用 RSA 公開金鑰 (Public Key) 加密 AES 工作階段金鑰。 最後,它會將加密的 AES 工作階段金鑰和加密的 XML 資料放在新的 <EncryptedData> 項目中,並儲存到 XML 文件內。

若要解密 XML 項目,您必須從金鑰容器擷取 RSA 私密金鑰、用此私密金鑰解密工作階段金鑰,再使用這個工作階段金鑰來解密文件。 如需有關如何解密以此程序加密之 XML 項目的詳細資訊,請參閱 HOW TO:使用非對稱金鑰解密 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 類別的建構函式 (Constructor) 時,金鑰便會自動儲存至金鑰容器中。 這個金鑰將可以用來加密 AES 工作階段金鑰,並在稍後擷取來進行解密。

    Dim rsaKey As New RSACryptoServiceProvider(cspParams)
    
    RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
    
  3. 從磁碟載入 XML 檔案,建立 XmlDocument 物件。 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 物件,並在其中填入加密 XML 項目的 URL 識別項。 這個 URL 識別項能讓解密的一方得知 XML 中含有加密的項目。 您可以使用 XmlEncElementUrl 欄位指定 URL 識別項。 純文字的 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 反組譯工具) 或在文字編輯器 (例如記事本) 中開啟組件,可以輕易從組件讀取內嵌金鑰。

當您完成使用密碼編譯金鑰時,請將每個位元組設為零,或呼叫 Managed 加密類別的 Clear 方法,從記憶體清除密碼編譯金鑰。 密碼編譯金鑰有時可以透過偵錯工具從記憶體讀取,或者如果記憶體位置被分頁至磁碟,則可以從硬碟讀取。

請參閱

工作

HOW TO:使用非對稱金鑰解密 XML 項目

參考

System.Security.Cryptography.Xml

其他資源

XML 加密和數位簽章