Search code examples
c#cryptographyecdsasha512

ECDsa Signature Validation With SHA512


Here is a C# method that validates a webhook signature. The signature is generated by the Ubble API.

private bool SignatureValid(string rawBody, string signatureHeader, string pubKey)
{
    string[] signatureParts = signatureHeader.Split(':');

    if (signatureParts.Length is not 3)
    {
        logger.LogError("Webhook signature not split into 3 parts: {sig}", signatureHeader);
        return false;
    }

    string timestamp = signatureParts[0];
    string signature = signatureParts[2];

    string signedPayload = $"{timestamp}:{rawBody}";
    byte[] signedPayloadBytes = Encoding.UTF8.GetBytes(signedPayload);

    using ECDsa ecdsa = ECDsa.Create();
    ecdsa.ImportFromPem($"-----BEGIN PUBLIC KEY-----\n{pubKey}\n-----END PUBLIC KEY-----".ToCharArray());

    byte[] signatureBytes = Convert.FromBase64String(signature);

    bool isValidSignature = ecdsa.VerifyData(signedPayloadBytes, signatureBytes, HashAlgorithmName.SHA512);

    if (!isValidSignature) logger.LogError("Unable to verify signature for body and sig header: {body}, {sig}", rawBody, signatureHeader);

    return isValidSignature;
}

The problem is that isValidSignature is always false. This method accepts three parameters - the body of the webhook, the signature the issuer places in the header and the public key. I will share them as well. Can you spot what is wrong the logic given the three inputs?

rawBody:

{"specversion": "2.0", "type": "identity_verification_created", "subject": "idv_01j1zgha0pa5cwhxfhcw08da7p", "id": "evnt_01j1zgha43jd06kgzs1xe1hy96", "time": "2024-07-04T18:36:32Z", "datacontenttype": "application/json", "data": {"applicant_id": "aplt_01j1zgh9z21hjwk045ednvxbs9", "status": "pending", "identity_verification_id": "idv_01j1zgha0pa5cwhxfhcw08da7p", "response_codes": [], "user_journey_id": "usj_01j19zhh6bjwfad1w464vbzhg0"}}

signatureHeader:

1720118192.350353:2530-test-v1:MIGHAkEMjhZbBcB6RxOhXURr0+VV8j66Wgml+UpITG3sE54nVju7K2oFOWSIX/4W1kp0jfyY1DmOu8rVpPCFjnikRB/jWAJCAQhStnif+xLIOYavKos3c8kF+0nAOMJdVQAI3fY/mPfjD7Kqru2JxRaFPcz0FJPUekL+6x9nhNAx3v23ByuDRPrg

pubKey:

MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBJKgbekd2Q01oTmu7FK8m8hASky+O
ssU7lFAuTIVd5/tA+h8Cjh2ieK+gjv8bHLov6b+3eegWvp+aq25nge/nCHcBCrfi
7gJDMuZ5Yg5kG4+mEwADC8caLhejF3ITs1n01dwuWHgD2/jniqvhVwITNRta00+K
PRCYbQjw32/kti8WV50=

Please note I have a Java sample that is known to work. I can share that as well if needed.


Solution

  • The signature has the ASN.1/DER format. .NET uses P1363 by default, ASN.1/DER must be explicitly specified in the fourth parameter of VerifyData():

    ...
    bool isValidSignature = ecdsa.VerifyData(signedPayloadBytes, signatureBytes, HashAlgorithmName.SHA512, DSASignatureFormat.Rfc3279DerSequence);
    ...
    

    With this fix, verification is successful.