Открытый ключ шифрования 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!

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

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


person Bogdan Varlamov    schedule 06.05.2009    source источник
comment
Это правильно: как только я сохраню RSACryptoServiceProvider ТОЛЬКО с закрытым ключом. Вы имели в виду, как только я сохраню RSACryptoServiceProvider ТОЛЬКО с открытым ключом   -  person Christian    schedule 06.05.2009


Ответы (3)


У меня был очень похожий вопрос.

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

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

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

... что примерно так же ясно, как я смог найти через MSDN.

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

person Andrew Kimball    schedule 18.02.2010

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

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

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

person Ajw    schedule 05.06.2009
comment
Существует свойство, которое вы можете установить, чтобы определить, сохраняется ли экземпляр в магазине. У меня создалось впечатление, что если это установлено на сохранение, то изменения, которые вы вносите в ключи RSA, также сохраняются в хранилище ключей? - person Bogdan Varlamov; 16.06.2009
comment
Можно с уверенностью предположить, что мой ответ неточен и что PersistKeyInCsp должен делать именно это при вызове ImportParameters. Мне было бы интересно узнать, пробовали ли вы использовать методы Export / ImportCspBlob или To / FromXmlString. Знаете ли вы, где модуль и экспонента открытого ключа больше не совпадают для serverKey и clientKey (RSAParameters)? Есть ли шанс протестировать код на моно? - person Ajw; 16.06.2009

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

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

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

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

Дэн

person Community    schedule 09.06.2009