FIPS Compliant RSA Signatures with Bouncy Castle and C#

What has lasted for 45 years and defines the core of trust on the Internet? Well, it is RSA, and virtually every Web page that you connect…

FIPS Compliant RSA Signatures with Bouncy Castle and C#

What has lasted for 45 years and defines the core of trust on the Internet? Well, it is RSA, and virtually every Web page that you connect to relies on the usage of the RSA signature for its trust. While ECDSA is increasingly used for digital signatures, most of the public keys for digital signatures are RSA ones. But what if your company needs to comply with FIP140–2? Well, for this, we need a FIPS-compliant library and will use the Bouncy Castle library for C#.

Overall, we use the private key to sign a message and then use the public key to verify the signature:

First we can create an RSA key pair:

// Generate key pair
FipsRsa.KeyGenerationParameters keyGenParameters = new FipsRsa.KeyGenerationParameters(new Org.BouncyCastle.Math.BigInteger("65537"), size);
FipsRsa.KeyPairGenerator kpGen =CryptoServicesRegistrar.CreateGenerator(keyGenParameters, new SecureRandom());
var pair = kpGen.GenerateKeyPair();

and then sign for a PKCS1v15 signature with the private key:

ISignatureFactoryService signatureFactoryProvider = CryptoServicesRegistrar.CreateService(pair.PrivateKey, new SecureRandom());
ISignatureFactory <FipsRsa.SignatureParameters> signer = signatureFactoryProvider.CreateSignatureFactory(FipsRsa.Pkcs1v15);
IStreamCalculator <IBlockResult> calculator = signer.CreateCalculator();
Stream sOut = calculator.Stream;
sOut.Write(message, 0, message.Length);
sOut.Close();
var sig=calculator.GetResult().Collect();

and then verify with the public key:

IVerifierFactoryService verifierFactoryProvider = CryptoServicesRegistrar.CreateService(pair.PublicKey);
IVerifierFactory <FipsRsa.SignatureParameters> verifier =
verifierFactoryProvider.CreateVerifierFactory(FipsRsa.Pkcs1v15);
IStreamCalculator <IVerifier> calculator2 = verifier.CreateCalculator();
Stream sOut2 = calculator2.Stream;
sOut2.Write(message, 0, message.Length);
sOut2.Close();
var rtn=calculator2.GetResult().IsVerified(sig);

First, we create a folder named “bc_fips_rsa”, and then go into that folder.We can create a Dotnet console project for .NET 8.0 with:

dotnet new console --framework net8.0

Next, we download the FIPS module (bc-fips-1.0.2.dll) from [here] and add it as a reference to the project: This produces a Csproject file of:

<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Reference Include="Org.BouncyCastle.Crypto">
<HintPath>bc-fips-1.0.2.dll</HintPath>
</Reference>

</ItemGroup>
</Project>

Next some code [here]:

namespace RSA
{
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Fips;
using Org.BouncyCastle.Security;
using System.Text.Json;

class Program
{

static void Main(string[] args)
{



var msg="01020304";
var size=1024;
if (args.Length >0) msg=args[0];
if (args.Length >1) size=Convert.ToInt32(args[1]);
var message=System.Text.Encoding.ASCII.GetBytes(msg);

try {

// Generate key pair
FipsRsa.KeyGenerationParameters keyGenParameters = new FipsRsa.KeyGenerationParameters(new Org.BouncyCastle.Math.BigInteger("65537"), size);
FipsRsa.KeyPairGenerator kpGen =CryptoServicesRegistrar.CreateGenerator(keyGenParameters, new SecureRandom());
var pair = kpGen.GenerateKeyPair();

// Create signature
ISignatureFactoryService signatureFactoryProvider = CryptoServicesRegistrar.CreateService(pair.PrivateKey, new SecureRandom());
ISignatureFactory<FipsRsa.SignatureParameters>
signer = signatureFactoryProvider.CreateSignatureFactory(FipsRsa.Pkcs1v15);
IStreamCalculator<IBlockResult>
calculator = signer.CreateCalculator();
Stream sOut = calculator.Stream;
sOut.Write(message, 0, message.Length);
sOut.Close();
var sig=calculator.GetResult().Collect();
IVerifierFactoryService verifierFactoryProvider = CryptoServicesRegistrar.CreateService(pair.PublicKey);
IVerifierFactory<FipsRsa.SignatureParameters> verifier =
verifierFactoryProvider.CreateVerifierFactory(FipsRsa.Pkcs1v15);
IStreamCalculator<IVerifier> calculator2 = verifier.CreateCalculator();
Stream sOut2 = calculator2.Stream;
sOut2.Write(message, 0, message.Length);
sOut2.Close();
var rtn=calculator2.GetResult().IsVerified(sig);

Console.WriteLine("Message: {0}",msg);
Console.WriteLine("Signature: {0} [{1}]",Convert.ToHexString(sig),Convert.ToBase64String(sig));
Console.WriteLine("Verified: {0}",rtn);
Console.WriteLine("\n\nPublic key===");
Console.WriteLine("Public key modulus: {0}",pair.PublicKey.Modulus);
Console.WriteLine("Public key exponent: {0}",pair.PublicKey.PublicExponent);
Console.WriteLine("\n\nPrivate key===");
Console.WriteLine("Private key modulus: {0}",pair.PrivateKey.Modulus);
Console.WriteLine("Private key private exponent: {0}",pair.PrivateKey.PrivateExponent);
Console.WriteLine("Private key p: {0}",pair.PrivateKey.P);
Console.WriteLine("Private key q: {0}",pair.PrivateKey.Q);

} catch (Exception e) {
Console.WriteLine("Error: {0}",e.Message);
}
}
}
}

A sample run gives [here]:

Message: Hello 123
Signature: 4C839BE3C4D576388AF1BD135E505E9E8205D72E4D3B8FE04FE6D2C032A5546C6DA20BDB11B1A413C5C0E28EC5BE0FA381D2A08CD6A69F1D9CFC3B3AD7A56D3D9E0E2783DC6B82D14358BD28E603670AE4FEC3037765F932E2015666C0316D6CCF7CCCACF3A95EE1317F8F78FD7914EF25C6EE4B2499A34B081AFEDF080A0361 [TIOb48TVdjiK8b0TXlBenoIF1y5NO4/gT+bSwDKlVGxtogvbEbGkE8XA4o7Fvg+jgdKgjNamnx2c/Ds616VtPZ4OJ4Pca4LRQ1i9KOYDZwrk/sMDd2X5MuIBVmbAMW1sz3zMrPOpXuExf494/XkU7yXG7kskmaNLCBr+3wgKA2E=]
Verified: True

Public key===
Public key modulus: 168074680255987703502538376248274939633694440356696501422745972181305076886801899341192725896342889832813623517148844930145709854960264573299311142256817502128625291578464969659025080748784135999767111708853168995277992483121581140168034092646141314818196181804380320041468968519006721694112242468719845853301
Public key exponent: 65537

Private key===
Private key modulus: 168074680255987703502538376248274939633694440356696501422745972181305076886801899341192725896342889832813623517148844930145709854960264573299311142256817502128625291578464969659025080748784135999767111708853168995277992483121581140168034092646141314818196181804380320041468968519006721694112242468719845853301
Private key private exponent: 19111227509156970360268489247328148223908493973451673537121061151640835451126047177786108509384732518029923897184692500716325584618824352659207266614245446581575024206176227784555514028338540039660997826272961871844338012806658301518517789381655804336922294312888303874741526738485492156694270469941714762625
Private key p: 13174523955180823227978128649959784582897314301436443869122056007627918630655023534158371293866882677937151785930348341266950763091319698716209545824165177
Private key q: 12757552441953174291289536430403167172724129834622858695713927516209831242875560616364400239765591331931933356497712594228962215221964151528971389512778013

Conclusions

So, say thank you to RSA for securing the Internet. Overall, it works hand-in-hand with ECDH. For key exchange, it is ECDH, and for signatures, it is typically RSA that is used.