In this article I will show how to sign string data with private key and validate it using public key.

All code examples you can find here.

First thing you have to do is to generate private key (PKCS8 RSA). You can do it using openssl. I’m using openssl already installed in git bash.
Open git bash in folder where keys should be placed and execute below commands.

openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048 
openssl rsa -pubout -in private.pem -out public.pem

Now we can start writing code. Create the new ms-test project to be able test the solution easily.
Create your test class “SignTest” and “Crypto” class where you place your signing algorithm. You need also copy private and public key, that you generated before.

Remember also to check-in option to copy your keys to output folder. Click on key and select option properties. After that select “Copy to Output Directory” – “Copy always”

I will be using Portable.BouncyCastle nugget package – so install it in your project.

Now have a look to our code in Crypto class.

 public string SignData(string message, string privateKeyPath)
 {
     var privateKey = ReadAsymmetricKeyParameter(privateKeyPath);

     //Initialization
     ISigner sig = SignerUtilities.GetSigner("SHA512withRSA");
     sig.Init(true, privateKey);

     //Get bytes from message
     var bytes = Encoding.UTF8.GetBytes(message);

     //Signing data
     sig.BlockUpdate(bytes, 0, bytes.Length);
     byte[] signature = sig.GenerateSignature();

     //Return string from bytes as Base64
     return Convert.ToBase64String(signature);
 }

 private AsymmetricKeyParameter ReadAsymmetricKeyParameter(string privateKeyPath)
 {
     using var fileStream = File.OpenText(privateKeyPath);
     var pemReader = new PemReader(fileStream);
     return (AsymmetricKeyParameter)pemReader.ReadObject();
 }

Firstly, you need to read private key parameters. Next you need to convert your message to byte array and generate signature. After that you return your signature as Base64.

Now it’s time to verify signature.

 private RSACryptoServiceProvider GetPublicKeyCryptoProvider(string publicKeyPath)
 {
     var publicKey = File.ReadAllText(publicKeyPath);

     //Read public key params
     var pr = new PemReader(new StringReader(publicKey));
     var publicKeyParams = (AsymmetricKeyParameter)pr.ReadObject();
     var rsaParams = DotNetUtilities.ToRSAParameters((RsaKeyParameters)publicKeyParams);

     //Create provider
     var rsaPublicKey = new RSACryptoServiceProvider();
     rsaPublicKey.ImportParameters(rsaParams);
     return rsaPublicKey;
 }

public bool VerifyData(string originalMessage, string signature, string publicKeyPath)
{
    var rsaPublicKey = GetPublicKeyCryptoProvider(publicKeyPath);

    //Get message from Base64
    var signatureBytes = Convert.FromBase64String(signature);

    //Get bytes from original message
    var bytesToVerify = new UTF8Encoding().GetBytes(originalMessage);

    //Verify data
    return rsaPublicKey.VerifyData(bytesToVerify, CryptoConfig.MapNameToOID("SHA512")!, signatureBytes);
}

Firstly you need to load your private key using method “GetPublicKeyCryptoProvider”. You need to convert back your signature from Base64 to byte array. Afther that you verify if signature match to message you expecting.

To prove the algorithm works properly I’ve created some tests.

    Crypto crypto = new Crypto();

    [TestMethod]
    public void GivenPhrase_TextTheSame_ThenGoodValidation()
    {
        var signature = crypto.SignData("Text to validate", @".\Keys\private.pem");
        var resultCheckGood = crypto.VerifyData("Text to validate", signature, @".\Keys\public.pem");

        Assert.IsTrue(resultCheckGood);
    }

    [TestMethod]
    public void GivenPhrase_TextDifferent_ThenBadValidation()
    {
        var signature = crypto.SignData("Text to validate", @".\Keys\private.pem");
        var resultCheckBad = crypto.VerifyData("Different text to validate", signature, @".\Keys\public.pem");
        Assert.IsFalse(resultCheckBad);
    }

    [TestMethod]
    public void GivenPhrase_KeyDifferent_ThenBadValidation()
    {
        var signature = crypto.SignData("Text to validate", @".\Keys\private2.pem");
        var resultCheckBad = crypto.VerifyData("Text to validate", signature, @".\Keys\public.pem");
        Assert.IsFalse(resultCheckBad);
    }

In first test you just give a sample of text signing it with private key and verifying if the signature match to this text.
Second test shows that if the message will be changed it cannot be verified.
Third test shows that text signed with different key cannot be verified also.

That’s it! Happy signing!

4 Comments

  1. Great article ! Maybe we can add some example related with signing & verifying some data between languages C# vs Java ?

Leave a Reply