How to use public and private key encryption technique in C#
Asked Answered
T

3

50

I want to encrypt data using public/private key technique. I mean, encrypt with the public key of receiver and the receiver can decrypt with their own private key.

How can I do that? Do you have any suggestion or sample code ?

Tical answered 28/8, 2013 at 10:43 Comment(0)
S
49

Code example:

private static string _privateKey;
private static string _publicKey;
private static UnicodeEncoding _encoder = new UnicodeEncoding();

private static void RSA()
{
  var rsa = new RSACryptoServiceProvider();
  _privateKey = rsa.ToXmlString(true);
  _publicKey = rsa.ToXmlString(false);

  var text = "Test1";
  Console.WriteLine("RSA // Text to encrypt: " + text);
  var enc = Encrypt(text);
  Console.WriteLine("RSA // Encrypted Text: " + enc);
  var dec = Decrypt(enc);
  Console.WriteLine("RSA // Decrypted Text: " + dec);
}

public static string Decrypt(string data)
{
  var rsa = new RSACryptoServiceProvider();
  var dataArray = data.Split(new char[] { ',' });
  byte[] dataByte = new byte[dataArray.Length];
  for (int i = 0; i < dataArray.Length; i++)
  {
    dataByte[i] = Convert.ToByte(dataArray[i]);
  }

  rsa.FromXmlString(_privateKey);
  var decryptedByte = rsa.Decrypt(dataByte, false);
  return _encoder.GetString(decryptedByte);
}

public static string Encrypt(string data)
{
  var rsa = new RSACryptoServiceProvider();
  rsa.FromXmlString(_publicKey);
  var dataToEncrypt = _encoder.GetBytes(data);
  var encryptedByteArray = rsa.Encrypt(dataToEncrypt, false).ToArray();
  var length = encryptedByteArray.Count();
  var item = 0;
  var sb = new StringBuilder();
  foreach (var x in encryptedByteArray)
  {
    item++;
    sb.Append(x);

    if (item < length)
      sb.Append(",");
  }

  return sb.ToString();
}
Spondee answered 28/8, 2013 at 11:9 Comment(5)
The RSACryptoServiceProvider is not intended to encrypted random blobs of data (data size is limited to keysize). Also in general you should use OAEP padding and not explictly switch it PCKS1v1.5 to help protect against chosen ciphertext attacks. No one should use this sample code.Passer
@ jbtule : so tell us what would be best to encrypt decrypt large data with private & public key concept?Goree
@Goree From what I've gathered, you generate a a symmetric key and then transmit this encrypting it with the receiver's public key. the receiver then decrypts it with their private key and then you do your large data encrypted with the shared symmetric key. you could update the symmetric key every n transmissions. I think this is how SSL works (I'm sure that's a very simplified description)Kalikow
You might want to check learn.microsoft.com/en-us/dotnet/standard/security/… for a good example.Gpo
IMO it's better to convert byte[] to base64 instead of combining comma separated strings. So in Encrypt() after 'var encryptedByteArray = rsa.Encrypt(dataToEncrypt, false).ToArray();' I'd just use 'return Convert.ToBase64String(encryptedByteArray);'. So we get base64 string. Then in Decrypt you just simply use opposite method 'Convert.FromBase64String()' to get byte[] array back.Replication
B
1

this example: https://learn.microsoft.com/en-us/dotnet/standard/security/walkthrough-creating-a-cryptographic-application

found by #NKCSS is very good. I built a test app with it that passed code review of security officers.

Simply copying the relevant parts from the example incase the link will change:

// Declare global objects
//
// Declare CspParmeters and RsaCryptoServiceProvider
// objects with global scope of your Form class.
readonly CspParameters _cspp = new CspParameters();
RSACryptoServiceProvider _rsa;

// Path variables for source, encryption, and
// decryption folders. Must end with a backslash.
const string EncrFolder = @"c:\Encrypt\";
const string DecrFolder = @"c:\Decrypt\";
const string SrcFolder = @"c:\docs\";

// Public key file
const string PubKeyFile = @"c:\encrypt\rsaPublicKey.txt";

// Key container name for
// private/public key value pair.
const string KeyName = "Key01";

private void buttonCreateAsmKeys_Click(object sender, EventArgs e)
{
    // Stores a key pair in the key container.
    _cspp.KeyContainerName = KeyName;
    _rsa = new RSACryptoServiceProvider(_cspp)
    {
        PersistKeyInCsp = true
    };

    label1.Text = _rsa.PublicOnly
        ? $"Key: {_cspp.KeyContainerName} - Public Only"
        : $"Key: {_cspp.KeyContainerName} - Full Key Pair";
}
private void buttonEncryptFile_Click(object sender, EventArgs e)
{
    if (_rsa is null)
    {
        MessageBox.Show("Key not set.");
    }
    else
    {
        // Display a dialog box to select a file to encrypt.
        _encryptOpenFileDialog.InitialDirectory = SrcFolder;
        if (_encryptOpenFileDialog.ShowDialog() == DialogResult.OK)
        {
            string fName = _encryptOpenFileDialog.FileName;
            if (fName != null)
            {
                // Pass the file name without the path.
                EncryptFile(new FileInfo(fName));
            }
        }
    }
}

// Add the following EncryptFile method to the form.

private void EncryptFile(FileInfo file)
{
    // Create instance of Aes for
    // symmetric encryption of the data.
    Aes aes = Aes.Create();
    ICryptoTransform transform = aes.CreateEncryptor();

    // Use RSACryptoServiceProvider to
    // encrypt the AES key.
    // rsa is previously instantiated:
    //    rsa = new RSACryptoServiceProvider(cspp);
    byte[] keyEncrypted = _rsa.Encrypt(aes.Key, false);

    // Create byte arrays to contain
    // the length values of the key and IV.
    int lKey = keyEncrypted.Length;
    byte[] LenK = BitConverter.GetBytes(lKey);
    int lIV = aes.IV.Length;
    byte[] LenIV = BitConverter.GetBytes(lIV);

    // Write the following to the FileStream
    // for the encrypted file (outFs):
    // - length of the key
    // - length of the IV
    // - ecrypted key
    // - the IV
    // - the encrypted cipher content

    // Change the file's extension to ".enc"
    string outFile = 
        Path.Combine(EncrFolder, Path.ChangeExtension(file.Name, ".enc"));

    using (var outFs = new FileStream(outFile, FileMode.Create))
    {
        outFs.Write(LenK, 0, 4);
        outFs.Write(LenIV, 0, 4);
        outFs.Write(keyEncrypted, 0, lKey);
        outFs.Write(aes.IV, 0, lIV);

        // Now write the cipher text using
        // a CryptoStream for encrypting.
        using (var outStreamEncrypted = 
            new CryptoStream(outFs, transform, CryptoStreamMode.Write))
        {
            // By encrypting a chunk at
            // a time, you can save memory
            // and accommodate large files.
            int count = 0;
            int offset = 0;

            // blockSizeBytes can be any arbitrary size.
            int blockSizeBytes = aes.BlockSize / 8;
            byte[] data = new byte[blockSizeBytes];
            int bytesRead = 0;

            using (var inFs = new FileStream(file.FullName, FileMode.Open))
            {
                do
                {
                    count = inFs.Read(data, 0, blockSizeBytes);
                    offset += count;
                    outStreamEncrypted.Write(data, 0, count);
                    bytesRead += blockSizeBytes;
                } while (count > 0);
            }
            outStreamEncrypted.FlushFinalBlock();
        }
    }
}

// Then to Decrypt a file -
private void buttonDecryptFile_Click(object sender, EventArgs e)
{
    if (_rsa is null)
    {
        MessageBox.Show("Key not set.");
    }
    else
    {
        // Display a dialog box to select the encrypted file.
        _decryptOpeFileDialog.InitialDirectory = EncrFolder;
        if (_decryptOpeFileDialog.ShowDialog() == DialogResult.OK)
        {
            string fName = _decryptOpeFileDialog.FileName;
            if (fName != null)
            {
                DecryptFile(new FileInfo(fName));
            }
        }
    }
}

// And -
private void DecryptFile(FileInfo file)
{
    // Create instance of Aes for
    // symmetric decryption of the data.
    Aes aes = Aes.Create();

    // Create byte arrays to get the length of
    // the encrypted key and IV.
    // These values were stored as 4 bytes each
    // at the beginning of the encrypted package.
    byte[] LenK = new byte[4];
    byte[] LenIV = new byte[4];

    // Construct the file name for the decrypted file.
    string outFile =
        Path.ChangeExtension(file.FullName.Replace("Encrypt", "Decrypt"), ".txt");

    // Use FileStream objects to read the encrypted
    // file (inFs) and save the decrypted file (outFs).
    using (var inFs = new FileStream(file.FullName, FileMode.Open))
    {
        inFs.Seek(0, SeekOrigin.Begin);
        inFs.Read(LenK, 0, 3);
        inFs.Seek(4, SeekOrigin.Begin);
        inFs.Read(LenIV, 0, 3);

        // Convert the lengths to integer values.
        int lenK = BitConverter.ToInt32(LenK, 0);
        int lenIV = BitConverter.ToInt32(LenIV, 0);

        // Determine the start postition of
        // the ciphter text (startC)
        // and its length(lenC).
        int startC = lenK + lenIV + 8;
        int lenC = (int)inFs.Length - startC;

        // Create the byte arrays for
        // the encrypted Aes key,
        // the IV, and the cipher text.
        byte[] KeyEncrypted = new byte[lenK];
        byte[] IV = new byte[lenIV];

        // Extract the key and IV
        // starting from index 8
        // after the length values.
        inFs.Seek(8, SeekOrigin.Begin);
        inFs.Read(KeyEncrypted, 0, lenK);
        inFs.Seek(8 + lenK, SeekOrigin.Begin);
        inFs.Read(IV, 0, lenIV);

        Directory.CreateDirectory(DecrFolder);
        // Use RSACryptoServiceProvider
        // to decrypt the AES key.
        byte[] KeyDecrypted = _rsa.Decrypt(KeyEncrypted, false);

        // Decrypt the key.
        ICryptoTransform transform = aes.CreateDecryptor(KeyDecrypted, IV);

        // Decrypt the cipher text from
        // from the FileSteam of the encrypted
        // file (inFs) into the FileStream
        // for the decrypted file (outFs).
        using (var outFs = new FileStream(outFile, FileMode.Create))
        {
            int count = 0;
            int offset = 0;

            // blockSizeBytes can be any arbitrary size.
            int blockSizeBytes = aes.BlockSize / 8;
            byte[] data = new byte[blockSizeBytes];

            // By decrypting a chunk a time,
            // you can save memory and
            // accommodate large files.

            // Start at the beginning
            // of the cipher text.
            inFs.Seek(startC, SeekOrigin.Begin);
            using (var outStreamDecrypted =
                new CryptoStream(outFs, transform, CryptoStreamMode.Write))
            {
                do
                {
                    count = inFs.Read(data, 0, blockSizeBytes);
                    offset += count;
                    outStreamDecrypted.Write(data, 0, count);
                } while (count > 0);

                outStreamDecrypted.FlushFinalBlock();
            }
        }
    }
}

// you can also try to Export a public key:

void buttonExportPublicKey_Click(object sender, EventArgs e)
{
    // Save the public key created by the RSA
    // to a file. Caution, persisting the
    // key to a file is a security risk.
    Directory.CreateDirectory(EncrFolder);
    using (var sw = new StreamWriter(PubKeyFile, false))
    {
        sw.Write(_rsa.ToXmlString(false));
    }
}

// or Import a public key
void buttonImportPublicKey_Click(object sender, EventArgs e)
{
    using (var sr = new StreamReader(PubKeyFile))
    {
        _cspp.KeyContainerName = KeyName;
        _rsa = new RSACryptoServiceProvider(_cspp);

        string keytxt = sr.ReadToEnd();
        _rsa.FromXmlString(keytxt);
        _rsa.PersistKeyInCsp = true;

        label1.Text = _rsa.PublicOnly
            ? $"Key: {_cspp.KeyContainerName} - Public Only"
            : $"Key: {_cspp.KeyContainerName} - Full Key Pair";
    }
}

// (Get a private key)
private void buttonGetPrivateKey_Click(object sender, EventArgs e)
{
    _cspp.KeyContainerName = KeyName;
    _rsa = new RSACryptoServiceProvider(_cspp)
    {
        PersistKeyInCsp = true
    };

    label1.Text = _rsa.PublicOnly
        ? $"Key: {_cspp.KeyContainerName} - Public Only"
        : $"Key: {_cspp.KeyContainerName} - Full Key Pair";
}

Note that you may wish to switch from using Aes since this is just a code example (by Microsoft), and also consider replacing the use of Rijndael Class for encryption.

Blotto answered 11/1, 2022 at 15:21 Comment(0)
Y
1

Simplified version of Erwin's code:

  • Used the Base64 string conversion (Like Marcin has suggested)
  • Removed the var keyword.
using System.Security.Cryptography;
using System.Text;

namespace Test_RsaKeyEncryption
{
    public class RSA
    {
        private static string _privateKey = null!;
        private static string _publicKey = null!;

        public static void Test_RSA()
        {
            RSACryptoServiceProvider rsa = new();
            _privateKey = rsa.ToXmlString(true);
            _publicKey = rsa.ToXmlString(false);
            Console.WriteLine("Private Key: " + _privateKey + Environment.NewLine);
            Console.WriteLine("Public Key: " + _publicKey + Environment.NewLine);

            string text = "Stringa da Criptare@#[]123123";
            Console.WriteLine("Text to encrypt: " + Environment.NewLine + text + Environment.NewLine);
            string enc = Encrypt(text);
            Console.WriteLine("Encrypted Text: " + Environment.NewLine + enc + Environment.NewLine);
            string dec = Decrypt(enc);
            Console.WriteLine("Decrypted Text: " + Environment.NewLine + dec + Environment.NewLine);
        }

        private static string Encrypt(string data)
        {
            RSACryptoServiceProvider rsa = new();
            rsa.FromXmlString(_publicKey);

            byte[] dataToEncrypt = Encoding.ASCII.GetBytes(data);
            byte[] encryptedByteArray = rsa.Encrypt(dataToEncrypt, false).ToArray();

            return Convert.ToBase64String(encryptedByteArray);
        }

        private static string Decrypt(string data)
        {
            RSACryptoServiceProvider rsa = new();
            rsa.FromXmlString(_privateKey);

            byte[] dataByte = Convert.FromBase64String(data);
            byte[] decryptedByte = rsa.Decrypt(dataByte, false);

            return Encoding.UTF8.GetString(decryptedByte);
        }
    }
}
Yvor answered 13/7, 2022 at 7:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.