Cómo generar clave pública y privada única a través de RSA

Estoy construyendo un carrito de compras personalizado donde los números de CC y la fecha de Exp. Se almacenarán en una base de datos hasta que se procesen (luego se eliminarán). Necesito cifrar esta información (obviamente).

Quiero usar la clase RSACryptoServiceProvider.

Aquí está mi código para crear mis llaves.

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

Ahora el plan es almacenar la clave privada xml en una unidad USB conectada al llavero de los gerentes.

Cada vez que un gerente deja la empresa, quiero poder generar nuevas claves públicas y privadas (y volver a cifrar todos los números CC almacenados actualmente con la nueva clave pública).

Mi problema es que las claves generadas por este código son siempre las mismas. ¿Cómo generaría un conjunto único de claves todo el tiempo?

ACTUALIZAR. Mi código de prueba está abajo .:
nota: el parámetro “clave privada” aquí es la clave privada original. Para poder cambiar las claves, necesito verificar que la clave privada sea válida.

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

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

Cuando usas un código como este:

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

.NET (en realidad, Windows) almacena su clave en un contenedor de claves persistentes para siempre. El contenedor es generado aleatoriamente por .NET

Esto significa:

  1. Cualquier clave aleatoria de RSA / DSA que haya ALGUNA vez generado con el fin de proteger los datos, crear un certificado X.509 personalizado, etc. puede haber sido expuesta sin su conocimiento en el sistema de archivos de Windows. Accesible por cualquier persona que tenga acceso a su cuenta.

  2. Su disco se está llenando lentamente de datos. Normalmente no es una gran preocupación, pero depende de su aplicación (por ejemplo, podría generar cientos de claves por minuto).

Para resolver estos problemas:

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

SIEMPRE

El RSACryptoServiceProvider(CspParameters) crea un par de claves que se almacena en el almacén de claves en la máquina local. Si ya tiene un par de llaves con el nombre especificado, usa el par de llaves existente.

Parece que no está interesado en tener la clave almacenada en la máquina.

Entonces use el 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... } 

EDITAR:

Alternativamente intente configurar el PersistKeyInCsp en falso:

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

Lo que terminé haciendo es crear un nuevo nombre de KeyContainer basado en el DateTime actual (DateTime.Now.Ticks.ToString ()) cada vez que necesito crear una nueva clave y guardar el nombre del contenedor y la clave pública en la base de datos. Además, cada vez que creo una nueva clave, hago lo siguiente:

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

antes de crear la nueva clave.