Last active
January 12, 2023 09:25
-
-
Save yetanotherchris/d8330dd6f541f85903a9bdd5dd13bb1f to your computer and use it in GitHub Desktop.
Public key cryptography by example in .NET Core
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Security.Cryptography; | |
using System.Security.Cryptography.X509Certificates; | |
namespace PublicKeyCryptographyExample | |
{ | |
// | |
// https://yetanotherchris.dev/security/public-private-key-in-netcore-by-example/ | |
// | |
// nuget dependencies: | |
// - System.Security.Cryptography.Algorithms | |
// To generate a test key, install openssl and use: | |
// - openssl req -x509 -sha256 -days 365 -newkey rsa:4096 -keyout public.pem -out private.pem | |
// | |
class Program | |
{ | |
private static readonly string PublicPemFilename = "public.pem"; | |
private static readonly string PrivatePemFilename = "private.pem"; | |
static void Main(string[] args) | |
{ | |
string plainText = "hello world"; | |
Console.WriteLine($"Encrypting ${plainText}"); | |
string encrypted = Encrypt(plainText); | |
Console.WriteLine("[Encrypted]"); | |
Console.WriteLine(encrypted); | |
Console.WriteLine("-------------------"); | |
string decrypted = Decrypt(encrypted); | |
Console.WriteLine("[Decrypted]"); | |
Console.WriteLine(decrypted); | |
Console.WriteLine("-------------------"); | |
string signature = SignWithPrivateKey(plainText); | |
Console.WriteLine("[Signature]"); | |
Console.WriteLine(signature); | |
Console.WriteLine("-------------------"); | |
Console.WriteLine("[Verify the hash and signature with a public key]"); | |
Console.WriteLine("Verified: "+VerifySignature(plainText, signature)); | |
Console.WriteLine("-------------------"); | |
} | |
private static string Encrypt(string text) | |
{ | |
byte[] publicPemBytes = File.ReadAllBytes(PublicPemFilename); | |
using var publicX509 = new X509Certificate2(publicPemBytes); | |
var rsa = publicX509.GetRSAPublicKey(); | |
byte[] encrypted = rsa.Encrypt(System.Text.Encoding.Default.GetBytes(text), RSAEncryptionPadding.Pkcs1); | |
return Convert.ToBase64String(encrypted); | |
} | |
private static string Decrypt(string base64Text) | |
{ | |
using X509Certificate2 certificate = CombinePublicAndPrivateCerts(); | |
var rsa = certificate.GetRSAPrivateKey(); | |
byte[] textBytes = Convert.FromBase64String(base64Text); | |
byte[] decrypted = rsa.Decrypt(textBytes, RSAEncryptionPadding.Pkcs1); | |
return System.Text.Encoding.Default.GetString(decrypted); | |
} | |
private static string SignWithPrivateKey(string text) | |
{ | |
var privateKeyText = File.ReadAllText(PrivatePemFilename); | |
var privateKeyBlocks = privateKeyText.Split("-", StringSplitOptions.RemoveEmptyEntries); | |
var privateKeyBytes = Convert.FromBase64String(privateKeyBlocks[1]); | |
using RSA rsa = RSA.Create(); | |
rsa.ImportPkcs8PrivateKey(privateKeyBytes, out _); | |
byte[] textBytes = System.Text.Encoding.Default.GetBytes(text); | |
byte[] signature = rsa.SignData(textBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); | |
return Convert.ToBase64String(signature); | |
} | |
private static bool VerifySignature(string plainText, string base64Signature) | |
{ | |
byte[] publicPemBytes = File.ReadAllBytes(PublicPemFilename); | |
using var publicX509 = new X509Certificate2(publicPemBytes); | |
var rsa = publicX509.GetRSAPublicKey(); | |
byte[] dataBytes = System.Text.Encoding.Default.GetBytes(plainText); | |
byte[] signatureBytes = Convert.FromBase64String(base64Signature); | |
return rsa.VerifyData(dataBytes, signatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); | |
} | |
// With help from https://github.com/dotnet/runtime/issues/19581#issuecomment-581147166 | |
private static X509Certificate2 CombinePublicAndPrivateCerts() | |
{ | |
byte[] publicPemBytes = File.ReadAllBytes(PublicPemFilename); | |
using var publicX509 = new X509Certificate2(publicPemBytes); | |
var privateKeyText = File.ReadAllText(PrivatePemFilename); | |
var privateKeyBlocks = privateKeyText.Split("-", StringSplitOptions.RemoveEmptyEntries); | |
var privateKeyBytes = Convert.FromBase64String(privateKeyBlocks[1]); | |
using RSA rsa = RSA.Create(); | |
if (privateKeyBlocks[0] == "BEGIN PRIVATE KEY") | |
{ | |
rsa.ImportPkcs8PrivateKey(privateKeyBytes, out _); | |
} | |
else if (privateKeyBlocks[0] == "BEGIN RSA PRIVATE KEY") | |
{ | |
rsa.ImportRSAPrivateKey(privateKeyBytes, out _); | |
} | |
var keyPair = publicX509.CopyWithPrivateKey(rsa); | |
return publicX509; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment