Search code examples
c#cryptographyrsabouncycastlepublic-key

C# (.NET 4.8) - Verify signature using Public Key


I need to verify signature of a JSON webhook. In the documentation there is only JavaScript example of how to do this:

const crypto = require("crypto");
const fs = require('fs');

const publicKey = fs.readFileSync('publicKey.pem', 'utf-8');
const payload = fs.readFileSync('payload.json', 'utf-8');
const signature = fs.readFileSync('signature.txt', 'utf-8');

function verify(publicKey, signature, payload) {
  const verifier = crypto.createVerify("RSA-SHA256");
  verifier.update(payload);

  return verifier.verify(
    { key: publicKey, padding: crypto.constants.RSA_PKCS1_PSS_PADDING },
    Buffer.from(signature, 'base64')
  );
}

const verified = verify(publicKey, signature, payload);

console.log('Signature Verified: ', verified);

The above code prints "true" in the console log. I'm trying to simulate the same in C# and it always returns false:

private static bool VerifySignature(string publicKeyPath, string payload, string signature)
{
    byte[] byteToSign = Encoding.UTF8.GetBytes(payload);
    byte[] bsign = Convert.FromBase64String(signature);

    string pemText = File.ReadAllText(publicKeyPath);
    PemReader pr = new PemReader(new StringReader(pemText));
    AsymmetricKeyParameter publicKey = (AsymmetricKeyParameter)pr.ReadObject();
    RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaKeyParameters)publicKey);

    RSACryptoServiceProvider cryptoServiceProvider = new RSACryptoServiceProvider();
    cryptoServiceProvider.ImportParameters(rsaParams);

    bool result = cryptoServiceProvider.VerifyData(byteToSign, bsign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

    return result;
}

I tried different approaches in C#, the above one looks cleaner and shorter than the other ones. What am I doing wrong? Please note that I use .NET 4.8, so I don't have methods like RSACryptoServiceProvider.ImportFromPem that are available only from version 5. Thanks

UPDATE - This code works properly:

private static bool VerifySignature(string publicKeyPath, string payload, string signature)
{
    byte[] byteToSign = Encoding.UTF8.GetBytes(payload);
    byte[] bsign = Convert.FromBase64String(signature);

    string pemText = File.ReadAllText(publicKeyPath);
    PemReader pr = new PemReader(new StringReader(pemText));
    AsymmetricKeyParameter publicKey = (AsymmetricKeyParameter)pr.ReadObject();

    PssSigner pssSigner = new PssSigner(new RsaEngine(), new Sha256Digest(), bsign.Length - 32 - 2);
    pssSigner.Init(false, publicKey);
    pssSigner.BlockUpdate(byteToSign, 0, byteToSign.Length);
    bool result = pssSigner.VerifySignature(bsign);

    return result;
}

Solution

  • This code works properly:

    private static bool VerifySignature(string publicKeyPath, string payload, string signature)
    {
        byte[] byteToSign = Encoding.UTF8.GetBytes(payload);
        byte[] bsign = Convert.FromBase64String(signature);
    
        string pemText = File.ReadAllText(publicKeyPath);
        PemReader pr = new PemReader(new StringReader(pemText));
        AsymmetricKeyParameter publicKey = (AsymmetricKeyParameter)pr.ReadObject();
    
        PssSigner pssSigner = new PssSigner(new RsaEngine(), new Sha256Digest(), bsign.Length - 32 - 2);
        pssSigner.Init(false, publicKey);
        pssSigner.BlockUpdate(byteToSign, 0, byteToSign.Length);
        bool result = pssSigner.VerifySignature(bsign);
    
        return result;
    }