解密xml文档的问题

Posted

技术标签:

【中文标题】解密xml文档的问题【英文标题】:Problem with decrypting xml document 【发布时间】:2010-09-13 13:41:12 【问题描述】:

我编写了一些代码来加密包含用户凭据的 XML 配置文件,以及解密该文件的代码。当我在本地机器上同时运行加密和解密时,它按预期工作。但是,当我部署程序时,只有解密代码,xml文件不会解密。我得到一个加密异常:错误数据? 这是我的代码:

    public static void Encrypt(XmlDocument Doc, string ElementToEncrypt, string EncryptionElementID, RSA Alg, string Keyname)
    
        if (Doc == null)
            throw new ArgumentNullException("Doc");
        if (ElementToEncrypt == null)
            throw new ArgumentNullException("Element to Encrypt");
        if (EncryptionElementID == null)
            throw new ArgumentNullException("EncryptionElementID");
        if (Alg == null)
            throw new ArgumentNullException("ALG");
        //specify which xml elements to encrypt
        XmlElement elementToEncrypt = Doc.GetElementsByTagName(ElementToEncrypt)[0] as XmlElement;

        if (elementToEncrypt == null)
            throw new XmlException("The specified element was not found");
        try
        
            //create session key
            RijndaelManaged sessionkey = new RijndaelManaged();
            sessionkey.KeySize = 256;

            //encrypt using Encrypted exml object and hold in byte array
            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;

            edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
            //encrypt the session key and add it encrypted key 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 data reference to encrypted key

            ek.AddReference(dRef);
            //Add the encrypted key to the
            // EncryptedData object.

            edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));

         // Create a new KeyInfoName element.
        KeyInfoName kin = new KeyInfoName();



        // 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)
        
            throw e;
        
    


    public static string Decrypt()
    
            //create XML documentobject and load config file
            XmlDocument xmlDoc = new XmlDocument();

            try
            
                xmlDoc.Load("config.xml");
            
            catch (FileNotFoundException e)
            
                Console.WriteLine(e.Message);
                Console.ReadLine();

            
            catch (Exception e)
            
                Console.WriteLine(e.Message);
                Console.ReadLine();
            

            //create container for key
            CspParameters cspParam = new CspParameters();
            cspParam.KeyContainerName = "XML_RSA_FTP_KEY";
            cspParam.Flags = CspProviderFlags.UseMachineKeyStore;
            //create key and store in container
            RSACryptoServiceProvider ftpkey = new RSACryptoServiceProvider(cspParam);


            //add keyname mapping qnd decrypt the document
            EncryptedXml exml = new EncryptedXml(xmlDoc);
            exml.AddKeyNameMapping("ftpkey", ftpkey);
            exml.DecryptDocument();

            //pass decrypted document to extract credentials method
            string details =  Extract_Credentials(xmlDoc);

            //return decrypted log in details
            return details;

    

任何帮助将不胜感激。谢谢,达伦

【问题讨论】:

【参考方案1】:

我将您的加密函数更改为不传入 RSA Alg,而是使用字符串 Keyname 参数创建 RSACryptoServiceProvider rsaAlg,这应该与 KeyContainerName 的解密中使用的字符串相同,“XML_RSA_FTP_KEY”

尝试在另一台 PC 上解密时,解密函数抛出“Bad Data”异常的原因是 CspParameters 链接到运行加密的 PC 上的会话。

需要在 XML 中嵌入和加密 cspParams 对象才能在另一台 PC 上启用解密。幸运的是,我们可以使用 EncryptionProperty。

public static void Encrypt(XmlDocument Doc, string ElementToEncrypt, string EncryptionElementID, string Keyname)
    
        if (Doc == null)
            throw new ArgumentNullException("Doc");
        if (ElementToEncrypt == null)
            throw new ArgumentNullException("Element to Encrypt");
        if (EncryptionElementID == null)
            throw new ArgumentNullException("EncryptionElementID");

        // Create a CspParameters object and specify the name of the key container.
        var cspParams = new CspParameters  KeyContainerName = Keyname ; //"XML_RSA_FTP_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.
        var rsaAlg = new RSACryptoServiceProvider(cspParams);

        //specify which xml elements to encrypt
        XmlElement elementToEncrypt = Doc.GetElementsByTagName(ElementToEncrypt)[0] as XmlElement;

        if (elementToEncrypt == null)
            throw new XmlException("The specified element was not found");
        try
        
            //create session key
            RijndaelManaged sessionkey = new RijndaelManaged();
            sessionkey.KeySize = 256;

            //encrypt using Encrypted exml object and hold in byte array
            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;

            edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
            //encrypt the session key and add it encrypted key element
            EncryptedKey ek = new EncryptedKey();

            byte[] encryptedKey = EncryptedXml.EncryptKey(sessionkey.Key, rsaAlg, 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 data reference to encrypted key

            ek.AddReference(dRef);
            //Add the encrypted key to the
            // EncryptedData object.

            edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));

            // Save some more information about the key using the EncryptionProperty element.

            // Create a new "EncryptionProperty" XmlElement object. 
            var property = new XmlDocument().CreateElement("EncryptionProperty", EncryptedXml.XmlEncNamespaceUrl);

            // Set the value of the EncryptionProperty" XmlElement object.
            property.InnerText = RijndaelManagedEncryption.EncryptRijndael(Convert.ToBase64String(rsaAlg.ExportCspBlob(true)),
                            "Your Salt string here");

            // Create the EncryptionProperty object using the XmlElement object. 
            var encProperty = new EncryptionProperty(property);

            // Add the EncryptionProperty object to the EncryptedKey object.
            ek.AddProperty(encProperty);

            // Create a new KeyInfoName element.
            KeyInfoName kin = new KeyInfoName();



            // 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)
        
            throw;
        
    

    public static string Decrypt()
    
        //create XML documentobject and load config file
        XmlDocument xmlDoc = new XmlDocument();

        try
        
            xmlDoc.Load("config.xml");
        
        catch (FileNotFoundException e)
        
            Console.WriteLine(e.Message);
            Console.ReadLine();

        
        catch (Exception e)
        
            Console.WriteLine(e.Message);
            Console.ReadLine();
        

        //create container for key
        CspParameters cspParam = new CspParameters();
        cspParam.KeyContainerName = "XML_RSA_FTP_KEY";
        cspParam.Flags = CspProviderFlags.UseMachineKeyStore;
        //create key and store in container
        RSACryptoServiceProvider ftpkey = new RSACryptoServiceProvider(cspParam);

        var keyInfo = xmlDoc.GetElementsByTagName("EncryptionProperty")[0].InnerText;
        ftpkey.ImportCspBlob(
            Convert.FromBase64String(RijndaelManagedEncryption.DecryptRijndael(keyInfo,
                "Your Salt string here")));

        //add keyname mapping qnd decrypt the document
        EncryptedXml exml = new EncryptedXml(xmlDoc);
        exml.AddKeyNameMapping("ftpkey", ftpkey);
        exml.DecryptDocument();

        //pass decrypted document to extract credentials method
        string details = Extract_Credentials(xmlDoc);

        //return decrypted log in details
        return details;

    

查看here 的 RijndaelManagedEncryption 类。

【讨论】:

【参考方案2】:

显而易见的问题是您如何将 XML_RSA_FTP_KEY 私钥移至服务器。

如果您没有做任何事情,decrypt()-方法将在 XML_RSA_FTP_KEY-container 中生成一个新的密钥对。此密钥将无法解密使用不同密钥加密的数据并给出“错误数据”异常。

【讨论】:

感谢您的回复。除了我的代码中的内容之外,我没有对私钥做任何事情。这是我第一次使用加密/解密,我正在努力完成最后一部分。如果您能指出我需要做什么的正确方向,我将不胜感激?基本上,XMl 文件和解密代码将作为更大系统的一部分捆绑到 7 个不同的远程站点。每个站点都应该能够使用该代码解密文件。我将如何获得他们的私钥?再次感谢。 您现在面临与世界上所有其他软件公司相同的问题。这是 DRM 出现的两难境地:P 我真的很想看看这个问题的答案。 一种仔细检查它以创建测试工具的方法,例如带有文本框和解密/加密按钮的 Windows 窗体,这将允许您以简单的方式远程测试服务器上的加密测试方式...帮助不大,但可以为您指明正确的方向。【参考方案3】:

我在使用 EncryptedXml 类 X.509 证书时收到了同样的错误,并且我忘记向解密过程的进程/任务所有者授予对证书私钥的访问权限。所以,不要忘记授予对私钥的访问权限!

我知道,当您在使用自定义/共享 RSA-CSP 密钥加密的网络场中的所有服务器之间共享 web.config 文件时,您还需要将容器中的密钥共享给所有将需要解密 web.config 中的密文。从场中每台服务器上的容器中导入密钥后,您需要授予对 asp.net 应用程序在 IIS 中运行的应用程序池标识的访问权限。请参阅 aspnet_regiis.exe 工具的 -pc、-px、-pi 和 -pa 参数,了解如何分别创建、导出、导入和授权访问 RSA 密钥 (http://msdn.microsoft.com/en-us/library/k6h9cz8h.ASPX)。这是另一个很好的资源:@​​987654322@。

为了使这在实践中更具确定性,我在域服务帐户下的 IIS 中运行我的应用程序,创建了一个自定义共享 RSA 密钥,在我的网络场中导入,授予对域服务帐户密钥的访问权限,并且然后使用特定密钥加密 web.config 的敏感部分(请参阅 aspnet_regiis.exe 上的 -pef 和 -pdf 参数)。

如果您正在加密应用程序设置,您可能需要考虑创建自定义 app.config/web.config 部分(如果您不热衷于加密所有“appSettings”)。然后,使用 aspnet_regiis.exe 使用 Right 密钥对其进行加密。最后,在您的部署过程中分发受保护的 web.config(您可能会将加密的 web.config 与您的应用程序一起打包)。透明地加密/解密 app.config 和 web.config 部分的内置 configProtectionProvider 非常方便。

以下是文件中此类加密部分的示例:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

 <configSections>
    <section name="secureAppSettings" type="System.Configuration.NameValueSectionHandler"/>
 </configSections>

  <configProtectedData>
    <providers>
      <add name="MyKeyProvider"
           type="System.Configuration.RsaProtectedConfigurationProvider, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"
           keyContainerName="MyKey"
           useMachineContainer="true" />
    </providers>
  </configProtectedData>

  <secureAppSettings configProtectionProvider="MyKeyProvider">
    <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
      xmlns="http://www.w3.org/2001/04/xmlenc#">
      <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
      <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
        <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
          <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
          <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <KeyName>Rsa Key</KeyName>
          </KeyInfo>
          <CipherData>
            <CipherValue>deadbeef</CipherValue>
          </CipherData>
        </EncryptedKey>
      </KeyInfo>
      <CipherData>
        <CipherValue>cafef00d</CipherValue>
      </CipherData>
    </EncryptedData>
  </secureAppSettings>

</configuration>

如您所见,开箱即用的配置保护使用相同的 XML EncryptedData 框架,但只是为您完成所有加密工作。这对于 Windows 服务和桌面应用程序的工作方式相同,前提是您正确授予对私钥的访问权限。

【讨论】:

以上是关于解密xml文档的问题的主要内容,如果未能解决你的问题,请参考以下文章

关于 manifest.xml 文件的开放文档规范

在 java 中解析非常大的 XML 文档(以及更多)

如何使用 X.509 证书正确加密/解密 XMl?

无法在 .Net Core 中使用 RSA 私钥解密连接字符串

C#.NET 在一台机器上加密 XML 并在另一台机器上解密

怎样解密加密文件