Как сгенерировать уникальный открытый и закрытый ключ через RSA - PullRequest
63 голосов
/ 20 августа 2009

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

Я хочу использовать класс RSACryptoServiceProvider.

Вот мой код для создания моих ключей.

public static void AssignNewKey(){
    const int PROVIDER_RSA_FULL = 1;
    const string CONTAINER_NAME = "KeyContainer";
    CspParameters cspParams;
    cspParams = new CspParameters(PROVIDER_RSA_FULL);
    cspParams.KeyContainerName = CONTAINER_NAME;
    cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
    cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";
    rsa = new RSACryptoServiceProvider(cspParams);

    string publicPrivateKeyXML = rsa.ToXmlString(true);
    string publicOnlyKeyXML = rsa.ToXmlString(false);
    // do stuff with keys...
}

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

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

Моя проблема в том, что ключи, сгенерированные этим кодом, всегда одинаковы. Как мне каждый раз генерировать уникальный набор ключей?

ОБНОВЛЕНИЕ. Мой тестовый код ниже .:
примечание: параметр "privatekey" здесь является оригинальным закрытым ключом. Чтобы ключи были изменены, мне нужно убедиться, что закрытый ключ действителен.

In Default.aspx.cs

public void DownloadNewPrivateKey_Click(object sender, EventArgs e)
{
    StreamReader reader = new StreamReader(fileUpload.FileContent);
    string privateKey = reader.ReadToEnd();
    Response.Clear();
    Response.ContentType = "text/xml";
    Response.End();
    Response.Write(ChangeKeysAndReturnNewPrivateKey(privateKey));
}

В Crytpography.cs:

public static privateKey;
public static publicKey;
public static RSACryptoServiceProvider rsa;

public static string ChangeKeysAndReturnNewPrivateKey(string _privatekey)
{

    string testData = "TestData";
    string testSalt = "salt";
    // encrypt the test data using the exisiting public key...
    string encryptedTestData = EncryptData(testData, testSalt);
    try
    {
        // try to decrypt the test data using the _privatekey provided by user...
        string decryptTestData = DecryptData(encryptedTestData, _privatekey, testSalt);
        // if the data is successfully decrypted assign new keys...
        if (decryptTestData == testData)
        {
            AssignNewKey();
            // "AssignNewKey()" should set "privateKey" to the newly created private key...
            return privateKey;
        }
        else
        {
            return string.Empty;
        }
    }
    catch (Exception ex)
    {
        return string.Empty;
    }
}
public static void AssignParameter(){
    const int PROVIDER_RSA_FULL = 1;
    const string CONTAINER_NAME = "KeyContainer";
    CspParameters cspParams;
    cspParams = new CspParameters(PROVIDER_RSA_FULL);
    cspParams.KeyContainerName = CONTAINER_NAME;
    cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
    cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";
    rsa = new RSACryptoServiceProvider(cspParams);
}
public static void AssignNewKey()
{
    AssignParameter();

    using (SqlConnection myConn = new SqlConnection(Utilities.ConnectionString))
    {
        SqlCommand myCmd = myConn.CreateCommand();

        string publicPrivateKeyXML = rsa.ToXmlString(true);
        privateKey = publicPrivateKeyXML; // sets the public variable privateKey to the new private key.

        string publicOnlyKeyXML = rsa.ToXmlString(false);
        publicKey = publicOnlyKeyXML; // sets the public variable publicKey to the new public key.

        myCmd.CommandText = "UPDATE Settings SET PublicKey = @PublicKey";
        myCmd.Parameters.AddWithValue("@PublicKey", publicOnlyKeyXML);
        myConn.Open();

        myComm.ExecuteScalar();
    }
}
public static string EncryptData(string data2Encrypt, string salt)
{
    AssignParameter();

    using (SqlConnection myConn = new SqlConnection(Utilities.ConnectionString))
    {
        SqlCommand myCmd = myConn.CreateCommand();

        myCmd.CommandText = "SELECT TOP 1 PublicKey FROM Settings";

        myConn.Open();

        using (SqlDataReader sdr = myCmd.ExecuteReader())
        {
            if (sdr.HasRows)
            {
                DataTable dt = new DataTable();
                dt.Load(sdr);
                rsa.FromXmlString(dt.Rows[0]["PublicKey"].ToString());
            }
        }
    }

    //read plaintext, encrypt it to ciphertext
    byte[] plainbytes = System.Text.Encoding.UTF8.GetBytes(data2Encrypt + salt);
    byte[] cipherbytes = rsa.Encrypt(plainbytes, false);
    return Convert.ToBase64String(cipherbytes);
}
public static string DecryptData(string data2Decrypt, string privatekey, string salt)
{
    AssignParameter();

    byte[] getpassword = Convert.FromBase64String(data2Decrypt);

    string publicPrivateKeyXML = privatekey;
    rsa.FromXmlString(publicPrivateKeyXML);

    //read ciphertext, decrypt it to plaintext
    byte[] plain = rsa.Decrypt(getpassword, false);
    string dataAndSalt = System.Text.Encoding.UTF8.GetString(plain);
    return dataAndSalt.Substring(0, dataAndSalt.Length - salt.Length);
}

Ответы [ 3 ]

125 голосов
/ 01 мая 2011

Когда вы используете такой код:

using (var rsa = new RSACryptoServiceProvider(1024))
{
   // Do something with the key...
   // Encrypt, export, etc.
}

.NET (фактически Windows) хранит ваш ключ в постоянном контейнере ключей навсегда. Контейнер генерируется случайным образом .NET

Это означает:

  1. Любой случайный ключ RSA / DSA, который вы КОГДА-ЛИБО сгенерировали для защиты данных, создания собственного сертификата X.509 и т. Д., Мог быть открыт без вашего ведома в файловой системе Windows. Доступно всем, кто имеет доступ к вашей учетной записи.

  2. Ваш диск медленно заполняется данными. Обычно это не большая проблема, но это зависит от вашего приложения (например, оно может генерировать сотни ключей каждую минуту).

Для решения этих проблем:

using (var rsa = new RSACryptoServiceProvider(1024))
{
   try
   {
      // Do something with the key...
      // Encrypt, export, etc.
   }
   finally
   {
      rsa.PersistKeyInCsp = false;
   }
}

ВСЕГДА

23 голосов
/ 20 августа 2009

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

Звучит так, как будто вы не заинтересованы в хранении ключа на машине.

Так что используйте конструктор RSACryptoServiceProvider(Int32):

public static void AssignNewKey(){
    RSA rsa = new RSACryptoServiceProvider(2048); // Generate a new 2048 bit RSA key

    string publicPrivateKeyXML = rsa.ToXmlString(true);
    string publicOnlyKeyXML = rsa.ToXmlString(false);
    // do stuff with keys...
}

EDIT:

В качестве альтернативы попробуйте установить для PersistKeyInCsp значение false:

public static void AssignNewKey(){
    const int PROVIDER_RSA_FULL = 1;
    const string CONTAINER_NAME = "KeyContainer";
    CspParameters cspParams;
    cspParams = new CspParameters(PROVIDER_RSA_FULL);
    cspParams.KeyContainerName = CONTAINER_NAME;
    cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
    cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";
    rsa = new RSACryptoServiceProvider(cspParams);

    rsa.PersistKeyInCsp = false;

    string publicPrivateKeyXML = rsa.ToXmlString(true);
    string publicOnlyKeyXML = rsa.ToXmlString(false);
    // do stuff with keys...
}
8 голосов
/ 22 августа 2009

В итоге я создал новое имя KeyContainer на основе текущего DateTime (DateTime.Now.Ticks.ToString ()) всякий раз, когда мне нужно создать новый ключ и сохранить имя контейнера и открытый ключ в базе данных. , Кроме того, всякий раз, когда я создаю новый ключ, я делаю следующее:

public static string ConvertToNewKey(string oldPrivateKey)
{

    // get the current container name from the database...

    rsa.PersistKeyInCsp = false;
    rsa.Clear();
    rsa = null;

    string privateKey = AssignNewKey(true); // create the new public key and container name and write them to the database...

       // re-encrypt existing data to use the new keys and write to database...

    return privateKey;
}
public static string AssignNewKey(bool ReturnPrivateKey){
     string containerName = DateTime.Now.Ticks.ToString();
     // create the new key...
     // saves container name and public key to database...
     // and returns Private Key XML.
}

перед созданием нового ключа.

...