{"id":20347793,"url":"https://github.com/itext/pdfchain","last_synced_at":"2026-03-06T06:32:00.984Z","repository":{"id":31607523,"uuid":"128402585","full_name":"itext/pdfchain","owner":"itext","description":"pdfChain: (experimental) blockchain for the masses","archived":false,"fork":false,"pushed_at":"2026-02-06T09:10:25.000Z","size":498,"stargazers_count":16,"open_issues_count":26,"forks_count":5,"subscribers_count":13,"default_branch":"master","last_synced_at":"2026-02-06T17:24:36.479Z","etag":null,"topics":["blockchain","itext","itext7","pdf"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/itext.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-04-06T14:04:15.000Z","updated_at":"2025-10-06T09:02:40.000Z","dependencies_parsed_at":"2023-10-16T06:19:24.568Z","dependency_job_id":"abe01a5c-590b-4a4a-8a53-da94517cc804","html_url":"https://github.com/itext/pdfchain","commit_stats":null,"previous_names":["itext/pdfchain","itext/i7j-pdfchain"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/itext/pdfchain","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itext%2Fpdfchain","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itext%2Fpdfchain/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itext%2Fpdfchain/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itext%2Fpdfchain/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/itext","download_url":"https://codeload.github.com/itext/pdfchain/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itext%2Fpdfchain/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30164594,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T04:43:31.446Z","status":"ssl_error","status_checked_at":"2026-03-06T04:40:30.133Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blockchain","itext","itext7","pdf"],"created_at":"2024-11-14T22:18:07.813Z","updated_at":"2026-03-06T06:32:00.941Z","avatar_url":"https://github.com/itext.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿# pdfChain: (experimental) blockchain for the masses\n\n## What is a blockchain?\n\nA blockchain is a distributed database that is used to maintain a continuously growing list of records, called blocks. \nEach block contains a timestamp and a link to a previous block. \nA blockchain is typically managed by a peer-to-peer network collectively adhering to a protocol for validating new blocks. \nBy design, blockchains are inherently resistant to modification of the data. \nOnce recorded, the data in any given block cannot be altered retroactively without the alteration of all subsequent blocks and a collusion of the network majority. \nFunctionally, a blockchain can serve as \"an open, distributed ledger that can record transactions between two parties efficiently and in a verifiable and permanent way. \nThe ledger itself can also be programmed to trigger transactions automatically.\"\n\n## Why should you use it?\n\nA blockchain supersedes older technology that deals with authentication and non-repudiation.\nFirst, there are many ways you can sign a document.\nTypically by \"signing\" we mean creating a hash of a document and storing it.\nWith a blockchain, the useful part is that once such a hash is stored, it can not be changed or deleted. This gives you two advantages:\n\n1. The hash itself identifies the file from which it was computed\n2. The fact that your hash is in the blockchain gives you a point in time when the operation was done.\n\nLater you can say: \nHey, I’ve created this hash on 10 Oct 2016: here is the transaction in the blockchain which contains the hash. I’ve created it according to this formula from this file. \nNow any person can take your file and compute the hash again and verify that it matches the one stored in the blockchain. \nAll this works because:\n\n1. It is very easy to compute the hash from a file but very difficult to craft a similar file which will produce exactly the same hash.\n2. It is practically impossible to change the data stored inside blockchain.\n3. Every transaction in the blockchain has a timestamp, so having the transaction we know exactly when it was done.\n\nThe default iText implementation of the blockchain concept is specifically geared towards pdf documents. It stores:\n - a *hash value* of the document\n - the name of the algorithm that was used for *hashing*\n - a *signed hash value* of the document\n - the name of the algorithm that was used for *signing*\n - the pdf ID array\n \nThis allows you not only to store hash values of documents, but also to digitally sign them.\nBeing able to swap the hashing algorithm (in case of hashing algorithms becoming outdated) enables LTV (long term validation).\n\n## What does iText provide?\n\n### Interfaces that hide implementation details\n\nThe interfaces we impose on blockchain implementations are minimal, yet they provide us with the needed abstraction to enable us to build complex applications and workflows on top of them.\nWe abstract a blockchain as a multimap, allowing end-users to store an object (represented by Record, which is `HashMap\u003cString, Object\u003e`) and tying it to a key (`String`).\n\n![Figure 0: Class Diagram 01](img/class_diagram_1.png)\n\nLet's have a more in depth look at just the layer between blockchain and PDF technology\n\n![Figure 0: Class Diagram 02](img/class_diagram_2.png)\n\n```java\npublic interface IBlockChain {\n\n    /**\n     * Put data on the blockchain\n     *\n     * @param key  the key being used to put the data on the blockchain\n     * @param data the data being put on the blockchain\n     */\n    public boolean put(String key, Record data);\n\n    /**\n     * Get data from the blockchain\n     *\n     * @param key the key being queried\n     * @return\n     */\n    public List\u003cRecord\u003e get(String key);\n\n    /**\n     * Get all data from the blockchain\n     * @return\n     */\n    public List\u003cRecord\u003e all();\n}\n```\n\n### Concrete implementation using JSON-RPC and MultiChain\n\nAs a proof of concept we have provided an implementation of the interface IBlockchain using JSON-RPC (remote procedure call) and MultiChain.\nIf you want to learn more about setting up a blockchain instance with MultiChain, check out their website for more resources,\nin particular the [getting started guide](https://www.multichain.com/getting-started/).\n\n## Prerequisites\n\nBefore you are able to run the examples (tests) in the repository, it is assumed that you have successfully set up a local blockchain node.\nThe tests use following credentials:\n\n- IP: `http://127.0.0.1`\n- port: `4352`\n- chain name: `chain1`\n- username: `multichainrpc`\n- password: `BHcXLKwR218R883P6pjiWdBffdMx398im4R8BEwfAxMm`\n\n## Example(s)\n\n### Putting a document on the blockchain\n\n```java\n\t// define a multichain instance\n\tIBlockChain mc = new MultiChain(\n                \"http://127.0.0.1\",\n                4352,\n                \"chain1\",\n                \"stream1\",\n                \"multichainrpc\",\n                \"BHcXLKwR218R883P6pjiWdBffdMx398im4R8BEwfAxMm\");\n\n\t// provide the details about signing and hashing\n\tsign.AbstractExternalSignature sgn = new sign.DefaultExternalSignature(new File(\"path_to_keystore\"), \"demo\", \"password\");\n\n\t// file being handled\n\tFile inputFile = new File(\"input.pdf\");\n\n\t// instantiate blockchain\n\tpdfchain.PdfChain blockchain = new pdfchain.PdfChain(mc, sgn);\n\t\n\tblockchain.put(inputFile);\n```\n\n### Retrieving document information from the blockchain\n\n```java\n\tIBlockChain mc = new MultiChain(\n                \"http://127.0.0.1\",\n                4352,\n                \"chain1\",\n                \"stream1\",\n                \"multichainrpc\",\n                \"BHcXLKwR218R883P6pjiWdBffdMx398im4R8BEwfAxMm\");\n\n\tsign.AbstractExternalSignature sgn = new sign.DefaultExternalSignature(new File(\"path_to_keystore\"), \"demo\", \"password\");\n\n\tFile inputFile = new File(\"input.pdf\");\n\n\tpdfchain.PdfChain blockchain = new pdfchain.PdfChain(mc, sgn);\n\tfor (Map\u003cString, Object\u003e docEntry : blockchain.get(inputFile)) {\n\t\tfor (Map.Entry\u003cString, Object\u003e entry : docEntry.entrySet())\n\t\t\tSystem.out.println(padRight(entry.getKey(), 32) + \" : \" + entry.getValue());\n\t\tSystem.out.println(\"\");\n\t}\n```\n\nThis yields following example output:\n\n```java\nblocktime                        : 1499691151\nid2                              : �Ʊ��B�}ә`�-o�R\nid1                              : z�L{�Wd=��\u0010��G�\npublishers                       : [14pwDpkcfRvSiw6DJWpP7RdcYgv5NfRRn6Dudr]\ntxid                             : b0092d7eb967ac2e45671742ddf1a0a96bc049a4bbfe3528888b6d9ff396b7a2\nhsh                              : ��B��\u001c\u0014���șo�$'�A�d��L���\u001bxR�U\nconfirmations                    : 22\nkey                              : ï¿½ï¿½Bï¿½ï¿½\u001c\u0014ï¿½ï¿½ï¿½È oï¿½$'ï¿½Aï¿½dï¿½ï¿½Lï¿½ï¿½ï¿½\u001bxRï¿½U\nshsh                             : \u003cgarbled\u003e\n```\n\n### Complex queries on items stored in a blockchain\n\n```java\n\t/* build query\n\t * This query selects only those items with a specific id,\n\t * with either less than 5, or more than 10 confirmations.\n\t * It reduces the columns to [\"id1\", \"id2\", \"blocktime\", \"confirmations\"]\n\t * and sorts the records (default ascending order) by the field \"confirmations\"\n\t */\n\tAbstractBQLOperator op = new SortBy(new Select(\n                                                new And(\n                                                    new Or(\n                                                        new Greater(\"confirmations\", 10),\n                                                        new Smaller(\"confirmations\", 5)\n                                                    ),\n                                                    new EqualID(\"z�L{�Wd=��\\u007F\\u0010��G�\")\n                                                ),\n                                                new String[]{\"id1\", \"id2\", \"blocktime\", \"confirmations\"}\n                                            ),\n                                            \"confirmations\");\n\t// set up blockchain\n\tIBlockChain mc = new MultiChain(\n                \"http://127.0.0.1\",\n                4352,\n                \"chain1\",\n                \"stream1\",\n                \"multichainrpc\",\n                \"BHcXLKwR218R883P6pjiWdBffdMx398im4R8BEwfAxMm\");\n\t\t\t\t\n\tExecutor exe = new Executor(mc);\n\tCollection\u003cRecord\u003e resultSet = exe.execute(op);\n```\n\n### Building complex queries (using a statement)\n\n```java\n\tIBlockChain mc = new MultiChain(\n                \"http://127.0.0.1\",\n                4352,\n                \"chain1\",\n                \"stream1\",\n                \"multichainrpc\",\n                \"BHcXLKwR218R883P6pjiWdBffdMx398im4R8BEwfAxMm\");\n\n\t// build query\n\tAbstractBQLOperator op = BQLCompiler.compile(\"SELECT [id1, id2, confirmations,hsh]( confirmations \u003e 10 AND confirmations \u003c 50 ) SORT confirmations\");\n\n\t// build executor\n\tExecutor exe = new Executor(mc);\n\n\t// execute query\n\tCollection\u003cRecord\u003e resultSet = exe.execute(op);\n\tAssert.assertFalse(resultSet.isEmpty());\n```\n\n### Verifying a signature\n```java\n        // check signature\n        for(Record r : chain.get(\"z�L{�Wd=��\\u007F\\u0010��G�\")){\n            if(chain.isSigned(r, sgn.getPublicKey()))\n                System.out.println(\"This record is signed\");\n        }\n```\n\n## User interface\n\nWe've also provided a small UI that showcases some of the more common  usecases\n\n![Figure 1: Entering a BQL query through the user interface](img/bql_example.png)\n\n## BNF for BQL\n\n```\n\u003cvariable\u003e \t\t\t::= [a-zA-Z0-9]+\n\u003cstring\u003e\t\t\t::= '[a-zA-Z0-9]+'\n\u003cnumber\u003e\t\t\t::= ([0-9]*\\.)*[0-9]+\n\u003ccomma\u003e\t\t\t\t::= ,\n\n\u003carray_content\u003e\t\t::= \u003cvariable\u003e\n\u003carray_content\u003e\t\t::= \u003cvariable\u003e \u003carray_content\u003e\n\u003carray\u003e\t\t\t\t::= [ \u003carray_content\u003e ]\n\n\u003coperator\u003e\t\t\t::= \u003cand\u003e | \u003cor\u003e\n\t\t\t\t\t::= \u003cequal\u003e | \u003cgreater\u003e | \u003cgreater_or_equal\u003e | \u003cnot_equal\u003e | \u003csmaller\u003e | \u003csmaller_or_equal\u003e\n\t\t\t\t\t::= \u003cstarts_with\u003e | \u003cends_with\u003e\n\t\t\t\t\t::= \u003cstar\u003e\n\t\t\t\t\t::= \u003csort\u003e\n\t\t\t\t\t::= \u003cselect\u003e\n\t\t\t\t\n\u003cand\u003e\t\t\t\t::= \u003coperator\u003e AND \u003coperator\u003e\n\u003cor\u003e\t\t\t\t::= \u003coperator\u003e OR \u003coperator\u003e\n\n\u003cequal\u003e\t\t\t\t::= \u003cvariable\u003e == \u003cnumber\u003e\n\t\t\t\t\t::= \u003cvariable\u003e == \u003cstring\u003e\n\n\u003cnot_equal\u003e\t\t\t::= \u003cvariable\u003e != \u003cnumber\u003e\n\t\t\t\t\t::= \u003cvariable\u003e != \u003cstring\u003e\n\t\t\t\t\t\n\u003cgreater\u003e\t\t\t::= \u003cvariable\u003e \u003e \u003cnumber\u003e\n\u003cgreater_or_equal\u003e\t::= \u003cvariable\u003e \u003e= \u003cnumber\u003e\n\u003csmaller\u003e\t\t\t::= \u003cvariable\u003e \u003c \u003cnumber\u003e\n\u003csmaller_or_equal\u003e\t::= \u003cvariable\u003e \u003c= \u003cnumber\u003e\n\n\u003cstarts_with\u003e\t\t::= \u003cvariable\u003e STARTS_WITH \u003cstring\u003e\n\u003cends_with\u003e\t\t\t::= \u003cvariable\u003e ENDS_WITH \u003cstring\u003e\n\n\u003cstar\u003e\t\t\t\t::= \\*\n\n\u003csort\u003e\t\t\t\t::= \u003coperator\u003e SORT \u003cvariable\u003e\n\n\u003cselect\u003e\t\t\t::= SELECT \u003carray\u003e \u003coperator\u003e\n```\n\n## How can you extend upon it?\n\nThere are two important ways in which you can contribute to or extend this component:\n - implement IBlockChain for some other blockchain provider (e.g. HyperLedger)\n - implement another signing/hashing algorithm combination (current default is RSA / SHA-256)\n\n## Conclusion\n\nLearn more at [itextpdf.com/blockchain](https://itextpdf.com/blockchain).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fitext%2Fpdfchain","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fitext%2Fpdfchain","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fitext%2Fpdfchain/lists"}