95 votes

C# chiffrement/déchiffrement RSA avec transmission

J'ai vu beaucoup de tutoriels et d'exemples d'encryption/décryption sur le net en C# qui utilisent System.Security.Cryptography.RSACryptoServiceProvider, mais ce que j'espère pouvoir faire est:

  • Créer une paire de clés publique/privée RSA
  • Transmettre la clé publique (ou pour une preuve de concept, simplement la déplacer dans une variable de chaîne)
  • Créer un nouveau fournisseur de cryptage RSA et crypter une chaîne avec la clé publique
  • Transmettre la chaîne cryptée (ou les données) de retour au fournisseur de cryptage d'origine et décrypter la chaîne

Quelqu'un pourrait-il me diriger vers une ressource utile à ce sujet?

4 votes

Pouvez-vous indiquer un didacticiel spécifique qui explique le cryptage et le décryptage avec RSA en C# qui ne répond pas à vos besoins spécifiques ? Je dirais qu'ils sont assez simples, et il n'est pas clair pour moi, d'après votre question, quelle partie vous pose problème.

0 votes

Aussi sur le point n°1, quelle serait la meilleure pratique pour stocker la clé privée dans un conteneur/local sur la machine où les données seront finalement décryptées.

1 votes

Attention: Je ne considérerais aucune des réponses données comme une bonne pratique. Veuillez refactoriser, vous renseigner sur les systèmes de cryptage hybrides et ne pas simplement copier ces réponses dans votre code. Clés statiques, toujours encodées en XML ce qui n'est pas une pratique standard. Ne pas utiliser OAEP mais rester sur le cryptage PKCS#1. Tailles de clés trop petites. Utilisation de Unicode pour encoder du texte, etc. etc.

271voto

DarkSquirrel42 Points 4090

Eh bien, il y a vraiment assez d'exemples pour cela, mais de toute façon, voici

using System;
using System.Security.Cryptography;

namespace RsaCryptoExample
{
  static class Program
  {
    static void Main()
    {
      //lets take a new CSP with a new 2048 bit rsa key pair
      var csp = new RSACryptoServiceProvider(2048);

      //how to get the private key
      var privKey = csp.ExportParameters(true);

      //and the public key ...
      var pubKey = csp.ExportParameters(false);

      //converting the public key into a string representation
      string pubKeyString;
      {
        //we need some buffer
        var sw = new System.IO.StringWriter();
        //we need a serializer
        var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
        //serialize the key into the stream
        xs.Serialize(sw, pubKey);
        //get the string from the stream
        pubKeyString = sw.ToString();
      }

      //converting it back
      {
        //get a stream from the string
        var sr = new System.IO.StringReader(pubKeyString);
        //we need a deserializer
        var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
        //get the object back from the stream
        pubKey = (RSAParameters)xs.Deserialize(sr);
      }

      //conversion for the private key is no black magic either ... omitted

      //we have a public key ... let's get a new csp and load that key
      csp = new RSACryptoServiceProvider();
      csp.ImportParameters(pubKey);

      //we need some data to encrypt
      var plainTextData = "foobar";

      //for encryption, always handle bytes...
      var bytesPlainTextData = System.Text.Encoding.Unicode.GetBytes(plainTextData);

      //apply pkcs#1.5 padding and encrypt our data 
      var bytesCypherText = csp.Encrypt(bytesPlainTextData, false);

      //we might want a string representation of our cypher text... base64 will do
      var cypherText = Convert.ToBase64String(bytesCypherText);

      /*
       * some transmission / storage / retrieval
       * 
       * and we want to decrypt our cypherText
       */

      //first, get our bytes back from the base64 string ...
      bytesCypherText = Convert.FromBase64String(cypherText);

      //we want to decrypt, therefore we need a csp and load our private key
      csp = new RSACryptoServiceProvider();
      csp.ImportParameters(privKey);

      //decrypt and strip pkcs#1.5 padding
      bytesPlainTextData = csp.Decrypt(bytesCypherText, false);

      //get our original plainText back...
      plainTextData = System.Text.Encoding.Unicode.GetString(bytesPlainTextData);
    }
  }
}

comme note à part: les appels à Encrypt() et Decrypt() ont un paramètre booléen qui bascule entre le rembourrage OAEP et PKCS#1.5 ... vous voudrez peut-être choisir OAEP s'il est disponible dans votre situation

0 votes

De cette façon, le texte chiffré devient très long ! Existe-t-il un algorithme pour le raccourcir tout en conservant la même sécurité? (Et de préférence en ne contenant que des chiffres et des lettres majuscules)

19 votes

@SepehrM RSA n'est pas destiné à crypter de grandes quantités de données ... vous devriez peut-être envisager d'utiliser un chiffrement symétrique comme AES ou Twofish pour vos données, et RSA (ou un autre chiffrement asymétrique) pour le chiffrement de votre clé symétrique ... crypto hybride ...

1 votes

@DarkSquirrel42 C'est d'ailleurs ainsi que WannaCry est parvenu à crypter les fichiers si rapidement.

18voto

Mukund Points 87
public static string Encryption(string strText)
        {
            var publicKey = "21wEnTU+mcD2w0Lfo1Gv4rtcSWsQJQTNa6gio05AOkV/Er9w3Y13Ddo5wGtjJ19402S71HUeN0vbKILLJdRSES5MHSdJPSVrOqdrll/vLXxDxWs/U0UT1c8u6k/Ogx9hTtZxYwoeYqdhDblof3E75d9n2F0Zvf6iTb4cI7j6fMs=AQAB";

            var testData = Encoding.UTF8.GetBytes(strText);

            using (var rsa = new RSACryptoServiceProvider(1024))
            {
                try
                {
                    // client encrypting data with public key issued by server                    
                    rsa.FromXmlString(publicKey.ToString());

                    var encryptedData = rsa.Encrypt(testData, true);

                    var base64Encrypted = Convert.ToBase64String(encryptedData);

                    return base64Encrypted;
                }
                finally
                {
                    rsa.PersistKeyInCsp = false;
                }
            }
        }

        public static string Decryption(string strText)
        {
            var privateKey = "21wEnTU+mcD2w0Lfo1Gv4rtcSWsQJQTNa6gio05AOkV/Er9w3Y13Ddo5wGtjJ19402S71HUeN0vbKILLJdRSES5MHSdJPSVrOqdrll/vLXxDxWs/U0UT1c8u6k/Ogx9hTtZxYwoeYqdhDblof3E75d9n2F0Zvf6iTb4cI7j6fMs=AQAB/aULPE6jd5IkwtWXmReyMUhmI/nfwfkQSyl7tsg2PKdpcxk4mpPZUdEQhHQLvE84w2DhTyYkPHCtq/mMKE3MHw==3WV46X9Arg2l9cxb67KVlNVXyCqc/w+LWt/tbhLJvV2xCF/0rWKPsBJ9MC6cquaqNPxWWEav8RAVbmmGrJt51Q==8TuZFgBMpBoQcGUoS2goB4st6aVq1FcG0hVgHhUI0GMAfYFNPmbDV3cY2IBt8Oj/uYJYhyhlaj5YTqmGTYbATQ==FIoVbZQgrAUYIHWVEYi/187zFd7eMct/Yi7kGBImJStMATrluDAspGkStCWe4zwDDmdam1XzfKnBUzz3AYxrAQ==QPU3Tmt8nznSgYZ+5jUo9E0SfjiTu435ihANiHqqjasaUNvOHKumqzuBZ8NRtkUhS6dsOEb8A2ODvy7KswUxyA==cgoRoAUpSVfHMdYXW9nA3dfX75dIamZnwPtFHq80ttagbIe4ToYYCcyUz5NElhiNQSESgS5uCgNWqWXt5PnPu4XmCXx6utco1UVH8HGLahzbAnSy6Cj3iUIQ7Gj+9gQ7PkC434HTtHazmxVgIR5l56ZjoQ8yGNCPZnsdYEmhJWk=";

            var testData = Encoding.UTF8.GetBytes(strText);

            using (var rsa = new RSACryptoServiceProvider(1024))
            {
                try
                {                    
                    var base64Encrypted = strText;

                    // server decrypting data with private key                    
                    rsa.FromXmlString(privateKey);

                    var resultBytes = Convert.FromBase64String(base64Encrypted);
                    var decryptedBytes = rsa.Decrypt(resultBytes, true);
                    var decryptedData = Encoding.UTF8.GetString(decryptedBytes);
                    return decryptedData.ToString();
                }
                finally
                {
                    rsa.PersistKeyInCsp = false;
                }
            }
        }

0 votes

Comment avez-vous généré les clés ?

1 votes

@Sana Vous pouvez obtenir les clés en utilisant la méthode ToXmlString . Consultez cette réponse.

0 votes

Quelles sont les valeurs des valeurs sauf Modulus, si les clés sont générées via cryptotools.net/rsagen?

6voto

CraftedGaming Points 133

Honnetement, j'ai du mal à le mettre en œuvre car il n'y a presque aucun tutoriel que j'ai cherché qui montre comment écrire les clés dans les fichiers. La réponse acceptée était "correcte". Mais pour moi, j'ai dû l'améliorer pour que les deux clés soient enregistrées dans deux fichiers séparés. J'ai écrit une classe d'aide donc vous n'avez qu'à copier-coller. J'espère que cela vous aide lol.

using Microsoft.Win32;
using System;
using System.IO;
using System.Security.Cryptography;

namespace RsaCryptoExample
{
    class RSAFileHelper
    {
        readonly string pubKeyPath = "public.key";//changement si nécessaire
        readonly string priKeyPath = "private.key";//changement si nécessaire
        public void MakeKey()
        {
            //créer un nouveau CSP avec une nouvelle paire de clés rsa de 2048 bits
            RSACryptoServiceProvider csp = new RSACryptoServiceProvider(2048);

            //comment obtenir la clé privée
            RSAParameters privKey = csp.ExportParameters(true);

            //et la clé publique ...
            RSAParameters pubKey = csp.ExportParameters(false);
            //conversion de la clé publique en une représentation de chaîne
            string pubKeyString;
            {
                //nous avons besoin d'un tampon
                var sw = new StringWriter();
                //nous avons besoin d'un sérialiseur
                var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
                //sérialiser la clé dans le flux
                xs.Serialize(sw, pubKey);
                //obtenir la chaîne du flux
                pubKeyString = sw.ToString();
                File.WriteAllText(pubKeyPath, pubKeyString);
            }
            string privKeyString;
            {
                //nous avons besoin d'un tampon
                var sw = new StringWriter();
                //nous avons besoin d'un sérialiseur
                var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
                //sérialiser la clé dans le flux
                xs.Serialize(sw, privKey);
                //obtenir la chaîne du flux
                privKeyString = sw.ToString();
                File.WriteAllText(priKeyPath, privKeyString);
            }
        }
        public void EncryptFile(string filePath)
        {
            //conversion de la clé publique en une représentation de chaîne
            string pubKeyString;
            {
                using (StreamReader reader = new StreamReader(pubKeyPath)){pubKeyString = reader.ReadToEnd();}
            }
            //obtenir un flux à partir de la chaîne
            var sr = new StringReader(pubKeyString);

            //nous avons besoin d'un désérialiseur
            var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));

            //récupérer l'objet à partir du flux
            RSACryptoServiceProvider csp = new RSACryptoServiceProvider();
            csp.ImportParameters((RSAParameters)xs.Deserialize(sr));
            byte[] bytesPlainTextData = File.ReadAllBytes(filePath);

            //appliquer le rembourrage pkcs#1.5 et chiffrer nos données
            var bytesCipherText = csp.Encrypt(bytesPlainTextData, false);
            //nous voulons peut-être une représentation de chaîne de notre texte chiffré... base64 fera l'affaire
            string encryptedText = Convert.ToBase64String(bytesCipherText);
            File.WriteAllText(filePath, encryptedText);
        }
        public void DecryptFile(string filePath)
        {
            //nous voulons déchiffrer, donc nous avons besoin d'un csp et charger notre clé privée
            RSACryptoServiceProvider csp = new RSACryptoServiceProvider();

            string privKeyString;
            {
                privKeyString = File.ReadAllText(priKeyPath);
                //obtenir un flux à partir de la chaîne
                var sr = new StringReader(privKeyString);
                //nous avons besoin d'un désérialiseur
                var xs = new System.Xml.Serialization.XmlSerializer(typeof(RSAParameters));
                //récupérer l'objet à partir du flux
                RSAParameters privKey = (RSAParameters)xs.Deserialize(sr);
                csp.ImportParameters(privKey);
            }
            string encryptedText;
            using (StreamReader reader = new StreamReader(filePath)) { encryptedText = reader.ReadToEnd(); }
            byte[] bytesCipherText = Convert.FromBase64String(encryptedText);

            //déchiffrer et supprimer le rembourrage pkcs#1.5
            byte[] bytesPlainTextData = csp.Decrypt(bytesCipherText, false);

            //récupérons notre texte en clair original...
            File.WriteAllBytes(filePath, bytesPlainTextData);
        }
    }
}

2 votes

Pourquoi utilisez-vous XmlSerializer ?

0voto

Sediq AL-Badji Points 1

Pour les big data

public class RsaService : System.IDisposable
{
public delegate int TransformBlockCall(System.ReadOnlySpan data, System.Span destination);

private readonly RSA _encoder;
private readonly RSAEncryptionPadding _padding;

private readonly TransformBlockCall _encryptBlockCall;
private readonly TransformBlockCall _decryptBlockCall;

private int _encrypt_InputBlockSize;
private int _encrypt_OutputBlockSize;
private int _decrypt_InputBlockSize;
private int _decrypt_OutputBlockSize;

public RsaService(RSA encoder) {
    if(encoder == null)
        throw new System.ArgumentNullException(nameof(encoder));
    _encoder = encoder;

    _padding = RSAEncryptionPadding.Pkcs1;

    _encryptBlockCall = new TransformBlockCall(EncryptBlock);
    _decryptBlockCall = new TransformBlockCall(DecryptBlock);

    OnEndSetParameters();
}

private void OnEndSetParameters() {
    _encrypt_InputBlockSize = GetSizeOutputEncryptOfKeySize(_encoder.KeySize);
    _encrypt_OutputBlockSize = _encoder.KeySize / 8;
    _decrypt_InputBlockSize = _encrypt_OutputBlockSize;
    _decrypt_OutputBlockSize = _encrypt_OutputBlockSize;
}

public void ImportParameters(RSAParameters parameters) {
    _encoder.ImportParameters(parameters);
    OnEndSetParameters();
}

public byte[] Encrypt(byte[] data) {
    if(data == null) throw new System.ArgumentNullException(nameof(data));
    if(data.Length == 0) return data;
    int outputLength = GetEncryptOutputMaxByteCount(data.Length);
    byte[] outputData = new byte[outputLength];
    Encrypt(data, outputData);
    return outputData;
}

public byte[] Decrypt(byte[] data) {
    if(data == null) throw new System.ArgumentNullException(nameof(data));
    if(data.Length == 0) return data;
    int maxOutputLength = GetDecryptOutputMaxByteCount(data.Length);
    byte[] outputData = new byte[maxOutputLength];
    int actual_OutputLength = Decrypt(data, outputData);
    if(maxOutputLength > actual_OutputLength)
        System.Array.Resize(ref outputData, actual_OutputLength);
    return outputData;
}

public int Encrypt(System.ReadOnlySpan data, System.Span destination) {
#if DEBUG
    int inputBlockSize = _encrypt_InputBlockSize;
    int outputBlockSize = _encoder.KeySize / 8;
    int blockCount = (data.Length / inputBlockSize);
    if(data.Length % inputBlockSize != 0)
        blockCount++;
    System.Diagnostics.Debug.Assert((blockCount * outputBlockSize) <= destination.Length);
#endif

    if(data.Length > _encrypt_InputBlockSize)
        return TransformFinal(_encryptBlockCall, data, destination, _encrypt_InputBlockSize);
    else
        return _encryptBlockCall(data, destination);
}

public int Decrypt(System.ReadOnlySpan data, System.Span destination) {
    if(data.Length > _decrypt_InputBlockSize)
        return TransformFinal(_decryptBlockCall, data, destination, _decrypt_InputBlockSize);
    else
        return _decryptBlockCall(data, destination);
}

private int EncryptBlock(System.ReadOnlySpan data, System.Span destination) => _encoder.Encrypt(data, destination, _padding);
private int DecryptBlock(System.ReadOnlySpan data, System.Span destination) => _encoder.Decrypt(data, destination, _padding);

public int GetEncryptOutputMaxByteCount(int inputCount) => GetBlockCount(inputCount, _encrypt_InputBlockSize) * _encrypt_OutputBlockSize;
public int GetDecryptOutputMaxByteCount(int inputCount) => GetBlockCount(inputCount, _decrypt_InputBlockSize) * _decrypt_OutputBlockSize;
public void Dispose() {
    _encoder.Dispose();
    System.GC.SuppressFinalize(this);
}

#region Methods_Helper

public static RsaService Create(RSAParameters parameters) => new RsaService(RSA.Create(parameters));

public static RsaService Create() => new RsaService(RSA.Create());

// [keySize] ÷ 8 - [11 bytes for padding] = Result
// Exsimple: [2048 key size] ÷ 8 - [11 bytes for padding] = 245
public static int GetSizeOutputEncryptOfKeySize(int keySize) => (keySize / 8) - 11;

private static int GetBlockCount(int dataLength,int inputBlockSize) {
    int blockCount = (dataLength / inputBlockSize);
    if(dataLength % inputBlockSize != 0)
        blockCount++;
    return blockCount;
}

public static int TransformFinal(TransformBlockCall transformBlockCall, System.ReadOnlySpan data, System.Span destination, int inputBlockSize) {

    int blockCount = GetBlockCount(data.Length, inputBlockSize);

    int data_writtenCount = 0;
    int destination_writtenCount = 0;
    while(blockCount-- > 0) {
        if(blockCount == 0) {
            inputBlockSize = data.Length - data_writtenCount;
            if(inputBlockSize == 0) break;
        }
        destination_writtenCount += transformBlockCall(data: data.Slice(data_writtenCount, inputBlockSize)
            , destination: destination.Slice(destination_writtenCount));
        data_writtenCount += inputBlockSize;
    }
    return destination_writtenCount;
}

public static (RSAParameters keyPublic, RSAParameters keyPrivate) GenerateKeyPair(int keySize = 2048) {
    RSAParameters keyPriv;
    RSAParameters keyPub;
    using(var rsa = RSA.Create(keySize)) {
        keyPriv = rsa.ExportParameters(true);
        keyPub = rsa.ExportParameters(false);
    }
    return (keyPub, keyPriv);
}

#endregion Methods_Helper

}

public static class Program
{

static void Main() {

    var (keyPublic, keyPrivate) = RsaService.GenerateKeyPair();

    var encryptor = RsaService.Create(keyPublic);
    var decryptor = RsaService.Create(keyPrivate);
    string originalText = "";
    for(int i = 0; i < 1000; i++) {
        originalText += "ABC123456789";
    }
    byte[] inputData = Encoding.UTF8.GetBytes(originalText); // données aléatoires pour le test
    System.Console.WriteLine("inputData.Length: {0}", inputData.Length);

    var encryptedData = encryptor.Encrypt(inputData);

    System.Console.WriteLine("encryptedData.Length: {0}", encryptedData.Length);

    byte[] decryptedData = decryptor.Decrypt(encryptedData);
    string decryptedText = Encoding.UTF8.GetString(decryptedData);

    System.Console.WriteLine("status: {0}", decryptedText == originalText);

}
}

0voto

Tigran Points 21

Je vais partager mon code très simple à des fins d'exemple. J'espère que cela aidera quelqu'un comme moi cherchant une référence rapide de code. Mon objectif était de recevoir une signature rsa du backend, puis de la valider contre une chaîne d'entrée à l'aide de la clé publique et de la stocker localement pour de futures vérifications périodiques. Voici la partie principale utilisée pour la vérification de la signature :

        ...
        var signature = Get(url); // signature encodée en base64 reçue du serveur
        var inputtext= "inputtext"; // c'est le texte principal pour lequel la signature a été créée
        bool result = VerifySignature(inputtext, signature);
        ...

    private bool VerifySignature(string input, string signature)
    {
        var result = false;
        using (var cps=new RSACryptoServiceProvider())
        {
            // conversion des entrées et de la signature en tableaux de bytes à passer à la méthode VerifyData de rsa pour vérifier que le texte d'entrée a été signé à l'aide de la clé privée correspondant à la clé publique que nous avons ci-dessous
            byte[] inputtextBytes = Encoding.UTF8.GetBytes(input);
            byte[] signatureBytes  = Convert.FromBase64String(signature);

            cps.FromXmlString("........"); // clé publique formatée en xml
            result = cps.VerifyData(inputtextBytes , new SHA1CryptoServiceProvider(), signatureBytes  );
        }

        return result;
    }

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