Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I verify the signature from a BlockCypher Webhook? #532

Open
Olsm opened this issue Apr 24, 2024 · 2 comments
Open

How do I verify the signature from a BlockCypher Webhook? #532

Olsm opened this issue Apr 24, 2024 · 2 comments

Comments

@Olsm
Copy link

Olsm commented Apr 24, 2024

Hi there.

I have created webhooks with the attribute "signkey" added with the value "preset" so that BlockCypher will create a signature for the requests. This is described here: https://www.blockcypher.com/dev/bitcoin/#webhook-signing

I am trying to verify the signature when receiving webhook requests and I cannot find any documentation that shows how it is done. This is the current function I have created in Node.js and it is returning false:

function verifySignature(req) {
    // Get the signature value from the headers
    if (!('signature' in req.headers)) {
        throw new Error('Signature is missing in the headers');
    }
    const signatureHeader = req.headers['signature'];

    // Extract signature value from the signature header
    const signatureMatch = /signature="(.*?)"/.exec(signatureHeader);
    if (!signatureMatch || signatureMatch.length < 2) {
        throw new Error('Invalid signature header format');
    }
    const signature = signatureMatch[1];

    // Parse the provided public key into a format compatible with crypto module
    const publicKey = `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEflgGqpIAC9k65JicOPBgXZUExen4
rWLq05KwYmZHphTU/fmi3Oe/ckyxo2w3Ayo/SCO/rU2NB90jtCJfz9i1ow==
-----END PUBLIC KEY-----`;

    // Extract the required headers from the signature header
    const headersMatch = /headers="(.*?)"/.exec(signatureHeader);
    if (!headersMatch || headersMatch.length < 2) {
        throw new Error('Invalid signature header format: missing headers');
    }
    const requiredHeaders = headersMatch[1].split(' ');

    // Construct the content to be verified including the required headers
    const content = requiredHeaders.map(header => {
        if (header === '(request-target)') {
            // Extract the request-target from the original request
            return `${header}: ${req.method.toLowerCase()} ${req.url}`;
        } else {
            // Use the corresponding header value from the original request
            return `${header.toLowerCase()}: ${req.headers[header]}`;
        }
    }).join('\n');

    // Verify signature using the parsed public key and the constructed content
    const verifier = crypto.createVerify('sha256');
    verifier.update(content);

    return verifier.verify(publicKey, signature, 'base64');
}
@Olsm
Copy link
Author

Olsm commented Apr 24, 2024

I found the solution here: https://crypto.stackexchange.com/a/109446

Please improve your documentation and add a description and example code for how to verify the signature. The format "ieee-p1363" must be used when verifying the signature and this should be mentioned in the documentation.

Here is the updated code and it works for me now:

function verifySignature(req) {
    // Get the signature value from the headers
    if (!('signature' in req.headers)) {
        throw new Error('Signature is missing in the headers');
    }
    const signatureHeader = req.headers['signature'];

    // Extract signature value from the signature header
    const signatureMatch = /signature="(.*?)"/.exec(signatureHeader);
    if (!signatureMatch || signatureMatch.length < 2) {
        throw new Error('Invalid signature header format');
    }
    const signature = signatureMatch[1];

    // Parse the provided public key into a format compatible with crypto module
    const publicKey = `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEflgGqpIAC9k65JicOPBgXZUExen4
rWLq05KwYmZHphTU/fmi3Oe/ckyxo2w3Ayo/SCO/rU2NB90jtCJfz9i1ow==
-----END PUBLIC KEY-----`;

    // Extract the required headers from the signature header
    const headersMatch = /headers="(.*?)"/.exec(signatureHeader);
    if (!headersMatch || headersMatch.length < 2) {
        throw new Error('Invalid signature header format: missing headers');
    }
    const requiredHeaders = headersMatch[1].split(' ');

    // Construct the content to be verified including the required headers
    const content = requiredHeaders.map(header => {
        if (header === '(request-target)') {
            // Extract the request-target from the original request
            return `${header}: ${req.method.toLowerCase()} ${req.url}`;
        } else {
            // Use the corresponding header value from the original request
            return `${header}: ${req.headers[header]}`;
        }
    }).join('\n');

    // Verify signature using the parsed public key and the constructed content
    const verifier = crypto.createVerify('sha256');
    verifier.update(content);

    return verifier.verify(
        {key: publicKey, dsaEncoding: "ieee-p1363" },
        Buffer.from(signature, 'base64')
    )
}

@gissellestarch
Copy link

Hi there.

I have created webhooks with the attribute "signkey" added with the value "preset" so that BlockCypher will create a signature for the requests. This is described here: https://www.blockcypher.com/dev/bitcoin/#webhook-signing

I am trying to verify the signature when receiving webhook requests and I cannot find any documentation that shows how it is done. This is the current function I have created in Node.js and it is returning false:

function verifySignature(req) {
    // Get the signature value from the headers
    if (!('signature' in req.headers)) {
        throw new Error('Signature is missing in the headers');
    }
    const signatureHeader = req.headers['signature'];

    // Extract signature value from the signature header
    const signatureMatch = /signature="(.*?)"/.exec(signatureHeader);
    if (!signatureMatch || signatureMatch.length < 2) {
        throw new Error('Invalid signature header format');
    }
    const signature = signatureMatch[1];

    // Parse the provided public key into a format compatible with crypto module
    const publicKey = `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEflgGqpIAC9k65JicOPBgXZUExen4
rWLq05KwYmZHphTU/fmi3Oe/ckyxo2w3Ayo/SCO/rU2NB90jtCJfz9i1ow==
-----END PUBLIC KEY-----`;

    // Extract the required headers from the signature header
    const headersMatch = /headers="(.*?)"/.exec(signatureHeader);
    if (!headersMatch || headersMatch.length < 2) {
        throw new Error('Invalid signature header format: missing headers');
    }
    const requiredHeaders = headersMatch[1].split(' ');

    // Construct the content to be verified including the required headers
    const content = requiredHeaders.map(header => {
        if (header === '(request-target)') {
            // Extract the request-target from the original request
            return `${header}: ${req.method.toLowerCase()} ${req.url}`;
        } else {
            // Use the corresponding header value from the original request
            return `${header.toLowerCase()}: ${req.headers[header]}`;
        }
    }).join('\n');

    // Verify signature using the parsed public key and the constructed content
    const verifier = crypto.createVerify('sha256');
    verifier.update(content);

    return verifier.verify(publicKey, signature, 'base64');
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants
@Olsm @gissellestarch and others