One way to insert an arbitrary SHA-256 hash into the blockchain is by using it to generate a Bitcon address, then sending a very small amount of Bitcoin to that address. The Bitcoin wiki does a good job of covering the actual process of generating an address from an ECDSA private key here and here.
If you'd prefer not to have to perform the computation manually, however, there's a useful command-line utility called bitcoin-tool
. Suppose we want to generate a Bitcoin address from the SHA-256 hash of the string "test":
$ ./bitcoin-tool \
--input-file <(echo -n "test" | openssl dgst -sha256 -binary) \
--input-format raw \
--input-type private-key \
--network bitcoin \
--output-type all \
--public-key-compression uncompressed
address.hex:00b311d5766f9623408747554bcdec1d8dc05eeaf0
address.base58:13VhJywL2p5upGoXpU3RvECR7Heoq
address.base58check:1HKqKTMpBTZZ8H5zcqYEWYBaaWELrDEXeE
public-key-ripemd160.hex:b311d5766f9623408747554bcdec1d8dc05eeaf0
public-key-ripemd160.base58:3VhJywL2p5upGoXpU3RvECR7Heoq
public-key-ripemd160.base58check:HKqKTMpBTZZ8H5zcqYEWYBaaWELuen1WX
public-key-sha256.hex:c8d47a3b796bce36d80dd2e8622ce1bcc4eab1f4a78e8cd3e12b7db44d1c428a
public-key-sha256.base58:EWxTRzHpLN7GjXx6nwqDWJ6DSmrNTVYroZ2VdC7fg8Gq
public-key-sha256.base58check:2XSw67i599jF6FWxAApzSy2xBec9HrNCT7ZqWUo5dFhkqoDeQ3
public-key.hex:045f81956d5826bad7d30daed2b5c8c98e72046c1ec8323da336445476183fb7ca54ba511b8b782bc5085962412e8b9879496e3b60bebee7c36987d1d5848b9a50
public-key.base58:PPBn9d92VkgAZeSLkWyyRSzZusyAaR79PQEjVGX2UoXqHxmgEmr1BvJBYtXXux6TrytQ7FtYqAV2h7TxuMN1sxH5
public-key.base58check:3XTsV9raUUfajGdt6HibXREKMHXkZBQY6BLs9NSVGfE63GRFzKeBYQzCRVQzkkAQhnDkry1S6DdSXpmshbGuL73oiRr6Rs
private-key-wif.hex:809f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
private-key-wif.base58:fD8GVhUgAUmnrJPuyfqkUMDCk3RueN9bTskWv8nEsDWYF
private-key-wif.base58check:5K2YUVmWfxbmvsNxCsfvArXdGXm7d5DC9pn4yD75k2UaSYgkXTh
private-key.hex:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
private-key.base58:Bjj4AWTNrjQVHqgWbP2XaxXz4DYH1WZMyERHxsad7b2w
private-key.base58check:2DFtpKRbW2nfrzgAgE25onW3vwCQwM7S1iHk34LW9cwH1kzmHp
We would then proceed to send a small amount of Bitcoin to the address on the third line: 1HKqKTMpBTZZ8H5zcqYEWYBaaWELrDEXeE
. Sure enough, this address has seen a lot of activity over the years. Unsurprisingly, it has a zero balance. If someone were to send coins to that address, we'd be able to spend them, since we have the private key.
Notice that, in the above example, we opted to output an uncompressed public key. We could have instead chosen to compress the public key, and the output would've been different:
$ ./bitcoin-tool \
--input-file <(echo -n "test" | openssl dgst -sha256 -binary) \
--input-format raw \
--input-type private-key \
--network bitcoin \
--output-type all \
--public-key-compression compressed
address.hex:005ec7f2d15a1f20eab67d60f09effca58b43eee9f
address.base58:12Kb2TaC79MfRSvRsaivP8eUWQYUW
address.base58check:19eA3hUfKRt7aZymavdQFXg5EZ6KCVKxr8
public-key-ripemd160.hex:5ec7f2d15a1f20eab67d60f09effca58b43eee9f
public-key-ripemd160.base58:2Kb2TaC79MfRSvRsaivP8eUWQYUW
public-key-ripemd160.base58check:9eA3hUfKRt7aZymavdQFXg5EZ6KD5ucx5
public-key-sha256.hex:2ad1722dab79ddc2bb3a7b7fe3b397176bda623ad9ad94ca52022aa44f5213c9
public-key-sha256.base58:3t9Mnijb12wM4TYXj4jgAAUyMgK6o9eUbqwWTruXPeRN
public-key-sha256.base58check:KrjTuGpsnRCbBDsP4UGYKa2SeFhDAfWLruDUeEQMtTPyTLqaV
public-key.hex:025f81956d5826bad7d30daed2b5c8c98e72046c1ec8323da336445476183fb7ca
public-key.base58:htT5U6vHyh8jy39y6AuJuRDH95z23Q1VgRpNmTBK2aPb
public-key.base58check:5cYvx6NBYNdcJUym9WydRRs6329UTzJgzKii8dESmw2ZXwAYnt
private-key-wif.hex:809f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a0801
private-key-wif.base58:3ueX5Pff9rqahNbn8q8SAsiCrpyiwMvLw7d153XLgCeDNtp
private-key-wif.base58check:L2ZovMyTxxQVJmMtfQemgVcB5YmiEDapDwsvX6RqvuWibgUNRiHz
private-key.hex:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
private-key.base58:Bjj4AWTNrjQVHqgWbP2XaxXz4DYH1WZMyERHxsad7b2w
private-key.base58check:2DFtpKRbW2nfrzgAgE25onW3vwCQwM7S1iHk34LW9cwH1kzmHp
Note that the resulting address is different: 19eA3hUfKRt7aZymavdQFXg5EZ6KCVKxr8
. However, it's still an equally valid Bitcoin address, and it too has seen a bit of activity over the years, though not nearly as much as the address associated with the uncompressed key.
It's important to understand that, even though the public keys and addresses are different, the private keys are the same. Observe that the last three lines of each output are identical:
private-key.hex:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
private-key.base58:Bjj4AWTNrjQVHqgWbP2XaxXz4DYH1WZMyERHxsad7b2w
private-key.base58check:2DFtpKRbW2nfrzgAgE25onW3vwCQwM7S1iHk34LW9cwH1kzmHp
Nonetheless, you should be careful when converting raw private keys to addresses, since you can easily end up with different addresses.
So far, we've covered converting an arbitrary SHA-256 hash into a private key, but we can also convert one into a public key. This may be more suitable for public proofs. For example, if you publish a document, and you want others to be able to verify that it existed prior to a certain point in time, you can take the SHA-256 hash of that file, convert it into a public key, then send some small amount of Bitcoin to the associated address. There is, in fact, a real life example of this. Isis Agora Lovecruft, a core developer for the Tor Project, was being harassed by the FBI. She documented this in a post on her blog. In the post, she wrote:
In case the FBI is seeking data on Tor users or Tor bridges, and especially in case the subpoena turns out to be sealed or accompanied by an NSL: the original published contents of this post are archived as a PDF here, and the RIPE160(SHA256(PDF)) is equal to 5541405e08048658cf457b3c59bf42a51f84a1a3 and hence Bitcoin address 18mnc4BCud3vjAdLbCc3QhyrjN84VTT1iM, in order to prove in a cryptographically verifiable manner that I published before that point in time.
In this case, the SHA-256 hash of the PDF was converted to a uncompressed public key, which we can verify like so:
$ ./bitcoin-tool \
--input-file <(curl https://fyb.patternsinthevoid.net/blog.patterns-in-the-void-2016-04-30.pdf | openssl dgst -sha256 -binary) \
--input-format raw \
--input-type public-key-sha \
--network bitcoin \
--output-type address \
--output-format base58check \
--public-key-compression uncompressed
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 4022k 100 4022k 0 0 1228k 0 0:00:03 0:00:03 --:--:-- 1229k
address.hex:005541405e08048658cf457b3c59bf42a51f84a1a3
address.base58:12Bta5yx2FwcU7yg2B6V3u9HFFWGS
address.base58check:18mnc4BCud3vjAdLbCc3QhyrjN84VTT1iM
public-key-ripemd160.hex:5541405e08048658cf457b3c59bf42a51f84a1a3
public-key-ripemd160.base58:2Bta5yx2FwcU7yg2B6V3u9HFFWGS
public-key-ripemd160.base58check:8mnc4BCud3vjAdLbCc3QhyrjN84WuGgTg
public-key-sha256.hex:0e4498fb5f299159452f97f9113edfb7dbb3973859f96c22c874dc7dafbdcb27
public-key-sha256.base58:xhNoGDN2QGu7J3bf8aYM8xrvuvJcuEBqQQGZwqsfraz
public-key-sha256.base58check:7HTSYwK5m87PyqNaAKz9H87PAzubhzP2ev9LoAxZcCvQjqcXA
Sure enough, public-key-ripemd160.hex:5541405e08048658cf457b3c59bf42a51f84a1a3
matches the posted value of RIPE160(SHA256(PDF))
, and address.base58check:18mnc4BCud3vjAdLbCc3QhyrjN84VTT1iM
matches the posted Bitcoin address. Finally, the deposit of 0.001 BTC to this address on 2016-05-01 proves that the address existed as of that date. Since generating a PDF after the fact (i.e., to match an existing Bitcoin address) would require, at the very least, a successful preimage attack against full round SHA-256, the conjunction of these facts prove beyond any reasonable (cryptographically-informed) doubt that the document in question existed at least as early as 2016-05-01.