Signing and Verifying Signatures with Rsa C#

Signing and verifying signatures with RSA C#

Your problem is at the beginning of the VerifyData method:

public static bool VerifyData(string originalMessage, string signedMessage, RSAParameters publicKey)
{
bool success = false;
using (var rsa = new RSACryptoServiceProvider())
{
//Don't do this, do the same as you did in SignData:
//byte[] bytesToVerify = Convert.FromBase64String(originalMessage);
var encoder = new UTF8Encoding();
byte[] bytesToVerify = encoder.GetBytes(originalMessage);

byte[] signedBytes = Convert.FromBase64String(signedMessage);
try
...

For some reason you switched to FromBase64String instead of UTF8Encoding.GetBytes.

C# Signing and verifying signatures with RSA. Encoding issue

You cannot use ASCIIEncoding on the encoded message because it contains bytes which are invalid ASCII characters. The typical way you would store the encoded message is in a base64 string.

In SignData, use the following to encode the byte array into a string:

return Convert.ToBase64String(signedBytes);

and in VerifyData, use the following to decode the string back to the same byte array:

byte[] signedBytes = Convert.FromBase64String(signedMessage);

validating rsa signature from C# in java

In the XML representation on the C# side, the data are stored as unsigned big endian (and Base64 encoded).

However, the Java BigInteger(byte[] val)-constructor expects the data as signed (two's complement) big endian.

Therefore the BigInteger(int signum, byte[] magnitude)-constructor must be used, which expects the sign in the first parameter and the data in the second parameter as unsigned big endian, i.e. the following change is necessary:

RSAPublicKeySpec spec = new RSAPublicKeySpec(new BigInteger(1, modulus), new BigInteger(1, exponent));

RSA signing with Python verifying with C#

RSAParameters#Modulus and RSAParameters#Exponent expect modulus and exponent unsigned, so Org.BouncyCastle.Math.BigInteger#ToByteArrayUnsigned() must be used instead of Org.BouncyCastle.Math.BigInteger#ToByteArray(). With this change, verification is successful.

Also, the big endian byte order is required, but this applies to both methods.

Note that System.Numerics.BigInteger#ToByteArray() returns the data signed with little endian order.


Your current approach uses BC for key import and built-in .NET methods for verification. Alternatively, BC classes can also be used for verification, making for a slightly more efficient implementation overall.

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
...
ISigner signer = SignerUtilities.GetSigner("SHA256withRSA");
signer.Init(false, o);
signer.BlockUpdate(data, 0, data.Length);
bool verified = signer.VerifySignature(signature);

Verifying signed data from node in c# using rsa

PSS has a number of parameters, including the salt length. RFC8017, A.2.3. RSASSA-PSS defines a default salt length that corresponds to the output length of the digest, i.e. 32 bytes for SHA256.

Your recent C# code applies the C# built-in classes that use this default salt length. A different salt length cannot be specified!

The NodeJS code, on the other hand, defaults to the maximum possible salt length (crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN), which is given by:

<keysize> - <digest output length> - 2 = 256 - 32 - 2 = 222.

Thus, the two codes are incompatible!


Unlike the C# built-in classes, BouncyCastle allows the salt length to be configured:

string x509Pem = @"-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----";
string validar = "...";
string hash64 = "...";

byte[] message = Encoding.UTF8.GetBytes(validar);
byte[] signature = Convert.FromBase64String(hash64);
PemReader pr = new PemReader(new StringReader(x509Pem));
AsymmetricKeyParameter publicKey = (AsymmetricKeyParameter)pr.ReadObject();

PssSigner pssSigner = new PssSigner(new RsaEngine(), new Sha256Digest(), 256 - 32 - 2);
pssSigner.Init(false, publicKey);
pssSigner.BlockUpdate(message, 0, message.Length);
bool valid = pssSigner.VerifySignature(signature); // succeeds when the maximum possible salt length is used

With this, verification is successful.


Note that in the NodeJS code you can explicitly change the salt length to the output length of the digest (crypto.constants.RSA_PSS_SALTLEN_DIGEST). Then verification will also work with the built-in C# classes.

Verifying a RSA signature made by Crypto Node.JS in C#

As pointed out in the comments, the problem was that data in the C# client was converted to from Base64 when the data in the NodeJS application read from UTF-8.

The solution was to convert the string using Encoding.UTF8.GetBytes()

Thanks for the quick response!



Related Topics



Leave a reply



Submit