C# RSA Asymmetric Encryption Algorithm

2023年4月25日 22点热度 0人点赞 0条评论
内容目录

Basic Knowledge

In C#, there are symmetric and asymmetric encryption methods, among which RSA is the most commonly used asymmetric encryption.

Due to significant changes in the encryption libraries in different .NET versions, this discussion will focus on versions after .NET 7.

Firstly, the inheritance relationship of RSA is as follows:

Object -> AsymmetricAlgorithm -> RSA -> RSACryptoServiceProvider

AsymmetricAlgorithm is a unified abstract interface for asymmetric encryption, while SymmetricAlgorithm serves as the abstract interface for symmetric encryption.

Asymmetric encryption consists of public and private keys, where the private key is mainly used for decryption, and the public key is used for encryption.

RSA is an abstract class that provides a unified abstraction for all RSA algorithms, and the RSACryptoServiceProvider class is an implementation of RSA.

To create an RSA class, you can use:

RSA.Create()

Or directly:

new RSACryptoServiceProvider()

Using RSACryptoServiceProvider directly, you can refer to the following example:

void Main()
{
	var keySize = 2048;

	var rsaCryptoServiceProvider = new RSACryptoServiceProvider(keySize);

	// Print private key and public key
	Convert.ToBase64String(rsaCryptoServiceProvider.ExportPkcs8PrivateKey()).Dump();
	Convert.ToBase64String(rsaCryptoServiceProvider.ExportRSAPublicKey()).Dump();

	var parameters = rsaCryptoServiceProvider.ExportParameters(true);
	// Encrypted string
	var cipherText = Encrypt("hello world", parameters);

	// Decrypted string
	var plainText = Decrypt(cipherText, parameters);
	Console.WriteLine(plainText);

}

// Encrypt
public string Encrypt(string data, RSAParameters key)
{

	using (var rsa = new RSACryptoServiceProvider())
	{
		rsa.ImportParameters(key);
		var byteData = Encoding.UTF8.GetBytes(data);
		var encryptData = rsa.Encrypt(byteData, false);
		return Convert.ToBase64String(encryptData);
	}
}

// Decrypt
public string Decrypt(string cipherText, RSAParameters key)
{

	using (var rsa = new RSACryptoServiceProvider())
	{
		var cipherByteData = Convert.FromBase64String(cipherText);
		rsa.ImportParameters(key);

		var encryptData = rsa.Decrypt(cipherByteData, false);
		return Encoding.UTF8.GetString(encryptData);
	}
}

Using the RSA Class

For loading RSA public and private keys, we can categorize them into two types: PKCS1 and PKCS8. For PKCS1, the export and import can be done directly, while PKCS8 requires a few more steps.

Here, we design a helper class for RSA, mainly targeting PKCS8, and the example code is as follows:

/// <summary>
/// Rsa encryption and decryption.
/// </summary>
public static class RsaHelper
{
    /// <summary>
    /// Export PKCS8 public key.
    /// </summary>
    /// <param name="rsa"></param>
    /// <returns></returns>
    public static string ExportPublichKeyPck8(this RSA rsa)
    {
        StringBuilder builder = new StringBuilder();
        builder.AppendLine("-----BEGIN PUBLIC KEY-----");
        builder.AppendLine(Convert.ToBase64String(rsa.ExportSubjectPublicKeyInfo(), Base64FormattingOptions.InsertLineBreaks));
        builder.AppendLine("-----END PUBLIC KEY-----");
        return builder.ToString();
    }

    public static void ImportPublichKeyPck8(this RSA rsa, string publicKey)
    {
        publicKey = publicKey
        .Replace("-----BEGIN RSA PUBLIC KEY-----", "")
        .Replace("-----END RSA PUBLIC KEY-----", "")
        .Replace("-----BEGIN PUBLIC KEY-----", "")
        .Replace("-----END PUBLIC KEY-----", "")
            .Replace("\r", "")
            .Replace("\n", "");

        rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicKey), out var bytesRead);
    }

    /// <summary>
    /// Encrypt information.
    /// </summary>
    /// <param name="rsa"></param>
    /// <param name="message"></param>
    /// <param name="padding">e.g. <see cref="RSAEncryptionPadding.OaepSHA256"/></param>
    /// <returns></returns>
    public static string Encrypt(this RSA rsa, string message, RSAEncryptionPadding padding)
    {
        var encryptData = rsa.Encrypt(Encoding.UTF8.GetBytes(message), padding);
        return Convert.ToBase64String(encryptData);
    }

    /// <summary>
    /// Decrypt
    /// </summary>
    /// <param name="rsa"></param>
    /// <param name="message"></param>
    /// <param name="padding"></param>
    /// <returns></returns>
    public static string Decrypt(this RSA rsa, string message, RSAEncryptionPadding padding)
    {
        var cipherByteData = Convert.FromBase64String(message);

        var encryptData = rsa.Decrypt(cipherByteData, padding);
        return Encoding.UTF8.GetString(encryptData);
    }
}

You can use the following code to export RSA in PKCS8 format for both private and public keys, and then store them in files:

void Main()
{
	using (RSA rsa = RSA.Create())
	{
		rsa.ExportPkcs8PrivateKeyPem().Dump();

		string publicKeyPem = "-----BEGIN PUBLIC KEY-----\n" +
					  Convert.ToBase64String(rsa.ExportSubjectPublicKeyInfo(), Base64FormattingOptions.InsertLineBreaks) +
					  "\n-----END PUBLIC KEY-----";
					  publicKeyPem.Dump();
	}
}

file

-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDS+kP8X+q9qJdu
Rh3sft2WlxZZee4IIA+JXQKic8HSNEOTPB8ZFu8mjnxy16gebQDyOuMF3M00+wip
LLNSfYqQU3yLe/EcKgD9DqBps5CvSmv3PgyKrYgJNlXVcbUq9zGhfZ2dEJTWALhs
tHfMH7+GZEKtcah/sEm6m+Us4W49sIjjeAWrRQl9Fq8+288uoIg4tpYNBGRVi90R
uPnf8a3manbEPHwlPX9rIeskkXRpwoftfHegeK/1EReyuE86co17SJePzwHxiHEv
Ro4UoOkij49IIhiVbmksBlzN4XEgl3YXj0Gtshc5X8Ce6SccNkFoi3KRax/KWmuM
wG8dpT9NAgMBAAECggEBAJfsofJlu6sxcUJ2eWvo+3ZKfEyYceEl/SokcRY8l1Dg
U9z9iUNO8Y3pQxKL20N1qR3Fa9+33YmOT/FLACKhxpshk2j6KmjmkmmoE7mqFcE0
rUSQSQW/6lr/5pVaWWSENxgcVdhZrWPhhuy4lB/IqOmE30L4uqagcqdPRZupBfKj
EjMQzNhV9OwhjhPPPDq9Fek4tdjdPm8wPuQuiDmOmIsB29bRSpwrgzp31kND4jw0
4DCb8TaoJe90F/f1JX5zAHeeToBRHsJFuO2ICLMtNmJWTD1uZIup7vQAivW0xf9F
/oQE+cXsjc5GZmQaGUr03LtWKJBbD8Hc+MsOZynMk50CgYEA1WdmnZ9/7jn1sW3J
h80b10fQxM0tOdP0NySXdP+LShhqCvELzQZgg3aGww5KVR9Zax+LVmsM4GM90f6c
WrAspoNjOQXZxZeOmg71sP6tOKKaSaf0oqUxT3kXeFL5/ubo/hQ6cVyP7qgvojPM
Vc63YIdmLWwFl1aXNxrHbPUzmxcCgYEA/RbiWV3PyTc2yT7cvQzQAd4yLDiaijK0
gvwLzWx9UDrOBGs5DULF4QMOgsCK0rerwvi4FLOuPIdqWqW7ysX+lhn5X9OZGYHv
lNp0GaIcid/KJNBs5TNi6VWMEY0a1v8ic68lMsRvf1xQXTMBA8RQUkR1YrT90LlB
95vhrobgJzsCgYEAzSpm9n1kwgTJGHbjfQMNlDCAHuTfaSxEK0urrRkNsgO8154c
6VULLvih4R95CVNlZV7jWAb9TzE6OwzdBzc/BitlFmpwjs4BlE1zmmGO6dcyHEQ0
JrZIrQ5PKSglHxKix7ts4JXL7veVLA0+kvR1SoGCE4M58OCX6qt9NVyb66sCgYEA
ozrLKZATn1b5ArqEa3mD/nBsM5EeOtuRCJm+kvLRr5j9nmP5G9BhB0qNZU8BOf4z
zT/UmaV5TpiXw3b4s0MXe3+tElzKdWUUPBDYqF+hwFqRaUTztq95r7v45qj3Eori
kXH4r9F5h87mFfX7RY6rryNwAgVxXdjd7vCekY1zrFkCgYAYFqf1ywcnAN8+Sfmj
wRavUjG0T4SSaDjwDpP1dv1B6+WdX32S+2sQHyHUE41GCnhJxFghJqY+2HMrxUz+
tzgmicLGpTrLmXFomz0XhjrJyFdvh0Y0gQChK+14hr9ZluO/a3cgARHnHY20njkl
6BA30QaaarVkSR/NMa1gDEU86Q==
-----END PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0vpD/F/qvaiXbkYd7H7dlpcWWXnuCCAP
iV0ConPB0jRDkzwfGRbvJo58cteoHm0A8jrjBdzNNPsIqSyzUn2KkFN8i3vxHCoA/Q6gabOQr0pr
9z4Miq2ICTZV1XG1KvcxoX2dnRCU1gC4bLR3zB+/hmRCrXGof7BJupvlLOFuPbCI43gFq0UJfRav
PtvPLqCIOLaWDQRkVYvdEbj53/Gt5mp2xDx8JT1/ayHrJJF0acKH7Xx3oHiv9REXsrhPOnKNe0iX
j88B8YhxL0aOFKDpIo+PSCIYlW5pLAZczeFxIJd2F49BrbIXOV/AnuknHDZBaItykWsfylprjMBv
HaU/TQIDAQAB
-----END PUBLIC KEY-----

You can import/export PKCS8 format private and public keys like this:

var privateKey = RSA.Create();
privateKey.ImportFromPem(File.ReadAllText("./private.key"));
var publicKey = RSA.Create();
publicKey.ImportPublichKeyPck8(File.ReadAllText("./public.key"));

You can check the correctness of the exported private and public keys here: https://www.bejson.com/enc/rsa/

Using RSACryptoServiceProvider

Continue using the two public and private key files generated earlier.

Write the import method:

/// <summary>
/// Import public key
/// </summary>
/// <param name="rsa"></param>
/// <param name="publicKey"></param>
/// <returns></returns>
private static RSACryptoServiceProvider ImportPublicKeyFromPEM(RSACryptoServiceProvider rsa, string publicKey)
{
	publicKey = publicKey
	.Replace("-----BEGIN RSA PUBLIC KEY-----", "")
	.Replace("-----END RSA PUBLIC KEY-----", "")
	.Replace("-----BEGIN PUBLIC KEY-----", "")
	.Replace("-----END PUBLIC KEY-----", "")
		.Replace("\r", "")
		.Replace("\n", "");
	rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicKey), out var bytesRead);
	return rsa;
}

Import the PKCS8 format public key:

var publicKey = new RSACryptoServiceProvider();
RsaHelper.ImportPublicKeyFromPEM(publicKey, File.ReadAllText("./public.key"));

Since RSACryptoServiceProvider implements RSA, the usage methods are also quite similar.

However, RSA.Create() defaults to returning RSAWrapper and RSACng.

痴者工良

高级程序员劝退师

文章评论