RSA 加密公钥未从容器返回?

发布于 2024-07-18 04:52:11 字数 10192 浏览 15 评论 0原文

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

这是一个完整的代码片段来测试我想要做的事情:

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!

正如您可以想象的那样,您无法使用一组密钥加密某些内容,然后尝试使用一组全新的密钥进行解密!

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

I feel like what I am trying to do is very simple. But for some reason it doesn't want to work:

Here is a complete code snippet to test what I am trying to do:

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

        }

    }
}

This seems to work fine as long as I am saving/getting an RSACryptoServiceProvider with both a private and public key. Once I save a RSACryptoServiceProvider with JUST a public key, the next time I try to retrieve it all I get is a NEW and DIFFERENT RSACryptoServiceProvider!

As you can imagine, you cant encrypt something with one set of keys, and then try to decrypt with a whole new set!

Any ideas on why this is happening? or what the correct way would be to store a public-only key?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

转瞬即逝 2024-07-25 04:52:11

我有一个非常类似的问题。

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

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

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

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

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

I had a very similar question.

I'm now almost certain that the key containers cannot be used to store public keys. Their primary purpose appears to be for storing key pairs. The key container does only store the key that is originally generated, and importing a PublicOnly key affects only the instance and not the storage.

The "How to: Store Asymmetric Keys in a Key Container" page of the .NET Developer's Guide states that

If you need to store a private key, you should use a key container

... which is about as clear a a statement as I've been able to find through MSDN.

The substitute mechanism I used was to store the key in an XML file (since it's a public key it shouldn't matter if it's easily visible), with permissions set using File System Access Rules to prevent unwanted modification.

陌上青苔 2024-07-25 04:52:11

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

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

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

My understanding is that your call to ImportParameters in SaveKeyToContainer is not affecting the key in the store. Your implementation of SaveKeyToContainer is initializing an instance of RSACryptoServiceProvider using the key in the store (generating a new key pair when the container does not exist) and then importing the parameters, which affects the instance and not the store.

When you retrieve publicKeyContainer later, you are given the new key pair that was generated when you attempted to save it and not the imported public fragment.

Sorry, I can't help with any details about importing the key into the store with the Cryptography API. I believe the store only supports key pairs, i.e. don't expect to be able to import just the public key.

风吹雪碎 2024-07-25 04:52:11

.NET 加密类的文档非常贫乏。

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

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

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

The documentation for the .NET Crypto Classes is very poor.

I have being beating my brains out with the same issue and have come to the same conclusion even though it is not stated for definite in the docs.

When you create an instance of the the RSA provider, you get a new key pair.
If you provide a parameter object and name a key container, the new key pair is stored there.

If you import a public key, it does not get persisted!

Dan

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文