2009-08-20 17 views
57

Ich baue einen benutzerdefinierten Einkaufswagen, wo CC-Nummern und Exp-Datum in einer Datenbank bis zur Verarbeitung (dann gelöscht) gespeichert werden. Ich muss diese Daten (offensichtlich) verschlüsseln.So generieren Sie eindeutige öffentliche und private Schlüssel über RSA

Ich möchte die RSACryptoServiceProvider-Klasse verwenden.

Hier ist mein Code, um meine Schlüssel zu erstellen.

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

Jetzt ist der Plan, den privaten Schlüssel xml auf einem USB-Laufwerk zu speichern, das an die Schlüsselkette des Managers angehängt ist.

Immer wenn ein Manager das Unternehmen verlässt, möchte ich in der Lage sein, neue öffentliche und private Schlüssel zu generieren (und alle aktuell gespeicherten CC-Nummern mit dem neuen öffentlichen Schlüssel neu zu verschlüsseln).

Mein Problem ist, dass die von diesem Code generierten Schlüssel immer gleich sind. Wie würde ich jedes Mal einen einzigartigen Schlüsselsatz generieren?

UPDATE. Mein Testcode ist unten .:
Hinweis: der Parameter "privatekey" ist hier der ursprüngliche private Schlüssel. Um die Schlüssel zu ändern, muss ich überprüfen, dass der private Schlüssel gültig ist.

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

In 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

Wie testen Sie das? –

+0

Ich rufe grundsätzlich die Funktion AssignNewKey() von einer .net-Seite und dann die neue "publicPrivateKeyXML" gegen meine vorherige Version. Ich werde die obige Frage aktualisieren, um meinen Testcode hinzuzufügen. –

+2

Das ist ein bisschen tangential, aber ist Ihnen klar, dass Sie, um Kreditkartennummern speichern zu können, Ihr System PCI-konform sein müssen? Siehe http://stackoverflow.com/questions/4300863/storing-credit-card-number-pci – Art

Antwort

7

Was ich ein neues KeyContainer schaffen machen endete Name basiert auf der aktuellen DateTime (DateTime.Now.Ticks.ToString()) wenn ich einen neuen Schlüssel erstellen muss und Speichern Sie den Containernamen und den öffentlichen Schlüssel in der Datenbank. Auch wenn ich einen neuen Schlüssel erstelle, würde ich folgendes tun:

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

vor dem Erstellen des neuen Schlüssels.

+1

es wäre nett, wenn Sie vollständige Lösung gepostet haben, da ich nicht herausfinden kann, was in den Kommentaren getan wird – ShaneKm

+0

bin ich Ihren Code richtig lesen? Sie haben eine Variable namens 'privateKey', aber Ihre Kommentare deuten darauf hin, dass Sie einen neuen öffentlichen Schlüssel erstellen. – barrypicker

+0

Es ist fast 7 Jahre her, seit ich dies geschrieben habe ... aber ich denke, dass die 'AssignNewKey' Methode einen neuen' public' * und * privaten 'Schlüssel erzeugen sollte, der den' public' Schlüssel in der Datenbank speichert, während der 'private' Schlüssel als XML-String. –

21

Der RSACryptoServiceProvider(CspParameters) Konstruktor erzeugt ein Schlüsselpaar, das in den Schlüsselspeicher auf dem lokalen Rechner gespeichert ist. Wenn Sie bereits ein Schlüsselpaar mit dem angegebenen Namen haben, wird das vorhandene Schlüsselpaar verwendet.

Es klingt, als ob Sie nicht interessiert sind, den Schlüssel auf der Maschine gespeichert zu haben.

So verwenden Sie den RSACryptoServiceProvider(Int32) Konstruktor:

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:

versuchen Alternativ die PersistKeyInCsp auf 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

Korrekt; Ich denke, ich bin nicht daran interessiert, den Schlüssel auf der Maschine zu haben. Wenn ich den Konstruktor RSACryptoServiceProvider (Int32) verwende, gibt der folgende Code mir ein "Das System kann die angegebene Datei nicht finden." Error. RSA rsa = neuer RSACryptoServiceProvider (2048); rsa.ToXmlString (wahr); –

+0

Da ich das in asp.net ausführen, könnte das das Problem sein? –

+0

Ja, das Problem liegt wahrscheinlich daran, dass der "Netzwerkdienst" keine Schlüssel im Benutzerspeicher generieren kann. –

115

Wissen Sie, dass jedes Mal, wenn Sie einen Code wie folgt verwenden:

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

.NET (eigentlich Windows) Ihre Schlüssel in einem PERSISTENTE Schlüsselcontainer speichert - für immer? Und dieser Container wird zufällig von .NET generiert ...

Das Ergebnis ist:

  1. Jeder Zufall RSA/DSA-Schlüssel Sie jemals für den Zweck generiert haben Daten zu schützen, benutzerdefiniertes X.509-Zertifikat erstellen, usw. hat LEAKED in dem Windows-Dateisystem . Für alle, die Zugriff auf Ihr Konto haben, um es zu beanspruchen. Und Sie dachten, Ihre Daten wären sicher ...

  2. Ihre Festplatte wird langsam mit Daten gefüllt. Normalerweise kein großes Problem, aber es hängt von Ihrer Anwendung ab (z. B. kann es jede Minute Hunderte von Schlüsseln erzeugen).

Also, was tun Sie diese eher UNERWARTETE Verhalten zu vermeiden?

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

IMMER

Update:

Wollen Sie für sich selbst sehen?

Verwenden Sie dieses Werkzeug http://www.jensign.com/KeyPal/index.html. Ich habe mehrere tausend auf meiner Maschine.

+2

Ich habe genau das gleiche Bedürfnis wie das OP; Der öffentliche Schlüssel wird in einer Datenbank gespeichert, der private Schlüssel wird in einem sicheren Speicher auf einem USB-Stick gespeichert. Also, wenn ich Ihren Beispiel-Code verwendet, aber die erste Zeile war 'rs.FromXMLString (pubKey)', dann wird weder der generierte Schlüssel noch der geladene Schlüssel in einem Geschäft gespeichert? – KeithS

+0

Führt das Initialisieren des RSACryptoServiceProvider mit CspParameters() {Flags = CspProviderFlags.CreateEphemeralKey}) dasselbe durch? –

+0

Gibt es eine Möglichkeit, PersistKeyInCsp festzulegen, bevor das RSA-Objekt erstellt wird? – Nayef

Verwandte Themen