RSA Encryption public key not returning from container?

I feel that what I'm trying to do is very simple. But for some reason he doesn't want to work:

Here is the complete code snippet to check what I'm 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 works fine as long as I save / get RSACryptoServiceProvider with both private and public key. As soon as I save RSACryptoServiceProvider with a JUST public key, the next time I try to retrieve everything I get, it is a NEW and DIFFERENT RSACryptoServiceProvider!

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

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

+3
source share
3 answers

I had a very similar question .

Now I am pretty sure that key containers cannot be used to store public keys. Their main purpose is to store key pairs. The key container stores only the key that was originally generated, and importing the PublicOnly key affects only the instance, not the store.

Practical Guide: Storing Asymmetric Keys in a Key Container in the .NET Developer's Guide states that

If you need to save the private key, you must use the key container

... which is about as clear as the a operator, which I managed to find through MSDN.

The substitution mechanism that I used was to store the key in an XML file (since it is a public key, it does not matter whether it is easy to see), with permissions set using file system access rules to prevent unwanted modification.

+3
source

I understand that your call to ImportParameters in SaveKeyToContainer does not affect the key in the repository. Your implementation of SaveKeyToContainer initializes the RSACryptoServiceProvider instance using the key in the repository (generates a new key pair when the container does not exist), and then imports parameters that affect the instance, not the repository.

When you later receive a publicKeyContainer, you are provided with a new key pair that was generated when you tried to save it, and not an imported public fragment.

Sorry, I can’t help with the details of importing the key into the repository using the cryptography API. I believe that the store only supports key pairs, i.e. Does not expect to be able to import only the public key.

0
source

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

I beat my brains with the same problem and came to the same conclusion, although this is not indicated for those specified in the documents.

When you instantiate the RSA provider, you get a new key pair. If you provide a parameter object and name the key container, a new key pair will be saved there.

If you import the public key, it saves not !

Dan

0
source

Source: https://habr.com/ru/post/1210260/


All Articles