I've been looking to replace some cryptography services that use the standard .NET implementations to the BouncyCastle FIPS C# library. I've got RSA and PSS working but I'm struggling to understand an issue with Elliptical Curve.
As some groundwork and to better understand the library, I set up tests to verify that data signed with the standard .NET implementation can be verified using the FIPS library and that I can correctly load keys from JSON and X5092s.
I can load keys generated into both libraries and sign and verify data using both. However, if I sign something with the .NET implementation, I cannot verify it with the FIPS library, and vice versa.
As far as I can see, I'm not missing any parameters, but my cryptography knowledge is more practical than anything, so I may be missing something.
[Fact]
public void SignVerifyWithFipsAndDotNet()
{
FipsEC.KeyGenerationParameters originalKeyGenParameters =
new FipsEC.KeyGenerationParameters(FipsEC.DomainParams.P384);
FipsEC.KeyPairGenerator originalKpGen =
CryptoServicesRegistrar.CreateGenerator(originalKeyGenParameters, new SecureRandom());
var originalEd25519Keys = originalKpGen.GenerateKeyPair();
var ecParameters = new ECParameters()
{
Curve = System.Security.Cryptography.ECCurve.NamedCurves.nistP384,
Q = new System.Security.Cryptography.ECPoint()
{
X = originalEd25519Keys.PublicKey.W.XCoord.GetEncoded(),
Y = originalEd25519Keys.PublicKey.W.YCoord.GetEncoded()
},
D = originalEd25519Keys.PrivateKey.S.ToByteArrayUnsigned()
};
// Ensure that the parameters match
originalEd25519Keys.PrivateKey.S.ToByteArray().Should().BeEquivalentTo(ecParameters.D);
originalEd25519Keys.PublicKey.W.XCoord.GetEncoded().Should().BeEquivalentTo(ecParameters.Q.X);
originalEd25519Keys.PublicKey.W.YCoord.GetEncoded().Should().BeEquivalentTo(ecParameters.Q.Y);
var ecdsa = ECDsa.Create(ecParameters);
var cert = new CertificateRequest("SN=test", ecdsa, HashAlgorithmName.SHA384)
.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(2));
var message = Encoding.UTF8.GetBytes("Hello, World!");
// Sign DOTNET
var dotNetSignature = ecdsa.SignData(message, HashAlgorithmName.SHA384);
//SIGN FIPS
var fipsSignature = generateECDSASignature(originalEd25519Keys.PrivateKey, message);
// Verify Each using own verify method
cert.GetECDsaPublicKey().VerifyData(message, dotNetSignature, HashAlgorithmName.SHA384).Should().BeTrue();
verifyECDSASignature(originalEd25519Keys.PublicKey, fipsSignature, message).Should().BeTrue();
//VERIFY FIPS using DOTNET (FAILS)
cert.GetECDsaPublicKey().VerifyData(message, fipsSignature, HashAlgorithmName.SHA384).Should().BeTrue();
//VERIFY DOTNET using FIPS (FAILS)
verifyECDSASignature(originalEd25519Keys.PublicKey, dotNetSignature, message).Should().BeTrue();
}
public static byte[] generateECDSASignature(AsymmetricECPrivateKey key, byte[] message)
{
ISignatureFactoryService signatureFactoryProvider =
CryptoServicesRegistrar.CreateService(key, new SecureRandom());
ISignatureFactory<FipsEC.SignatureParameters> signer =
signatureFactoryProvider.CreateSignatureFactory(FipsEC.Dsa.WithDigest(FipsShs.Sha384));
IStreamCalculator<IBlockResult> calculator = signer.CreateCalculator();
Stream sOut = calculator.Stream;
sOut.Write(message, 0, message.Length);
sOut.Close();
return calculator.GetResult().Collect(); }
public static bool verifyECDSASignature(AsymmetricECPublicKey key, byte[] signature, byte[] message)
{
IVerifierFactoryService verifierFactoryProvider = CryptoServicesRegistrar.CreateService(key);
IVerifierFactory<FipsEC.SignatureParameters> verifier =
verifierFactoryProvider.CreateVerifierFactory(FipsEC.Dsa.WithDigest(FipsShs.Sha384));
IStreamCalculator<IVerifier> calculator = verifier.CreateCalculator();
Stream sOut = calculator.Stream;
sOut.Write(message, 0, message.Length);
sOut.Close();
return calculator.GetResult().IsVerified(signature);
}