Using blockchain technology to store documents or document hashes on a blockchain provides many advantages including easy validation and tamper resistance. In an older blog article, we explained how to store signed documents on a separate blockchain for each document.

The ASP.NET Core (.NET 6) sample project in this article is based on the same concept, but uses only one blockchain to store all transactions and to validate document hashes based on the integrity of previous blocks.

TX Text Control Blockchain

Tamper Resistance

Each block in a blockchain is cryptographically linked to the previous block by hashing the previous block hash into the current block hash. The highly effective tamper resistance of a blockchain prevents document fraud and enables easy validation processes. Storing a complete document on a blockchain is technically possible, but file size limitations might prevent you from utilizing this strategy. Hashes are smaller in size and therefore a more efficient option to store documents on a blockchain. Every time a document is changed, the cryptographic hash will change. As the block hash contains the document data, the blockchain can be used to verify the document by validating the integrity of the blockchain.

In this sample, the hash of each block is created by hashing the following components:

  • Timestamp
  • Previous block hash
  • Data (our document hash)
  • Nonce

A nonce ("number only used once") is a number added to the block hash that, when rehashed, meets the difficulty level restrictions.

internal string GenerateBlockHash()
SHA256 sha256 = SHA256.Create();
byte[] bInput =
Encoding.ASCII.GetBytes($"{TimeStamp}-{PreviousBlockHash ?? ""}-{Data}-{Nonce}");
byte[] bOutput = sha256.ComputeHash(bInput);
return Base64UrlEncoder.Encode(bOutput);
view raw test.cs hosted with ❤ by GitHub

If any component in the blockchain is tampered, the complete blockchain is invalid. In this sample, the Data that is stored is an MD5 hash of the signed document and additional information:

public class SignedDocument
public string Hash { get; set; }
public string Signer { get; set; }
public string DocumentId { get; set; }
view raw test.cs hosted with ❤ by GitHub

A simplified workflow is shown in the illustration below:

TX Text Control Blockchain

Storing the Document

This sample uses the TX Text Control DocumentViewer to sign a document. The signed PDF is stored as a hash on the blockchain and can be downloaded locally. After a document is stored, the resulting blockchain JSON looks similar this:

"Data":"{\"Hash\":\"c20ed3409dbcd3b89af77c9d47e5d9d2\",\"Signer\":\"Tim Typer\",\"DocumentId\":\"6003f9e8-3340-4231-82ad-2dd7b721142f\"}",
"Data":"{\"Hash\":\"4cd18f7b2e0200de1d4715ef2c92492e\",\"Signer\":\"Tim Typer\",\"DocumentId\":\"1d0394b7-660e-4408-a447-0d77f5bc39b9\"}",
view raw test.json hosted with ❤ by GitHub

You can see that each block contains the PreviousBlockHash and the Data. The BlockHash itself is generated based on the PreviousBlockHash, the timestamp and the Data itself.

Document Validation

After signing the document, the created PDF can be validated by uploading it:

TX Text Control Blockchain

The method ValidateDocument creates an MD5 hash of the uploaded document and compares that value with the stored value in the block specified by the blockHash:

public bool ValidateDocument(string document, string blockHash)
if (document == null || blockHash == null)
return false;
// calculate the MD5 of the uploaded document
string sChecksum = Checksum.CalculateMD5(Convert.FromBase64String(document));
// load the associated blockchain
Blockchain bcDocument = new Blockchain(sBlockchainPath);
Block blockDocument = bcDocument.GetBlock(blockHash);
if (blockDocument == null)
return false;
if (bcDocument.IsValid(blockDocument.BlockHash)) {
// get the SignedDocument object from the block
SignedDocument signedDocument =
// compare the checksum in the stored block
// with the checksum of the uploaded document
return (signedDocument.Hash == sChecksum ? true : false);
else return false;
view raw test.cs hosted with ❤ by GitHub

Before the hashes are compared, the integrity of the blockchain is checked by calling the IsValid method:

// checks, if the blockchain is consistent by
// re-generating and comparing the hashes in each block
public bool IsValid(string blockHash = "") {
// check all blocks
int iNumberBlocks = Chain.Count;
// check all blocks until the given, optional hash
if (blockHash != "") {
Block block = Chain.FirstOrDefault(h => h.BlockHash == blockHash);
if (block != null) {
iNumberBlocks = block.Index + 1;
// loop through all blocks, generate their hashes and compare
for (int i = 1; i < iNumberBlocks; i++) {
Block currentBlock = Chain[i];
Block previousBlock = Chain[i - 1];
if (currentBlock.BlockHash != currentBlock.GenerateBlockHash())
return false;
if (currentBlock.PreviousBlockHash != previousBlock.BlockHash)
return false;
return true;
view raw test.cs hosted with ❤ by GitHub

This method is literally looping through all blocks (until the current one), re-generates and compares the hashes for each block.

The sample also includes an overview of the current blockchain by displaying all entries and the stored data:

TX Text Control Blockchain


A blockchain can be used to store document hashes to validate the integrity of a document. A blockchain always contains information about the most current version of an uploaded or signed document.

You can download this sample application from our GitHub repository.