user2058904
user2058904

Reputation:

Verifying An RSA Signature In .NET

I've generated a private and public key pair in Node using node-rsa and can successfully sign and verify using those keys in node.

What I'm trying to do is use the modulus and exponent of the public key to verify the signature (Signed by the private key in node) in .NET.

e.g. I have a string ="foo", which has a signature of "abc123" generated by Node. In .NET I want to use the modulus and exponent of the public key, to confirm that the signature "abc123" is valid for "foo".

The reason I want to use the modulus and exponent rather than using the public key directly, is because I want to create a web service which exposes a jwks.json file at a well-known address where anyone can validate the tokens the service produces.

Here's the code I've got working in node:

let privateKey = fs.readFileSync('privateKeyPKCS1.pem');
let publicKey = fs.readFileSync('publicKeyPKCS1.pem');

let testpublicForExport = new NodeRSA(publicKey);
let keyComponents = testpublicForExport.exportKey('components-public');
console.log(keyComponents.n.toString('base64'));

let sign = crypto.createSign('RSA-SHA256');
let stringToSign = 'hello';
sign.update(stringToSign);
let sig = sign.sign(privateKey, 'base64');
console.log('sig='+sig);

let verify = crypto.createVerify('RSA-SHA256');
verify.update(stringToSign);
console.log(verify.verify(publicKey, sig, 'base64'));

And here's what I've got for .NET (I'm guessing that this part is somehow wrong):

    string modulus = "AJ/dk3x2xQjvNTJpRh/yW6vURVhkubHVrXk33zd76k7/VnHS/8RE49IM+d137kvz8SgVQBOcN1P2yhOFuhbtYR7spwQ5lhob9GNkzR4l/ybgcriIIwGO0vmo264Xz2vERhZ7cVITOvn49VpWRbeVS3Gsn3ecSILdeoh2yNoevqwFGnuPeVyD4JOwo+JhEhXDlzth9UFu18x5k7NNLG2gywI7qX/qMd1+2rnDI33rSLpXiPXGQg9BCmatMx+R4sWV2AGOx9YxI/YBUTl0hSGcWtzsPxGA2zY3oBT0P9FGg+QYvVjHdAkuJsx9QpPo0TlNbrNvodj3KEUwxoGwnWu6Ick=";
    int exponent = 65537;

    string stringToSign = "hello";
    string sig = "fGpD/SMPvS2CK9D4Z4ykUj+dRmPOIK92wdaRnhpAl3toOLdNT+96PUoCQKJ30UIRyXchIlmCvg+QH/MwimXueyY/oBwMH36EpajyraJZA/2RRA3/F7hRIT6/G9lJe69npeGyvMx+LlIov9B+I99KSLiVdYYPSrU4eUb3fe4wuZZd/cSIcwnIVvCrjOSUZAF09sfKx7Qt6LhTcQm2J332PsvotEZUuwTRsHh0QMMUd/eIWZ+yPfPGMX4D5WJEUxnlQT2f9Vfxx7JBnx5IDI9Jf6ohJCl5bymeKjh4ZsBqRINGG0aiElZdhGdcsGfhZGjRR2MMQkIo7ZSRrtaaKO5u9g==";

    RSA rsa = RSA.Create();
    rsa.ImportParameters(new RSAParameters()
    {
        Modulus = Convert.FromBase64String(modulus),
        Exponent = BitConverter.GetBytes(exponent)
    });

    bool isVerified = rsa.VerifyData(Encoding.UTF8.GetBytes(stringToSign), Convert.FromBase64String(sig), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

If necessary, I'm happy to use bouncy castle (Although I'm not very familiar with it).

TLDR; I'm trying to verify an RSA signature using only the modulus and exponent of a public key.

Thanks.

Upvotes: 4

Views: 909

Answers (2)

bartonjs
bartonjs

Reputation: 33088

There are two problems here.

First, your value for Exponent is incorrect, assuming the key used 65537.

You called BitConverter.GetBytes, which produces { 0x01, 0x00, 0x01, 0x00 } (Little Endian), which is used in context (Big Endian) as 16,777,472, 256 times larger than you intended.

Your best bet is to save it to a static array with a value of { 0x01, 0x00, 0x01 }.

Second, the Javascript code is outputting the modulus value in its DER-encoded representation, meaning it starts with 0x00. The RSAParameters class expects the integer value to be unsigned, and so it says you've imported a slightly invalid 2056-bit key. It then outright rejects the signature as being the wrong size for the key. You need to remove the leading 0x00.

All told:

string modulus = "AJ/dk3x2xQjvNTJpRh/yW6vURVhkubHVrXk33zd76k7/VnHS/8RE49IM+d137kvz8SgVQBOcN1P2yhOFuhbtYR7spwQ5lhob9GNkzR4l/ybgcriIIwGO0vmo264Xz2vERhZ7cVITOvn49VpWRbeVS3Gsn3ecSILdeoh2yNoevqwFGnuPeVyD4JOwo+JhEhXDlzth9UFu18x5k7NNLG2gywI7qX/qMd1+2rnDI33rSLpXiPXGQg9BCmatMx+R4sWV2AGOx9YxI/YBUTl0hSGcWtzsPxGA2zY3oBT0P9FGg+QYvVjHdAkuJsx9QpPo0TlNbrNvodj3KEUwxoGwnWu6Ick=";
int exponent = 65537;

string stringToSign = "hello";
string sig = "fGpD/SMPvS2CK9D4Z4ykUj+dRmPOIK92wdaRnhpAl3toOLdNT+96PUoCQKJ30UIRyXchIlmCvg+QH/MwimXueyY/oBwMH36EpajyraJZA/2RRA3/F7hRIT6/G9lJe69npeGyvMx+LlIov9B+I99KSLiVdYYPSrU4eUb3fe4wuZZd/cSIcwnIVvCrjOSUZAF09sfKx7Qt6LhTcQm2J332PsvotEZUuwTRsHh0QMMUd/eIWZ+yPfPGMX4D5WJEUxnlQT2f9Vfxx7JBnx5IDI9Jf6ohJCl5bymeKjh4ZsBqRINGG0aiElZdhGdcsGfhZGjRR2MMQkIo7ZSRrtaaKO5u9g==";

byte[] modulusBytes = Convert.FromBase64String(modulus);

if (modulusBytes[0] == 0)
{
    modulusBytes = modulusBytes.Skip(1).ToArray();
}

RSA rsa = RSA.Create();
rsa.ImportParameters(new RSAParameters()
{
    Modulus = modulusBytes,
    Exponent = new byte[] { 0x01, 0x00, 0x01 },
});

bool isVerified = rsa.VerifyData(Encoding.UTF8.GetBytes(stringToSign), Convert.FromBase64String(sig), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
Console.WriteLine(isVerified);

Upvotes: 5

Zergatul
Zergatul

Reputation: 2010

I have no idea why .NET RSA class cannot verify signature, everything looks ok. Below is BouncyCastle code:

string modulus = "AJ/dk3x2xQjvNTJpRh/yW6vURVhkubHVrXk33zd76k7/VnHS/8RE49IM+d137kvz8SgVQBOcN1P2yhOFuhbtYR7spwQ5lhob9GNkzR4l/ybgcriIIwGO0vmo264Xz2vERhZ7cVITOvn49VpWRbeVS3Gsn3ecSILdeoh2yNoevqwFGnuPeVyD4JOwo+JhEhXDlzth9UFu18x5k7NNLG2gywI7qX/qMd1+2rnDI33rSLpXiPXGQg9BCmatMx+R4sWV2AGOx9YxI/YBUTl0hSGcWtzsPxGA2zY3oBT0P9FGg+QYvVjHdAkuJsx9QpPo0TlNbrNvodj3KEUwxoGwnWu6Ick=";
int exponent = 65537;

string stringToSign = "hello";
string sig = "fGpD/SMPvS2CK9D4Z4ykUj+dRmPOIK92wdaRnhpAl3toOLdNT+96PUoCQKJ30UIRyXchIlmCvg+QH/MwimXueyY/oBwMH36EpajyraJZA/2RRA3/F7hRIT6/G9lJe69npeGyvMx+LlIov9B+I99KSLiVdYYPSrU4eUb3fe4wuZZd/cSIcwnIVvCrjOSUZAF09sfKx7Qt6LhTcQm2J332PsvotEZUuwTRsHh0QMMUd/eIWZ+yPfPGMX4D5WJEUxnlQT2f9Vfxx7JBnx5IDI9Jf6ohJCl5bymeKjh4ZsBqRINGG0aiElZdhGdcsGfhZGjRR2MMQkIo7ZSRrtaaKO5u9g==";

var signer = Org.BouncyCastle.Security.SignerUtilities.GetSigner("SHA256withRSA");
signer.Init(false, new Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters(
    false,
    new Org.BouncyCastle.Math.BigInteger(1, Convert.FromBase64String(modulus)),
    Org.BouncyCastle.Math.BigInteger.ValueOf(exponent)));
byte[] data = Encoding.ASCII.GetBytes(stringToSign);
signer.BlockUpdate(data, 0, data.Length);
bool ok = signer.VerifySignature(Convert.FromBase64String(sig));

Upvotes: 0

Related Questions