How does the callback signature work?
BlockBee's callback signature uses a 1024-bit RSA SHA256 signature with a public-key signature scheme to sign the entire callback sent to your service, ensuring that all data was sent from our service and can be trusted.
If the request is sent via GET, the full URL with all GET parameters is signed. If the callback is requested via POST, the entire request body is signed.
The signature is sent via the x-ca-signature header of the request, and is base64-encoded. The public key used to validate the signature can be fetched from the following endpoint: https://api.blockbee.io/pubkey/.
How do I validate the callback?
Here is an example of how the data provided to the verification function must look like.
uuid=TEST_aabf0e8e-cf58-4719-b5db-237c3e9a32c0&address_in=TEST_0x0000000000000000000000000000000000000000&address_out=TEST_0x0000000000000000000000000000000000000000&confirmations=1&txid_in=TEST_0x0000000000000000000000000000000000000000000000000000000000000000&txid_out=TEST_0x0000000000000000000000000000000000000000000000000000000000000000&fee=10000&fee_coin=0.01&value=1000000&value_coin=1&value_forwarded=990000&value_forwarded_coin=0.99&coin=test_coin&price=1&result=sent&pending=0
Below are examples of achieving this using various programming languages.
PHP
<?php $pubkey = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC3FT0Ym8b3myVxhQW7ESuuu6lo\ndGAsUJs4fq+Ey//jm27jQ7HHHDmP1YJO7XE7Jf/0DTEJgcw4EZhJFVwsk6d3+4fy\nBsn0tKeyGMiaE6cVkX0cy6Y85o8zgc/CwZKc0uw6d5siAo++xl2zl+RGMXCELQVE\nox7pp208zTvown577wIDAQAB\n-----END PUBLIC KEY-----"; $signature = base64_decode($_SERVER['HTTP_X_CA_SIGNATURE']); $algo = OPENSSL_ALGO_SHA256; // if request is GET $data = "https://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"; // if request is POST $data = file_get_contents('php://input'); if (openssl_verify($data, $signature, $pubkey, $algo) == 1) { // signature valid } else { // signature NOT valid }
Python (Django)
First install pyOpenSSL.
import base64 from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives import serialization from cryptography.hazmat.backends import default_backend pub_str = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC3FT0Ym8b3myVxhQW7ESuuu6lo\ndGAsUJs4fq+Ey//jm27jQ7HHHDmP1YJO7XE7Jf/0DTEJgcw4EZhJFVwsk6d3+4fy\nBsn0tKeyGMiaE6cVkX0cy6Y85o8zgc/CwZKc0uw6d5siAo++xl2zl+RGMXCELQVE\nox7pp208zTvown577wIDAQAB\n-----END PUBLIC KEY-----"; try: if request.method == 'POST': data = request.body else: data = request.build_absolute_uri() sig_b64 = request.headers['x-ca-signature'] # Load public key public_key = serialization.load_pem_public_key( pub_str.encode('utf8'), backend=default_backend() ) # Decode the signature from base64 signature = base64.b64decode(sig_b64) # Verify the signature public_key.verify( signature, data.encode('utf8'), padding.PKCS1v15(), hashes.SHA256() ) return True except Exception: return False
Python (Flask)
pub_str = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC3FT0Ym8b3myVxhQW7ESuuu6lo\ndGAsUJs4fq+Ey//jm27jQ7HHHDmP1YJO7XE7Jf/0DTEJgcw4EZhJFVwsk6d3+4fy\nBsn0tKeyGMiaE6cVkX0cy6Y85o8zgc/CwZKc0uw6d5siAo++xl2zl+RGMXCELQVE\nox7pp208zTvown577wIDAQAB\n-----END PUBLIC KEY-----"; @app.route("/callback", methods=['GET', 'POST']) def callback(): try: if request.method == 'POST': data = request.get_data(as_text=True) else: data = request.url sig_b64 = request.headers.get('x-ca-signature') if not sig_b64: return jsonify({"error": "Missing signature header"}), 400 public_key = serialization.load_pem_public_key( pub_str.encode('utf8'), backend=default_backend() ) signature = base64.b64decode(sig_b64) public_key.verify( signature, data.encode('utf8'), padding.PKCS1v15(), hashes.SHA256() ) return jsonify({"verified": True}) except Exception as e: return jsonify({"verified": False, "error": str(e)}), 400 except Exception: return False
Node.js
let crypto = require("crypto") let pubkey = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC3FT0Ym8b3myVxhQW7ESuuu6lo\ndGAsUJs4fq+Ey//jm27jQ7HHHDmP1YJO7XE7Jf/0DTEJgcw4EZhJFVwsk6d3+4fy\nBsn0tKeyGMiaE6cVkX0cy6Y85o8zgc/CwZKc0uw6d5siAo++xl2zl+RGMXCELQVE\nox7pp208zTvown577wIDAQAB\n-----END PUBLIC KEY-----" let sig_b64 = req.headers['x-ca-signature'] let signature = new Buffer(sig_b64, 'base64'); // if GET let data = req.protocol + '://' + req.get('host') + req.originalUrl; // if POST rawBody = '' req.on('data', function(chunk) { rawBody += chunk }); req.on('end', function(){ let data = rawBody verify(data, pubkey, signature) }); function verify(data, pubkey, signature) { let verifier = crypto.createVerify('RSA-SHA256'); verifier.update (data); if (verifier.verify (pubkey, signature)) { // signature is valid } else { // signature NOT valid } }
Was this article helpful?
That’s Great!
Thank you for your feedback
Sorry! We couldn't be helpful
Thank you for your feedback
Feedback sent
We appreciate your effort and will try to fix the article