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:
label=-
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:
label=-
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.