2009-08-20 14 views
57

Buduję niestandardowy koszyk, w którym numery CC i data Exp będą przechowywane w bazie danych do czasu przetworzenia (a następnie usunięcia). Muszę zaszyfrować te dane (oczywiście).Jak wygenerować unikatowy klucz publiczny i prywatny za pośrednictwem RSA

Chcę użyć klasy RSACryptoServiceProvider.

Oto mój kod do tworzenia moich kluczy.

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

Teraz planujemy przechowywać klucz prywatny xml na dysku USB dołączonym do łańcucha kluczy menedżerów.

Ilekroć menedżer opuszcza firmę, chcę mieć możliwość generowania nowych kluczy publicznych i prywatnych (i ponownie szyfrować wszystkie obecnie przechowywane numery CC za pomocą nowego klucza publicznego).

Mój problem polega na tym, że klucze generowane przez ten kod są zawsze takie same. Jak generować unikalny zestaw kluczy za każdym razem?

AKTUALIZACJA. Mój kod testowy jest poniżej .:
uwaga: tutaj parametr "privatekey" to oryginalny klucz prywatny. Aby klucze zostały zmienione, muszę sprawdzić, czy klucz prywatny jest ważny.

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

W 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

Jak testujesz to? –

+0

Jestem zasadniczo wywołanie funkcji AssignNewKey() ze strony .net, a następnie sprawdzanie nowego "publicPrivateKeyXML" przeciwko mojej poprzedniej wersji. Zaktualizuję powyższe pytanie, aby uwzględnić mój kod testowy. –

+2

To jest trochę styczne, ale czy zdajesz sobie sprawę, że aby przechowywać numery kart kredytowych, musisz mieć system zgodny z PCI? Zobacz http://stackoverflow.com/questions/4300863/storing-credit-card-number-pci – Art

Odpowiedz

7

Co skończyło się robi, to stworzyć nowy KeyContainer nazwa bazująca na aktualnej nazwie DateTime (DateTime.Now.Ticks.ToString()), gdy potrzebuję utworzyć nowy klucz i zapisz nazwę kontenera i klucz publiczny do bazy danych. Ponadto, gdy tylko utworzę nowy klucz, powinienem wykonać następujące czynności:

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

przed utworzeniem nowego klucza.

+1

Byłoby miło, gdybyś opublikował kompletne rozwiązanie, ponieważ nie mogę zrozumieć, co jest robione w komentarzach – ShaneKm

+0

czy poprawnie odczytuję Twój kod? masz zmienną o nazwie "privateKey", ale twoje komentarze sugerują twojemu nowemu kluczowi publicznemu? – barrypicker

+0

Minęło już prawie 7 lat, odkąd to napisałem ... ale myślę, że metoda 'AssignNewKey' miała na celu utworzenie nowego klucza' public' * i * 'private', przechowywanie klucza' public' w bazie danych podczas zwracania klucz 'private' jako ciąg znaków XML. –

21

Konstruktor RSACryptoServiceProvider(CspParameters) tworzy parę kluczy, które są przechowywane w magazynie kluczy na komputerze lokalnym. Jeśli masz już parę kluczy o określonej nazwie, używa ona istniejącej pary kluczy.

Wygląda na to, że jesteś , a nie zainteresowany przechowywaniem klucza w urządzeniu.

więc użyć konstruktora 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:

Ewentualnie spróbuj ustawić 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... 
} 
+0

Poprawnie; Chyba nie interesuje mnie przechowywanie klucza na maszynie. Gdybym użyć konstruktora RSACryptoServiceProvider (Int32) Poniższy kod daje mi „System nie może odnaleźć określonego pliku.” błąd. RSA rsa = nowa usługa RSACryptoServiceProvider (2048); rsa.ToXmlString (true); –

+0

Ponieważ uruchamiam to w asp.net może to być problem? –

+0

Tak, problem jest prawdopodobnie spowodowany tym, że "usługa sieciowa" nie może generować kluczy w magazynie użytkownika. –

115

Czy wiesz, że za każdym razem użyć kodu:

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

.NET (Windows) faktycznie przechowuje swój klucz w przetrwałego kontenera klucza - na zawsze? Ten kontener jest losowo generowany przez .NET ...

Wynikiem jest:

  1. Wszelkie losowy klucz RSA/DSA kiedykolwiek generowane w celu ochrony danych, tworząc niestandardowy certyfikat X.509 itp został wyciekły w systemie plików systemu Windows . Dla wszystkich, którzy mają dostęp do Twojego konta, aby go odebrać. A ty myślałeś dane były bezpieczne ...

  2. Twój dysk jest powoli wypełnione danymi. Zwykle nie jest to duży problem, ale zależy to od twojej aplikacji (np. Może generować setki kluczy co minutę).

Więc co zrobić, aby uniknąć tego dość Nieoczekiwana zachowanie?

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

ZAWSZE

Aktualizacja:

Chcesz zobaczyć na własne oczy?

użyć tego narzędzia http://www.jensign.com/KeyPal/index.html. Mam kilka tysięcy na moim komputerze.

+2

Mam dokładnie taką samą potrzebę jak OP; klucz publiczny przechodzi do bazy danych, klucz prywatny trafia do bezpiecznego miejsca na dysku. Więc jeśli użyłem twojego przykładowego kodu, ale pierwsza linia to 'rsa.FromXMLString (pubKey)', to ani wygenerowany klucz, ani załadowany nie jest przechowywany w sklepie? – KeithS

+0

Czy inicjalizacja RSACryptoServiceProvider za pomocą narzędzia CspParameters() {Flags = CspProviderFlags.CreateEphemeralKey}) powoduje to samo? –

+0

Czy istnieje sposób ustawić PersistKeyInCsp przed utworzeniem obiektu rsa? – Nayef

Powiązane problemy