2009-08-20 6 views
57

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

Я хочу использовать класс 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» здесь является первоначальным закрытым ключом. Чтобы изменить ключи, мне нужно проверить, что закрытый ключ действителен.

В 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); 
} 
+0

Как вы тестируете это? –

+0

Я в основном вызываю функцию AssignNewKey() с .net-страницы, а затем проверяю новую «publicPrivateKeyXML» на мою предыдущую версию. Я обновлю вопрос, чтобы включить мой тестовый код. –

+2

Это немного тангенциально, но понимаете ли вы, что для хранения номеров кредитных карт вам нужна ваша система, совместимая с PCI? См. Http://stackoverflow.com/questions/4300863/storing-credit-card-number-pci – Art

ответ

7

То, что я в конечном итоге делает это создать новый 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. 
} 

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

+1

было бы неплохо, если бы вы разместили полное решение, так как я не могу понять, что делается в комментариях. – ShaneKm

+0

Я правильно читаю ваш код? у вас есть переменная, называемая «privateKey», но ваши комментарии предполагают создание нового открытого ключа? – barrypicker

+0

Прошло почти 7 лет с тех пор, как я написал это ... но я думаю, что метод AssignNewKey был предназначен для создания нового 'public' * и *' private' ключа, хранящего ключ 'public' в базе данных, возвращая 'private' как строка XML. –

21

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 ложь:

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... 
} 
+0

Исправить; Наверное, мне неинтересно, что ключ хранится на машине. Если я использую конструктор RSACryptoServiceProvider (Int32), следующий код дает мне «Система не может найти указанный файл». ошибка. RSA rsa = новый RSACryptoServiceProvider (2048); rsa.ToXmlString (true); –

+0

Поскольку я запускаю это в asp.net, это может быть проблемой? –

+0

Да, проблема, вероятно, связана с тем, что «Сетевая служба» не может генерировать ключи в хранилище пользователей. –

115

Знаете ли вы, что каждый раз, когда вы используете код вроде этого:

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; 
    } 
} 

ВСЕГДА

Update:

Хотите увидеть для себя?

Используйте этот инструмент http://www.jensign.com/KeyPal/index.html. У меня есть несколько тысяч на моей машине.

+2

У меня точно такая же потребность, как у ОП; открытый ключ находится в БД, закрытый ключ переходит в защищенное хранилище на флэш-накопителе. Итак, если я использовал ваш пример кода, но первая строка была 'rsa.FromXMLString (pubKey)', то ни сгенерированный ключ, ни загруженный не сохранялись в хранилище? – KeithS

+0

Выполняет ли инициализация RSACryptoServiceProvider с помощью CspParameters() {Flags = CspProviderFlags.CreateEphemeralKey}) то же самое? –

+0

Есть ли способ установить PersistKeyInCsp перед созданием объекта rsa? – Nayef