3

我觉得我想要做的事很简单。但由于某些原因,它不希望工作:不从容器返回的RSA加密公钥?

下面是一个完整的代码片断来测试什么,我试图做的:

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!

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

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

+0

那是正确的: 一旦我救了的RSACryptoServiceProvider只是一个私钥 您的意思是 一旦我保存只是一个公钥 – Christian 2009-05-06 00:39:26

+0

是的,我的错字 – 2009-05-06 13:51:25

回答

0

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

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

对不起,我不能帮助任何关于使用加密API将密钥导入存储的详细信息。我相信商店只支持密钥对,即不希望只能导入公钥。

0

.NET Crypto Classes的文档非常差。

虽然在文档中没有明确说明,但我仍然在用同样的问题打我的大脑,并得出了相同的结论。

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

如果你导入一个公钥,它的确不是而是得到持久!

3

我有a very similar question

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

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

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

...这是几乎一样澄清AA语句,我已经能够找到通过MSDN。

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