RSA加密公钥未从容器返回? [英] RSA Encryption public key not returned from container?

查看:191
本文介绍了RSA加密公钥未从容器返回?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



这是一个完整的代码片段来测试我想做什么:

 使用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;
//创建两个提供者
RSACryptoServiceProvider serverRSA = GetKeyFromContainer(fullKeyContainer);

//保存公钥和全密钥对
SaveKeyToContainer(fullKeyContainer,serverRSA.ExportParameters(true));
SaveKeyToContainer(publicKeyContainer,serverRSA.ExportParameters(false));

//从内存中删除它们
serverRSA.Clear();
serverRSA = null;
GC.Collect();

//检索完整的服务器集和私人客户端集
serverRSA = GetKeyFromContainer(fullKeyContainer);
RSACryptoServiceProvider clientRSA = GetKeyFromContainer(publicKeyContainer);

//此时,RSA提供程序的公钥应该是相同的
string clientPublicKey = clientRSA.ToXmlString(false);
string serverPublicKey = serverRSA.ToXmlString(false);

if(clientPublicKey.Equals(serverPublicKey))
{//他们有相同的公钥。

//创建一个XmlDocument对象。
XmlDocument xmlDoc = new XmlDocument();

//将XML文件加载到XmlDocument对象中。
try
{
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load(test.xml);
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}

//我们可以用clientRSA使用公共密钥
encypt加密(xmlDoc,Fields,DataFields,clientRSA,test);

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

//应该能够使用私钥解密serverRSA
Decrypt(xmlDoc,serverRSA,test);

Console.WriteLine(Decrypted:\r\\\
+ xmlDoc.OuterXml);
}
else
{
Console.WriteLine(两个RSA有不同的公钥...);
}

Console.ReadLine();
}



私人静态CspParameters GetCspParameters(string containerName)
{
//创建CspParameters对象并设置密钥容器
//用于存储RSA密钥对的名称。
CspParameters tmpParameters = new CspParameters();
tmpParameters.Flags = CspProviderFlags.UseMachineKeyStore; //使用机器密钥存储 - 这允许我们在应用程序没有登录用户的情况下使用机器级容器
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);

//创建一个新的RSACryptoServiceProvider实例,访问
//密钥容器
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(tmpParameters);

//从文本中设置键信息
rsa.ImportParameters(rsaParameters);
}

public static RSACryptoServiceProvider GetKeyFromContainer(string containerName)
{
//创建CspParameters对象并设置密钥容器
//用于存储RSA密钥对。
CspParameters tmpParameters = GetCspParameters(containerName);

//创建一个新的RSACryptoServiceProvider实例,访问
//密钥容器。
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(tmpParameters);

return rsa;
}

public static void DeleteKeyFromContainer(string containerName)
{
//创建CspParameters对象并设置密钥容器
//名称用于存储RSA密钥对。
CspParameters tmpParameters = GetCspParameters(containerName);

//创建一个新的RSACryptoServiceProvider实例,访问
//密钥容器。
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(tmpParameters);

//删除容器中的键条目。
rsa.PersistKeyInCsp = false;

//调用清除以释放资源并从容器中删除密钥。
rsa.Clear();
}



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(ElementToEncrypt);
if(EncryptionElementID == null)
throw new ArgumentNullException(EncryptionElementID);
if(Alg == null)
throw new ArgumentNullException(Alg);
if(KeyName == null)
throw new ArgumentNullException(KeyName);

////////////////////////////////////////// //////
//在XmlDocument
//对象中查找指定的元素并创建一个新的XmlElemnt对象。
//////////////////////////////////////////////// //
XmlElement elementToEncrypt = Doc.GetElementsByTagName(ElementToEncrypt)[0] as XmlElement;

//如果未找到元素,则抛出XmlException。
if(elementToEncrypt == null)
{
throw new XmlException(找不到指定的元素);

}
RijndaelManaged sessionKey = null;

try
{
////////////////////////////////// ////////////////////
//创建一个EncryptedXml类的新实例
//并使用它用$ b $加密XmlElement b //新的随机对称密钥。
//////////////////////////////////////////////// ////

//创建一个256位的Rijndael键。
sessionKey = new RijndaelManaged();
sessionKey.KeySize = 256;

EncryptedXml eXml = new EncryptedXml();

byte [] encryptedElement = eXml.EncryptData(elementToEncrypt,sessionKey,false);
//////////////////////////////////////////////// //
//构造一个EncryptedData对象,并使用所需的加密信息填充
//。
//////////////////////////////////////////////// //

EncryptedData edElement = new EncryptedData();
edElement.Type = EncryptedXml.XmlEncElementUrl;
edElement.Id = EncryptionElementID;
//创建一个EncryptionMethod元素,以便
//接收者知道用于解密的算法。

edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
//加密会话密钥并将其添加到EncryptedKey元素。
EncryptedKey ek = new EncryptedKey();

byte [] encryptedKey = EncryptedXml.EncryptKey(sessionKey.Key,Alg,false);

ek.CipherData = new CipherData(encryptedKey);

ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url);

//为KeyInfo元素创建一个新的DataReference元素
//。这个可选的
//元素指定哪个EncryptedData
//使用这个键。一个XML文档可以有
//多个EncryptedData元素,它们使用
//不同的键。
DataReference dRef = new DataReference();

//指定EncryptedData URI。
dRef.Uri =#+ EncryptionElementID;

//将DataReference添加到EncryptedKey。
ek.AddReference(dRef);
//将加密的密钥添加到
// EncryptedData对象。

edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));
//设置KeyInfo元素以指定RSA密钥的
//名称。


//创建一个新的KeyInfoName元素。
KeyInfoName kin = new KeyInfoName();

//指定键的名称。
kin.Value = KeyName;

//将KeyInfoName元素添加到
// EncryptedKey对象中。
ek.KeyInfo.AddClause(kin);
//将加密的元素数据添加到
// EncryptedData对象。
edElement.CipherData.CipherValue = encryptedElement;
//////////////////////////////////////////////// //////
//使用EncryptedData元素替换原始XmlDocument
//对象中的元素。
//////////////////////////////////////////////// //////
EncryptedXml.ReplaceElement(elementToEncrypt,edElement,false);
}
catch(Exception e)
{
//重新抛出异常。
throw e;
}
finally
{
if(sessionKey!= null)
{
sessionKey.Clear();
}

}

}

public static void Decrypt(XmlDocument Doc,RSA Alg,string KeyName)
{
//检查参数。
if(Doc == null)
throw new ArgumentNullException(Doc);
if(Alg == null)
throw new ArgumentNullException(Alg);
if(KeyName == null)
throw new ArgumentNullException(KeyName);

//创建一个新的EncryptedXml对象。
EncryptedXml exml = new EncryptedXml(Doc);

//添加键名映射。
//此方法只能解密呈现指定键名称的文档
//。
exml.AddKeyNameMapping(KeyName,Alg);

//解密元素。
exml.DecryptDocument();

}

}
}

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



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



有什么想法为什么会发生?

我有非常相似的问题



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



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


你需要存储一个私钥,你应该使用一个密钥容器


...这是一个清晰的aa语句,我已经能够通过MSDN找到。



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


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?

解决方案

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.

这篇关于RSA加密公钥未从容器返回?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆