2009-08-20 6 views
57

Je construis un panier personnalisé où les numéros CC et la date d'expiration seront stockés dans une base de données jusqu'au traitement (puis supprimés). J'ai besoin de crypter ces données (évidemment).Comment générer une clé publique et privée unique via RSA

Je souhaite utiliser la classe RSACryptoServiceProvider.

Voici mon code pour créer mes clés.

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

Maintenant, le plan est de stocker la clé privée xml sur une clé USB attachée à la chaîne de clés de gestionnaires. Chaque fois qu'un responsable quitte la société, je souhaite pouvoir générer de nouvelles clés publiques et privées (et rechiffrer tous les numéros CC stockés avec la nouvelle clé publique).

Mon problème est que les clés générées par ce code sont toujours les mêmes. Comment pourrais-je générer un jeu de clés unique à chaque fois?

MISE À JOUR. Mon code de test est ci-dessous .:
note: le paramètre "privatekey" ici est la clé privée d'origine. Pour que les clés soient modifiées, j'ai besoin de vérifier que la clé privée est valide.

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

Comment testez-vous cela? –

+0

J'appelle essentiellement la fonction AssignNewKey() à partir d'une page .net, puis je vérifie le nouveau "publicPrivateKeyXML" par rapport à la version précédente. Je vais mettre à jour la question ci-dessus pour inclure mon code de test. –

+2

Ceci est un peu tangentiel, mais vous rendez-vous compte que pour stocker les numéros de carte de crédit, vous avez besoin que votre système soit conforme à la norme PCI? Voir http://stackoverflow.com/questions/4300863/storing-credit-card-number-pci – Art

Répondre

7

Ce que je fini par faire est de créer une nouvelle KeyContainer nom basé sur le DateTime en cours (DateTime.Now.Ticks.ToString()) chaque fois que je dois créer une nouvelle clé et enregistrer le nom du conteneur et la clé publique dans la base de données. Aussi, chaque fois que je crée une nouvelle clé je ferais ce qui suit:

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

avant de créer la nouvelle clé.

+1

ce serait bien si vous avez posté une solution complète que je ne peux pas comprendre ce qui se fait dans les commentaires – ShaneKm

+0

suis-je lire votre code correctement? vous avez une variable appelée 'privateKey' mais vos commentaires suggèrent que vous créez une nouvelle clé publique? – barrypicker

+0

Cela fait presque 7 ans que j'ai écrit ceci ... mais je pense que la méthode 'AssignNewKey' était destinée à créer une nouvelle clé' public' * et * 'private', en stockant la clé' public' dans la base de données tout en retournant le clé 'private' en tant que chaîne XML. –

21

Le constructeur RSACryptoServiceProvider(CspParameters) crée une paire de clés qui est stocké dans le magasin de clés sur la machine locale. Si vous avez déjà une paire de clés avec le nom spécifié, elle utilise la paire de clés existante.

Il semble que vous soyez et non intéressé par l'enregistrement de la clé sur la machine.

Il faut donc utiliser le constructeur 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:

Vous pouvez également essayer le réglage de la 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

Correct; Je suppose que je ne suis pas intéressé à avoir la clé stockée sur la machine. Si j'utilise le constructeur RSACryptoServiceProvider (Int32) le code suivant me donne un "Le système ne trouve pas le fichier spécifié." Erreur. RSA rsa = nouveau RSACryptoServiceProvider (2048); rsa.ToXmlString (true); –

+0

Depuis que je cours cela dans asp.net pourrait-il être le problème? –

+0

Oui, le problème est probablement dû au fait que le "Service réseau" ne peut pas générer de clés dans le magasin de l'utilisateur. –

115

Savez-vous que chaque fois que vous utilisez un code comme ceci:

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

.NET (en fait Windows) stocke votre clé dans un PERSISTENT conteneur de clé - pour toujours? Et ce conteneur est généré aléatoirement par .NET ...

Le résultat est:

  1. Toute aléatoire clé RSA/DSA que vous avez jamais généré dans le but de la protection des données, la création d'un certificat X.509 personnalisé, etc. a eu une fuite dans le système de fichiers Windows . Pour tous ceux qui ont accès à votre compte pour le réclamer. Et vous pensiez que vos données étaient en sécurité ...

  2. Votre disque est lentement rempli de données. Normalement, ce n'est pas une grosse préoccupation, mais cela dépend de votre application (par exemple, elle peut générer des centaines de clés chaque minute).

Alors, que faites-vous pour éviter ce comportement plutôt INATTENDU?

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

TOUJOURS

Mise à jour:

Vous voulez voir par vous-même?

Utilisez ces outils http://www.jensign.com/KeyPal/index.html. J'ai plusieurs milliers sur ma machine.

+2

J'ai exactement le même besoin que l'OP; la clé publique va dans une base de données, la clé privée va dans le stockage sécurisé sur une clé USB. Donc, si j'ai utilisé votre exemple de code, mais que la première ligne était 'rsa.FromXMLString (pubKey)', alors ni la clé générée ni la clé chargée n'est conservée dans un magasin? – KeithS

+0

L'initialisation de RSACryptoServiceProvider avec CspParameters() {Flags = CspProviderFlags.CreateEphemeralKey} aboutit-elle à la même chose? –

+0

Existe-t-il un moyen de définir PersistKeyInCsp avant de créer l'objet rsa? – Nayef

Questions connexes