3

Я чувствую, что то, что я пытаюсь сделать, очень просто. Но по какой-то причине он не хочет работать:Открытый ключ RSA Encryption не возвращается из контейнера?

Вот полный фрагмент кода, чтобы проверить, что я пытаюсь сделать:

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 с JUST открытым ключом, в следующий раз, когда я попытаюсь получить все, что я получаю, это НОВЫЙ и РАЗЛИЧНЫЙ RSACryptoServiceProvider!

Как вы можете себе представить, вы не можете зашифровать что-то одним набором ключей, а затем попытаться расшифровать весь новый набор!

Любые идеи о том, почему это происходит? или каким правильным способом было бы хранить общедоступный ключ?

+0

Это правильно: После того, как я сохранить RSACryptoServiceProvider с JUST секретный ключ вы имели в виду После того, как я сохранить RSACryptoServiceProvider с JUST открытым ключом – Christian

+0

Да, я извиняюсь за опечатку –

ответ

0

Я понимаю, что ваш призыв к ImportParameters в SaveKeyToContainer не влияет на ключ в магазине. Ваша реализация SaveKeyToContainer инициализирует экземпляр RSACryptoServiceProvider с использованием ключа в хранилище (генерирует новую пару ключей, когда контейнер не существует), а затем импортирует параметры, которые влияют на экземпляр, а не на хранилище.

Когда вы возвращаете publicKeyContainer, вам предоставляется новая пара ключей, которая была сгенерирована при попытке сохранить ее, а не импортированный общедоступный фрагмент.

Извините, я не могу помочь с подробностями об импорте ключа в магазин с помощью API криптографии. Я считаю, что в хранилище поддерживаются только пары ключей, т. Е. Не предполагается, что вы сможете импортировать только открытый ключ.

+0

Существует свойство, которое вы можете установить, чтобы определить, сохраняется ли экземпляр в хранилище. У меня создалось впечатление, что если это будет продолжаться, то изменения, внесенные вами в ключи RSA, также сохраняются в хранилище ключей? –

+0

Можно с уверенностью предположить, что мой ответ неточен и что PersistKeyInCsp должен делать именно это при вызове ImportParameters. Мне было бы интересно узнать, пытались ли вы использовать методы Export/ImportCspBlob или To/FromXmlString. Знаете ли вы, где модуль и показатель открытого ключа больше не совпадают для serverKey и clientKey (RSAParameters)? Есть ли шанс проверить код на моно? – Ajw

0

Документация для классов .NET Crypto очень бедна.

Я избиваю свои мозги одной и той же проблемой и пришел к одному и тому же выводу, хотя это не указано для определенных в документах.

Когда вы создаете экземпляр поставщика RSA, вы получаете новую пару ключей. Если вы предоставите объект параметра и назовите контейнер ключа, там будет сохранена новая пара ключей.

Если вы импортируете открытый ключ, то не получите постоянный!

Dan

3

я имел a very similar question.

Теперь я почти уверен, что ключевые контейнеры не могут использоваться для хранения открытых ключей. Их основная цель состоит в том, чтобы хранить пары ключей. Контейнер ключей хранит только тот ключ, который был первоначально сгенерирован, а импорт ключа PublicOnly влияет только на экземпляр, а не на хранилище.

«Как хранить асимметричные ключи в контейнере ключей».Руководство NET разработчика утверждает, что

Если вам нужно хранить закрытый ключ, вы должны использовать контейнер ключей

..., который является столь же четким заявлением аа, как я смог найти через MSDN.

Подставляемый механизм, который я использовал, заключался в том, чтобы хранить ключ в XML-файле (поскольку это открытый ключ, не имеет значения, легко ли это видно), с разрешениями, установленными с использованием правил доступа к файловой системе, чтобы предотвратить нежелательную модификацию.

Смежные вопросы