67 votes

Comment générer une clé publique et privée unique via RSA

Je suis en train de créer un panier d'achat personnalisé où les numéros de 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).

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

Le plan consiste maintenant à stocker la clé privée xml sur une clé USB attachée au porte-clés des managers.

Chaque fois qu'un responsable quitte l'entreprise, je veux pouvoir générer de nouvelles clés publiques et privées (et recrypter tous les numéros de CC actuellement 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 puis-je générer un ensemble unique de clés à chaque fois ?

UPDATE. Mon code de test est ci-dessous. :
note : le paramètre "privatekey" ici est la clé privée originale. Pour que les clés soient modifiées, je dois 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));
}

Dans 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 votes

En fait, j'appelle la fonction AssignNewKey() à partir d'une page .net, puis je compare la nouvelle "publicPrivateKeyXML" à la version précédente. Je vais mettre à jour la question ci-dessus pour inclure mon code de test.

2 votes

Cette question est un peu secondaire, mais savez-vous que pour stocker des numéros de cartes de crédit, votre système doit être conforme à la norme PCI ? Voir stackoverflow.com/questions/4300863/

0 votes

Oui, c'est en fait ce qui a suscité cette question. bien que nous ayons fini par utiliser un fournisseur de paiement externe en fin de compte.

132voto

coder5 Points 611

Quand vous utilisez un code comme celui-ci :

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

.NET (en fait Windows) stocke votre clé dans un fichier persistant conteneur de clés pour toujours. Le conteneur est généré de manière aléatoire par .NET

Cela signifie :

  1. Toute clé RSA/DSA aléatoire que vous avez JAMAIS générée dans le but de protéger des données, de créer un certificat X.509 personnalisé, etc. peut avoir été exposée à votre insu dans le système de fichiers de Windows. Accessible par toute personne ayant accès à votre compte.

  2. Votre disque se remplit lentement de données. Normalement, ce n'est pas un gros problème, mais cela dépend de votre application (par exemple, elle peut générer des centaines de clés chaque minute).

Pour résoudre ces problèmes :

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

TOUJOURS

2 votes

J'ai exactement le même besoin que le PO ; la clé publique va dans une BD, la clé privée va dans un stockage sécurisé sur une clé USB. Donc, si j'utilisais votre exemple de code, mais que la première ligne était rsa.FromXMLString(pubKey) Dans ce cas, ni la clé générée ni la clé chargée ne sont conservées dans un magasin ?

0 votes

L'initialisation du RSACryptoServiceProvider avec CspParameters() { Flags = CspProviderFlags.CreateEphemeralKey }) permet-elle d'obtenir le même résultat ?

0 votes

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

27voto

Rasmus Faber Points 24195

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

On dirait que vous êtes no intéressé à ce que la clé soit stockée sur la machine.

Utilisez donc le RSACryptoServiceProvider(Int32) constructeur :

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 aussi essayer de définir la valeur de PersistKeyInCsp sur 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 votes

Correct ; je suppose que je ne suis pas intéressé par le fait que la clé soit stockée sur la machine. Si j'utilise le constructeur RSACryptoServiceProvider(Int32), le code suivant me donne une erreur "Le système ne peut pas trouver le fichier spécifié". RSA rsa = new RSACryptoServiceProvider(2048) ; rsa.ToXmlString(true) ;

0 votes

Comme je l'exécute en asp.net, cela pourrait-il être le problème ?

0 votes

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

8voto

David Murdoch Points 28521

Ce que j'ai fini par faire, c'est créer un nouveau nom de KeyContainer basé sur l'heure actuelle (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. En outre, chaque fois que je crée une nouvelle clé, je fais 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 votes

Il serait bien que vous postiez la solution complète car je n'arrive pas à comprendre ce qui se passe dans les commentaires.

0 votes

Est-ce que je lis correctement votre code ? Vous avez une variable appelée 'privateKey' mais vos commentaires suggèrent que vous créez une nouvelle clé publique ?

0 votes

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X