Secure (Client-Side) Signing Reference Suite

We have provided 5 language libraries (Ruby, NodeJS, PHP, Python, Java) that perform secure signing of transactions by default. However, if you need to use a currently unimplemented language, you can create your own library with client-side signing.

You can use this reference to implement client-side signing in any language of your choice. When encrypting/decrypting/signing data, you must have a reference to avoid some very annoying mistakes. Please use the reference inputs and outputs provided below to test your implementation.

This reference is your friend. If you're stuck somewhere, or confused, contact us.

Overview: How to Sign Transactions Client-Side

To use client-side signing, make a withdrawal request through the Block.io API, and do not provide your Secret PIN. The response will indicate data_to_sign fields, and public_keys that need to sign those data, i.e., scripts.

Example Withdrawal Response:

{
  "status": "success",
  "data": {
    "reference_id": "29399635d5d583da8383c0a87cbc270aef3f45bd58dc23b8107b439b7ade7d01",
    "inputs": [
      {
        "input_no": 0,
        "signatures_needed": 1,
        "data_to_sign": "1ccff1633671f403a14523f798137a3f553297e92c82e245684e0bbc24ea5d3e",
        "signers": [
          {
            "signer_public_key": "034b129d950fe7888309e8528b712985fe5e3ee9855b56f8c3bbd7726272152204",
            "signed_data": null
          }
        ]
      }
    ],
    "encrypted_passphrase": {
      "signer_public_key": "034b129d950fe7888309e8528b712985fe5e3ee9855b56f8c3bbd7726272152204",
      "passphrase": "oG77kBvE/47n4erL0utiPXWHRV..."
    }
  }
}

For basic multi-signature addresses (non-dtrust), we will provide your encrypted passphrase stored at Block.io. (For dTrust addresses, you must use private keys stored at your end.) You must decrypt this passphrase with your Secret PIN (see Steps 1-7). The result is the Private Key needed to sign the data_to_sign (Steps 8-14).

Iterate over the inputs array, and sign each data_to_sign. For each signature you generate, replace the "signed_data": null field in the respective input. With the signed data, the withdrawal response now looks like this:

Example Signed Withdrawal Request:

{
    "reference_id": "29399635d5d583da8383c0a87cbc270aef3f45bd58dc23b8107b439b7ade7d01",
    "inputs": [
      {
        "input_no": 0,
        "signatures_needed": 1,
        "data_to_sign": "1ccff1633671f403a14523f798137a3f553297e92c82e245684e0bbc24ea5d3e",
        "signers": [
          {
            "signer_public_key": "034b129d950fe7888309e8528b712985fe5e3ee9855b56f8c3bbd7726272152204",
            "signed_data": "!!!YOU GENERATED THIS SIGNATURE!!!"
          }
        ]
      }
    ],
    "encrypted_passphrase": {
      "signer_public_key": "034b129d950fe7888309e8528b712985fe5e3ee9855b56f8c3bbd7726272152204",
      "passphrase": "oG77kBvE/47n4erL0utiPXWHRV..."
    }
}

Note above that we have extracted the withdrawal response's data field. Once signed, convert this object into a JSON string. This JSON string becomes the value of the signature_data POST parameter for the final sign_and_finalize_withdrawal API call, along with your api_key. Using cURL, our API call would look like this:


$ curl https://block.io/api/v2/sign_and_finalize_withdrawal -d 'api_key=YOUR API KEY&signature_data={"reference_id":"...","inputs":[...]}'

If successful, your withdrawal will be processed, and you will receive the Transaction ID as proof.

Steps to a Successful Client-Side Signature

To generate an Encryption Key from a Secret PIN, we use the following standard at Block.io:

  • (0) Use a strong Secret PIN:
  • (1) Pass (0) through the PBKDF2 function with 128 Bit Key Length and 1,024 iterations of SHA256:
  • (2) Pass (1) through the PBKDF2 function with 256 Bit Key Length and 1,024 iterations of SHA256.
  • (3) Final Encryption Key from (2):

Now that we have an Encryption Key, we will use the AES-256-ECB cipher to encrypt some basic text:

  • (4) Data to Encrypt:
  • (5) Pass (4) as data, and (3) as encryption key to AES-256-ECB cipher.
  • (6) Encrypted result as a Base64 String (new lines or spaces removed):
  • (7) Sanity check, let's decrypt the encrypted data:

Now let's use the decrypted data as a Private Key, and get its Public Key:

  • (8) Pass (7) through SHA256 once:
  • (9) We use (8) as the Private Key.
  • (10) Use (8) to get Public Key:
  • Note: The Public Key derivation from (9) to (11) occurs through the Secp256k1 curve. You don't need to know what the details are. Instead, just use your programming language's Bitcoin or OpenSSL libraries to achieve the same result. OpenSSL supports Secp256k1 by default.

Finally, let's sign some data. This final step puts you in control of your addresses -- you sign your transactions outside of Block.io.

  • Signing of data in Dogecoin/Bitcoin/Litecoin occurs through the Secp256k1 curve. Use your language library to figure out what method signs the data. Typically, when you create a Key object, that Key object will allow you to 'sign' data.
  • (11) Data to Sign:
  • (12) Convert (11) to Hex if it isn't:
  • (13) Signed version of (12) using Private Key in (9):
  • (14) Use Block.io to verify signature (13) is valid for Public Key in (10):
  • In Step (14), we used the following verify_signature API call:

    
    
        

That's it. If you reached this far successfully, you're all set to do client-side signing in the language of your choice.

If you were not successful with any of these steps, just ask. We're a few clicks away.

Need help? We're here for you. Contact us.