【问题标题】:RSA Encryption public key not returned from container?RSA加密公钥未从容器返回?
【发布时间】:2010-10-24 01:45:50
【问题描述】:

我觉得我想做的事情很简单。但由于某种原因它不想工作:

这是一个完整的代码 sn-p 来测试我正在尝试做的事情:

using System;
using System.Xml;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;

namespace XmlCryptographySendingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string fullKeyContainer = "fullKeyContainer";
            string publicKeyContainer = "publicKeyContainer";
        //create the two providers
        RSACryptoServiceProvider serverRSA = GetKeyFromContainer(fullKeyContainer);

        //save public and full key pairs
        SaveKeyToContainer(fullKeyContainer, serverRSA.ExportParameters(true));
        SaveKeyToContainer(publicKeyContainer, serverRSA.ExportParameters(false));

        //get rid of them from memory
        serverRSA.Clear();
        serverRSA = null;
        GC.Collect();

        //retrieve a full server set and a private client set
        serverRSA = GetKeyFromContainer(fullKeyContainer);
        RSACryptoServiceProvider clientRSA = GetKeyFromContainer(publicKeyContainer);

        //at this point the public key should be the same for both RSA providers
        string clientPublicKey = clientRSA.ToXmlString(false);
        string serverPublicKey = serverRSA.ToXmlString(false);

        if (clientPublicKey.Equals(serverPublicKey))
        {//they have the same public key.

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

            //we can encypt with the clientRSA using the public key
            Encrypt(xmlDoc, "Fields", "DataFields", clientRSA, "test");

            Console.WriteLine("Encrypted: \r\n" + xmlDoc.OuterXml);

            //and should be able to decrypt with the serverRSA using the private key
            Decrypt(xmlDoc, serverRSA, "test");

            Console.WriteLine("Decrypted : \r\n" + xmlDoc.OuterXml);
        }
        else
        {
            Console.WriteLine("The two RSA have different public keys...");
        }

        Console.ReadLine();
    }



    private static CspParameters GetCspParameters(string containerName)
    {
        // Create the CspParameters object and set the key container 
        // name used to store the RSA key pair.
        CspParameters tmpParameters = new CspParameters();
        tmpParameters.Flags = CspProviderFlags.UseMachineKeyStore; //use the machine key store--this allows us to use the machine level container when applications run without a logged-in user
        tmpParameters.ProviderType = 1;
        tmpParameters.KeyNumber = (int)KeyNumber.Exchange;
        tmpParameters.KeyContainerName = containerName;
        return tmpParameters;
    }


    public static void SaveKeyToContainer(string containerName, RSAParameters rsaParameters)
    {
        CspParameters tmpParameters = GetCspParameters(containerName);

        // Create a new instance of RSACryptoServiceProvider that accesses
        // the key container 
        RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(tmpParameters);

        //set the key information from the text
        rsa.ImportParameters(rsaParameters);
    }

    public static RSACryptoServiceProvider GetKeyFromContainer(string containerName)
    {
        // Create the CspParameters object and set the key container 
        // name used to store the RSA key pair.
        CspParameters tmpParameters = GetCspParameters(containerName);

        // Create a new instance of RSACryptoServiceProvider that accesses
        // the key container.
        RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(tmpParameters);

        return rsa;
    }

    public static void DeleteKeyFromContainer(string containerName)
    {
        // Create the CspParameters object and set the key container 
        // name used to store the RSA key pair.
        CspParameters tmpParameters = GetCspParameters(containerName);

        // Create a new instance of RSACryptoServiceProvider that accesses
        // the key container.
        RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(tmpParameters);

        // Delete the key entry in the container.
        rsa.PersistKeyInCsp = false;

        // Call Clear to release resources and delete the key from the container.
        rsa.Clear();
    }



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

        }

    }
}

只要我使用私钥和公钥保存/获取 RSACryptoServiceProvider,这似乎工作正常。一旦我用一个公钥保存了一个 RSACryptoServiceProvider,下次我尝试检索它时,我得到的只是一个新的和不同的 RSACryptoServiceProvider!

你可以想象,你不能用一组密钥加密某些东西,然后尝试用一套全新的密钥解密!

关于为什么会发生这种情况的任何想法?或者存储公钥的正确方法是什么?

【问题讨论】:

  • 正确吗:一旦我用一个私钥保存了一个 RSACryptoServiceProvider 你是说我用一个公钥保存了一个 RSACryptoServiceProvider 后

标签: cryptography c#-2.0 rijndaelmanaged public-key rsacryptoserviceprovider


【解决方案1】:

我有a very similar question

我现在几乎可以肯定密钥容器不能用于存储公钥。它们的主要目的似乎是存储密钥对。密钥容器只存储最初生成的密钥,导入PublicOnly 密钥只影响实例而不影响存储。

.NET 开发人员指南的“如何:在密钥容器中存储非对称密钥”页面指出

如果你需要存储私钥,你应该使用密钥容器

...这是我通过 MSDN 找到的最清晰的声明。

我使用的替代机制是将密钥存储在 XML 文件中(因为它是一个公钥,所以它是否容易可见并不重要),并使用文件系统访问规则设置权限以防止不必要的修改。

【讨论】:

    【解决方案2】:

    .NET 加密类的文档很差。

    我一直在用同样的问题绞尽脑汁,并得出了相同的结论,尽管文档中没有明确说明。

    当您创建 RSA 提供程序的实例时,您会获得一个新的密钥对。 如果您提供参数对象并命名密钥容器,则新密钥对将存储在那里。

    如果您导入公钥,它不会被持久化!

    【讨论】:

      【解决方案3】:

      我的理解是,您在 SaveKeyToContainer 中对 ImportParameters 的调用不会影响存储中的密钥。您的 SaveKeyToContainer 实现正在使用存储中的密钥初始化 RSACryptoServiceProvider 的实例(当容器不存在时生成新的密钥对),然后导入参数,这影响实例而不是商店。

      当您稍后检索 publicKeyContainer 时,您将获得在您尝试保存它时生成的新密钥对,而不是导入的公共片段。

      抱歉,我无法提供有关使用 Cryptography API 将密钥导入存储的任何详细信息。我相信商店只支持密钥对,即不要指望能够只导入公钥。

      【讨论】:

      • 您可以设置一个属性来确定实例是否在存储中持久存在。我的印象是,如果将其设置为持久,那么您对 ​​RSA 密钥所做的更改也会保留在密钥库中?
      • 可以肯定地假设我的回答不准确,并且 PersistKeyInCsp 在调用 ImportParameters 时应该这样做。我很想知道您是否尝试过使用 Export/ImportCspBlob 或 To/FromXmlString 方法。你知道公钥的模数和指数在哪里不再匹配 serverKey 和 clientKey (RSAParameters) 吗?你有机会在单声道上测试代码吗?
      猜你喜欢
      • 2011-11-07
      • 1970-01-01
      • 1970-01-01
      • 2014-06-23
      • 1970-01-01
      • 1970-01-01
      • 2011-11-10
      • 2018-06-07
      相关资源
      最近更新 更多