SoFunction
Updated on 2025-03-02

C# Implementing RSA asymmetric encryption algorithm

Public and private keys

Public keys and private keys are paired. Generally, we think of public key encryption, private key decryption, private key signature, and public key verification. Some people say it is private key encryption, but it is wrong when decrypting the public key.

There are many ways to generate public and private keys, which can be generated through programs (specifically implemented below), and can be achieved through the openssl tool:

    # Generate a private key. It is recommended to use a 1024-bit secret key. The secret key is saved in the pem format to the file specified by the -out parameter, and adopts the PKCS1 format.    openssl genrsa -out  1024 
    # Generate the public key corresponding to the private key, and the Subject Public Key is generated, which is generally used with the PKCS8 format private key.    openssl rsa -in  -pubout -out   

There are generally two formats for RSA to generate public and private keys: PKCS1 and PKCS8. The secret key generated by the above command is in PKCS1 format, while the public key is Subject Public Key. It is generally used with the PKCS8 format private key, so it may involve the conversion between PKCS1 and PKCS8:

    # Convert PKCS1 format private key to PKCS8 format private key, and output the private key directly to the file specified by the -out parameter    openssl pkcs8 -topk8 -inform PEM -in  -outform pem -nocrypt -out rsa_pkcs8.pem
    # Convert PKCS8 format private key to PKCS1 format private key, and output the private key directly to the file specified by the -out parameter    openssl rsa -in rsa_pkcs8.pem -out rsa_pkcs1.pem

    # Convert the PKCS1 format public key to the PKCS8 format public key, and output the converted content directly    openssl rsa -pubin -in  -RSAPublicKey_out
    # Convert the PKCS8 format public key to the PKCS1 format public key, and output the converted content directly    openssl rsa -RSAPublicKey_in -pubout -in 

In reality, we often obtain public and private keys from pem, crt, and pfx files. For the production of crt and pfx, we can refer to:Simple ssl certificates and use them in nginx and IIS, or use ready-made:/s/1MJ5YmuZiLBnf-DfNR_6D7A(Extraction code: c6tj), passwords are: 123456

C# implementation

In order to facilitate reading of public and private keys in pem, crt, and pfx files, I used a third-party package here: You can use NuGet to install: Install-Package

Next, here I encapsulate an RsaHelper helper class to implement various RSA encryption processes:

using ;
using System;
using ;
using ;
using ;
using .X509Certificates;
using ;

namespace ConsoleApp1
{
    public class RsaHelper
    {

        #region key
        /// <summary>
        /// Save the key to the Pem file        /// </summary>
        /// <param name="isPrivateKey"></param>
        /// <param name="buffer"></param>
        /// <param name="pemFileName"></param>
        /// <returns></returns>
        public static void WriteToPem(byte[] buffer, bool isPrivateKey, string pemFileName)
        {
            PemObject pemObject = new PemObject(isPrivateKey ? "RSA PRIVATE KEY" : "RSA PUBLIC KEY", buffer);
            if ((pemFileName))
            {
                (pemFileName);
            }
            using (var fileStream = new FileStream(pemFileName, , ))
            {
                using (var sw = new StreamWriter(fileStream))
                {
                    var writer = new PemWriter(sw);
                    (pemObject);
                    ();
                }
            }
        }
        /// <summary>
        /// Read the secret key from the Pem file        /// </summary>
        /// <param name="pemFileName"></param>
        /// <returns></returns>
        public static byte[] ReadFromPem(string pemFileName)
        {
            using (var fileStream = new FileStream(pemFileName, , ))
            {
                using (var sw = new StreamReader(fileStream))
                {
                    var writer = new PemReader(sw);
                    return ().Content;
                }
            }
        }

        /// <summary>
        /// Read the secret key from xml        /// </summary>
        /// <param name="xml"></param>
        /// <param name="isPrivateKey"></param>
        /// <param name="usePkcs8"></param>
        /// <returns></returns>
        public static byte[] ReadFromXml(string xml, bool isPrivateKey, bool usePkcs8)
        {
            using (var rsa = new RSACryptoServiceProvider())
            {
                (xml);
                if (isPrivateKey)
                {
                    return usePkcs8 ? rsa.ExportPkcs8PrivateKey() : ();
                }
                return usePkcs8 ? () : ();
            }
        }
        /// <summary>
        /// Save the key to xml        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="isPrivateKey"></param>
        /// <param name="usePkcs8"></param>
        /// <returns></returns>
        public static string WriteToXml(byte[] buffer, bool isPrivateKey, bool usePkcs8)
        {
            using (var rsa = CreateRSACryptoServiceProvider(buffer, isPrivateKey, usePkcs8))
            {
                return (isPrivateKey);
            }
        }

        /// <summary>
        /// Get the key of RSA asymmetric encryption        /// </summary>
        /// <param name="publicKey"></param>
        /// <param name="privateKey"></param>
        public static void GenerateRsaKey(bool usePKCS8, out byte[] publicKey, out byte[] privateKey)
        {
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
            {
                 = 1024;//1024 bit                if (usePKCS8)
                {
                    //Export using pkcs8 fill                    publicKey = ();//Public key                    privateKey = rsa.ExportPkcs8PrivateKey();//Private Key                }
                else
                {
                    //Export using pkcs1 fill                    publicKey = ();//Public key                    privateKey = ();//Private Key                }
            }
        }
        /// <summary>
        /// Get RSA asymmetric encryption key from Pfx file        /// </summary>
        /// <param name="pfxFileName"></param>
        /// <param name="publicKey"></param>
        /// <param name="privateKey"></param>
        public static void ReadFromPfx(string pfxFileName, string password, out byte[] publicKey, out byte[] privateKey)
        {
            X509Certificate2 x509Certificate2 = new X509Certificate2(pfxFileName, password, );
            publicKey = ().ExportRSAPublicKey();
            privateKey = ().ExportRSAPrivateKey();
        }
        /// <summary>
        /// Read the public key from the Crt file        /// </summary>
        /// <param name="crtFileName"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public static byte[] ReadPublicKeyFromCrt(string crtFileName, string password)
        {
            X509Certificate2 x509Certificate2 = new X509Certificate2(crtFileName, password, );
            var publicKey = ().ExportRSAPublicKey();
            return publicKey;
        }
        #endregion

        #region Pkcs1 and Pkcs8

        /// <summary>
        /// Pkcs1 to Pkcs8        /// </summary>
        /// <param name="isPrivateKey"></param>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public static byte[] Pkcs1ToPkcs8(bool isPrivateKey, byte[] buffer)
        {
            using (var rsa = new RSACryptoServiceProvider())
            {
                if (isPrivateKey)
                {
                    (buffer, out _);
                    return rsa.ExportPkcs8PrivateKey();
                }
                else
                {
                    (buffer, out _);
                    return ();
                }
            }
        }
        /// <summary>
        /// Pkcs8 to Pkcs1        /// </summary>
        /// <param name="isPrivateKey"></param>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public static byte[] Pkcs8ToPkcs1(bool isPrivateKey, byte[] buffer)
        {
            using (var rsa = new RSACryptoServiceProvider())
            {
                if (isPrivateKey)
                {
                    rsa.ImportPkcs8PrivateKey(buffer, out _);
                    return ();
                }
                else
                {
                    (buffer, out _);
                    return ();
                }
            }
        }

        #endregion

        #region RSA

        /// <summary>
        /// Get an RSACryptoServiceProvider        /// </summary>
        /// <param name="isPrivateKey"></param>
        /// <param name="buffer"></param>
        /// <param name="usePkcs8"></param>
        /// <returns></returns>
        public static RSACryptoServiceProvider CreateRSACryptoServiceProvider(byte[] buffer, bool isPrivateKey, bool usePkcs8 = false)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            if (isPrivateKey)
            {
                if (usePkcs8)
                    rsa.ImportPkcs8PrivateKey(buffer, out _);
                else
                    (buffer, out _);
            }
            else
            {
                if (usePkcs8)
                    (buffer, out _);
                else
                    (buffer, out _);
            }
            return rsa;
        }

        /// <summary>
        /// RSA public key encryption        /// </summary>
        /// <param name="value">plain text to be encrypted</param>        /// <param name="publicKey">Public key</param>        /// <param name="usePkcs8">Whether to use pkcs8 to fill</param>        /// &lt;returns&gt;&lt;/returns&gt;
        public static string RsaEncrypt(string value, byte[] publicKey, bool usePkcs8 = false)
        {
            if ((value)) return value;

            using (RSACryptoServiceProvider rsa = CreateRSACryptoServiceProvider(publicKey, false, usePkcs8))
            {
                var buffer = Encoding.(value);
                buffer = (buffer, false);

                //Export data using hex format                StringBuilder result = new StringBuilder();
                foreach (byte b in buffer)
                {
                    ("{0:x2}", b);
                }
                return ();
                //or use the following output                //return (buffer).Replace("-", "").ToLower();
            }
        }
        /// &lt;summary&gt;
        /// Decrypt RSA private key        /// &lt;/summary&gt;
        /// <param name="value">ciphertext</param>        /// <param name="privateKey">Private Key</param>        /// <param name="usePkcs8">Whether to use pkcs8 to fill</param>        /// &lt;returns&gt;&lt;/returns&gt;
        public static string RsaDecrypt(string value, byte[] privateKey, bool usePkcs8 = false)
        {
            if ((value)) return value;

            using (RSACryptoServiceProvider rsa = CreateRSACryptoServiceProvider(privateKey, true, usePkcs8))
            {
                //Convert hex format data to byte array                var buffer = new byte[ / 2];
                for (var i = 0; i &lt; ; i++)
                {
                    buffer[i] = (byte)Convert.ToInt32((i * 2, 2), 16);
                }
                buffer = (buffer, false);
                return Encoding.(buffer);
            }
        }
        /// &lt;summary&gt;
        /// Signature generated by RSA private key        /// &lt;/summary&gt;
        /// <param name="value">Raw value</param>        /// <param name="publicKey">Public key</param>        /// <param name="halg">Signature hash algorithm: SHA, SHA1, MD5, SHA256, SHA384, SHA512</param>        /// <param name="usePkcs8">Whether to use pkcs8 to fill</param>        /// &lt;returns&gt;&lt;/returns&gt;
        public static string Sign(string value, byte[] privateKey, string halg = "MD5", bool usePkcs8 = false)
        {
            if ((value)) return value;

            using (RSACryptoServiceProvider rsa = CreateRSACryptoServiceProvider(privateKey, true, usePkcs8))
            {
                byte[] buffer = Encoding.(value);
                buffer = (buffer, (halg));

                //Export data using hex format                StringBuilder result = new StringBuilder();
                foreach (byte b in buffer)
                {
                    ("{0:x2}", b);
                }
                return ();
                //or use the following output                //return (buffer).Replace("-", "").ToLower();
            }
        }
        /// &lt;summary&gt;
        /// RSA public key verification signature        /// &lt;/summary&gt;
        /// <param name="value">Raw value</param>        /// <param name="publicKey">Public key</param>        /// <param name="signature">Signature</param>        /// <param name="halg">Signature hash algorithm: SHA, SHA1, MD5, SHA256, SHA384, SHA512</param>        /// <param name="usePkcs8">Whether to use pkcs8 to fill</param>        /// &lt;returns&gt;&lt;/returns&gt;
        public static bool Verify(string value, byte[] publicKey, string signature, string halg = "MD5", bool usePkcs8 = false)
        {
            if ((value)) return false;

            using (RSACryptoServiceProvider rsa = CreateRSACryptoServiceProvider(publicKey, false, usePkcs8))
            {
                //Convert hex format data to byte array                var buffer = new byte[ / 2];
                for (var i = 0; i &lt; ; i++)
                {
                    buffer[i] = (byte)Convert.ToInt32((i * 2, 2), 16);
                }
                return (Encoding.(value), (halg), buffer);
            }
        }
        #endregion
    }
}

You can use the public and private keys that generate RSA:

    // Generate through program    (usePKCS8, out publicKey, out privateKey);

After generating the secret key, it needs to be saved, usually saved to the pem file:

    //Save to Pem file, filePath is the file directory    (publicKey, false, (filePath, ""));
    (privateKey, true, (filePath, ""));

Also output the public and private keys into xml format:

    //Save to xml    string publicKeyXml = (publicKey, false, usePKCS8);
    string privateKeyXml = (privateKey, true, usePKCS8);

After saving it to the pem file and xml, you can also read it from it:

    //Fetch from the Pem file, filePath is the file directory    publicKey = ((filePath, ""));
    privateKey = ((filePath, ""));

    //Read from xml    publicKey = (publicKeyXml, false, usePKCS8);
    privateKey = (privateKeyXml, true, usePKCS8);

You can also read the public key from the crt certificate, and the crt file does not contain the private key, so you need to obtain the private key separately:

    //Read from the crt file, filePath is the file directory    publicKey = ((filePath, ""), "");
    privateKey = ((filePath, ""));

The pfx file contains the public and private keys, which can be read easily:

    //Read from the file (using pkcs1), filePath is the file directory    ((filePath, ""), "123456", out publicKey, out privateKey);

Sometimes we may also need to convert the key:

    // Convert Pkcs8 format public key to Pkcs1 format public key    publicKey = RsaHelper.Pkcs8ToPkcs1(false, publicKey);
    // Convert Pkcs8 format private key to Pkcs1 format private key    privateKey = RsaHelper.Pkcs8ToPkcs1(true, privateKey);
    // Convert Pkcs1 format public key to Pkcs8 format public key    publicKey = RsaHelper.Pkcs1ToPkcs8(false, publicKey);
    // Convert Pkcs1 format private key to Pkcs8 format private key    privateKey = RsaHelper.Pkcs1ToPkcs8(true, privateKey);

With the public and private keys, you can then implement encryption, decryption, signature, verification of signatures and other operations:

    string encryptText = (text, publicKey, usePKCS8);
    ($"【{text}】go through【RSA】After encryption:{encryptText}");

    string decryptText = (encryptText, privateKey, usePKCS8);
    ($"【{encryptText}】go through【RSA】After decryption:{decryptText}");

    string signature = (text, privateKey, "MD5", usePKCS8);
    ($"【{text}】go through【RSA】After signing:{signature}");

    bool result = (text, publicKey, signature, "MD5", usePKCS8);
    ($"【{text}】Signature of【{signature}】go through【RSA】After verification, the result is:{result}");

Complete demo code:

    using System;
    using ;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static void Main(string[] args)
            {
                string text = "Go up the mountain to fight tigers";
                bool usePKCS8 = true;// usePKCS8=true indicates whether it is a public and private key in PKCS8 format, otherwise the public and private key in PKCS1 format will be used.                string filePath = ();
                ($"File path:{filePath}");// Directory of files that store pem, crt, pfx, etc.                byte[] publicKey, privateKey;// Public and private keys    
                // Generate through program                (usePKCS8, out publicKey, out privateKey);
                //Fetch from the Pem file, filePath is the file directory                //publicKey = ((filePath, ""));
                //privateKey = ((filePath, ""));
    
                //Read from the file (using pkcs1), filePath is the file directory                //((filePath, ""), "123456", out publicKey, out privateKey);
                //Read from the crt file, filePath is the file directory                //publicKey = ((filePath, ""), "");
                //privateKey = ((filePath, ""));
    
                //Save to Pem file, filePath is the file directory                (publicKey, false, (filePath, ""));
                (privateKey, true, (filePath, ""));
    
                //Save to xml                string publicKeyXml = (publicKey, false, usePKCS8);
                string privateKeyXml = (privateKey, true, usePKCS8);
    
                //Read from xml                publicKey = (publicKeyXml, false, usePKCS8);
                privateKey = (privateKeyXml, true, usePKCS8);
    
                // Convert Pkcs8 format public key to Pkcs1 format public key                publicKey = RsaHelper.Pkcs8ToPkcs1(false, publicKey);
                // Convert Pkcs8 format private key to Pkcs1 format private key                privateKey = RsaHelper.Pkcs8ToPkcs1(true, privateKey);
                // Convert Pkcs1 format public key to Pkcs8 format public key                publicKey = RsaHelper.Pkcs1ToPkcs8(false, publicKey);
                // Convert Pkcs1 format private key to Pkcs8 format private key                privateKey = RsaHelper.Pkcs1ToPkcs8(true, privateKey);
    
                string encryptText = (text, publicKey, usePKCS8);
                ($"【{text}】go through【RSA】After encryption:{encryptText}");
    
                string decryptText = (encryptText, privateKey, usePKCS8);
                ($"【{encryptText}】go through【RSA】After decryption:{decryptText}");
    
                string signature = (text, privateKey, "MD5", usePKCS8);
                ($"【{text}】go through【RSA】After signing:{signature}");
    
                bool result = (text, publicKey, signature, "MD5", usePKCS8);
                ($"【{text}】Signature of【{signature}】go through【RSA】After verification, the result is:{result}");
            }
        }
    }

The above is the detailed content of c# implementing RSA asymmetric encryption algorithm. For more information about c# RSA asymmetric encryption algorithm, please pay attention to my other related articles!