How to selectively reveal data on a blockchain

By design, any raw data stored on a blockchain is visible to every node connected to the chain. In many cases, this loss of confidentiality poses a significant problem. In this tutorial we will learn a common approach to resolving the confidentiality problem, where data is encrypted before being stored and timestamped on the chain. The password for reading the encrypted data is only made available to a subset of blockchain participants, leaving the others unable to read it. By using a combination of symmetric and asymmetric cryptography, we can do this in an efficient way.

The method makes use of three blockchain streams, whose purposes are as follows:

  • One stream, which we call pubkeys, is used by participants to distribute their public keys under the RSA public-key cryptography scheme.
  • A second stream, which we call items, is used to publish large pieces of data, each of which is encrypted using symmetric AES cryptography.
  • A third stream, which we call access, provides data access. For each participant who should see a piece of data, a stream entry is created which contains that data’s secret password, encrypted using that participant’s public key.

The tutorial requires two servers running Linux and the bash shell, as well as openssl and xxd which you should install as necessary. Both servers should have a multichaind node up and running on the same blockchain, with no native currency or other unusual parameters. If you don’t yet have this, follow the instructions in sections 1 and 2 of the Getting Started guide. The first server’s node should also have an address with admin and create permissions – this will automatically be the case if it started the chain.

Create the streams

To begin with, enter the name of the blockchain you are running:

On the first server, create the three streams as follows from the bash command line:

multichain-cli create stream pubkeys true
multichain-cli create stream items true
multichain-cli create stream access true

Each command should return a 64-character hexadecimal transaction ID, showing the transaction which created the stream. The true values mean the streams are open for writing by any participant who has global send permissions for the chain. You can view these new streams, along with any others previously created on the chain, as follows:

multichain-cli liststreams

Publish an RSA key pair

In this tutorial, the first server will be reading confidential content written by the second server. As a first step, we will generate an RSA public key on the first server, and publish it to the stream for the second server to read.

On the first server, find a local address which has send permissions by running these two commands:

multichain-cli listpermissions send
multichain-cli listaddresses

An address is suitable if it appears in the output of the first command, and also in the second command together with "ismine" : true.

Enter the address here:

Now let’s create a directory for RSA private keys, use openssl to store a new key there, and take a look:

mkdir ~/.multichain//stream-privkeys/
openssl genpkey -algorithm RSA -out ~/.multichain//stream-privkeys/.pem
cat ~/.multichain//stream-privkeys/.pem

You should see a block of alphanumeric gibberish, surrounded by BEGIN PRIVATE KEY and END PRIVATE KEY lines. This is an RSA private key. Now let’s generate a public key from that, convert it immediately to hexadecimal, store it in the pubkeyhex shell variable, and take a look:

pubkeyhex=$(openssl rsa -pubout -in ~/.multichain//stream-privkeys/.pem | xxd -p -c 9999)
echo $pubkeyhex

Finally, let’s publish our public key as an unlabelled item in the pubkeys stream, which should return a transaction ID:

multichain-cli publishfrom pubkeys '' $pubkeyhex

Store some encrypted data

Now let’s move to the second server. To begin, let’s choose an address we will use for posting confidential stream items:

multichain-cli getaddresses

Enter any address here:

Now use the first server to give global send and receive (for change) permissions to this address:

multichain-cli grant send,receive

Back on the second server, let’s create a random password, use it to encrypt the /proc/cpuinfo operating system file with the AES algorithm, convert the result immediately to hexadecimal, store it in the cipherhex shell variable, and take a look:

password=$(openssl rand -base64 48)
cipherhex=$(openssl enc -aes-256-cbc -in /proc/cpuinfo -pass pass:$password | xxd -p -c 99999)
echo $cipherhex

Now we can publish this large chunk of hexadecimal data to the items stream:

multichain-cli publishfrom items secret-cpuinfo $cipherhex

Copy and paste the displayed txid here:

Now let’s create a directory for item passwords, and store this one for future reference:

mkdir ~/.multichain//stream-passwords/
echo $password > ~/.multichain//stream-passwords/.txt

Publish the encrypted password

Remaining on the second server, we need to first subscribe to the pubkeys stream, and then retrieve the first server’s RSA public key from that stream:

multichain-cli subscribe pubkeys
multichain-cli liststreampublisheritems pubkeys true 1

Copy and paste the txid shown:
Copy and paste the vout shown:

Although the data we need may already be shown in the response, we will retrieve it in a separate step to make it easier to convert from hexadecimal and place in a temporary file, whose contents we can view:

multichain-cli gettxoutdata | tail -n 1 | xxd -p -r > /tmp/pubkey.pem
cat /tmp/pubkey.pem

You should see a block of alphanumeric gibberish, surrounded by BEGIN PUBLIC KEY and END PUBLIC KEY lines. This is the RSA public key which was published in the pubkeys stream earlier. Now let’s use this public key to encrypt the item’s password, convert it immediately to hexadecimal, store it in the keycipherhex shell variable, and take a look:

keycipherhex=$(echo $password | openssl rsautl -encrypt -inkey /tmp/pubkey.pem -pubin | xxd -p -c 9999)
echo $keycipherhex

Now let’s publish this encrypted password to the access stream, using the previous stream item txid and target address together as a label for the stream item, to enable the other party to easily find it:

multichain-cli publishfrom access $label $keycipherhex

Retrieving the encrypted item

Let’s go back to the first server. We need to subscribe to the items and access streams, then look out for the latest item to be published:

multichain-cli subscribe '["items","access"]'
multichain-cli liststreamitems items true 1

The txid shown should match:
Copy and paste the vout shown:

Although the data we need may already be shown in the response, we will retrieve it in a separate step to make it easier to assign to a shell variable:

cipherhex=$(multichain-cli gettxoutdata | tail -n 1)

Now let’s retrieve the password for this item, which was encrypted especially for this server using its public key:

multichain-cli liststreamkeyitems access $label true

Copy and paste the txid shown:
Copy and paste the vout shown:

As before, although the data we need may already be shown in the response, we will retrieve it in a separate step:

keycipherhex=$(multichain-cli gettxoutdata | tail -n 1)

We can retrieve the original password by decrypting this using the RSA private key stored earlier on this server:

password=$(echo $keycipherhex | xxd -p -r | openssl rsautl -decrypt -inkey ~/.multichain//stream-privkeys/.pem)

And finally we can use this password to decrypt the stream item content, to reveal the secret data that was stored:

echo $cipherhex | xxd -p -r | openssl enc -d -aes-256-cbc -pass pass:$password

Where to go from here

That’s it! The second server was able to send its /proc/cpuinfo file to the first server over the blockchain, without the content being seen by any other node. The file could be revealed to an additional participant by republishing the password in the access stream, encrypted using the RSA public key which the additional participant published in the pubkeys stream. There is never a need to store the large file again in the items stream. Notwithstanding the restricted visibility, all nodes in the network participate in storing, timestamping and notarizing the published information.

A real application using this technique would be unlikely to use command line tools as in this tutorial. Instead, it would access MultiChain commands using the JSON-RPC API, and take advantage of the native encryption libraries of the programming language used. The RSA and AES encryption algorithms above can be replaced with any public-private key and symmetric encryption techniques respectively.