Creating a certificate for document with an IPFS hash


I’m totally new to blockcerts, so I’m really still thinking about how I could use it

I have a use case I’d like to achieve and I wonder if the community could advise me if this is possible with block certs.

Basically, I am publishing documents on the distributed IPFS network. Each document has an unique IPFS hash.

Then I want the academic societies I’m working with to be able to publish peer reviews on the same distributed network. These “reviews” would also be available on the IPFS system and addressable via a unique hash. I think of these “reviews” as the certificates. They would contain basic review information, like who reviewed, when they reviewed, what rubric they used in their review, etc.

What might be unique, however, is that I want these certificates to be issued, not to a person, but to the document they are approving.

So the “issuer” of the certificate would be the academic society, represented by their public key, the certificate would the hash of the review (referenced via its IPFS hash) and the recipient of the certificate would be the document addressed by its document hash (instead of the public key of a person).

Since, I’m using IPFS there is no need to store any of the review or document information on the block chain. We only need the hash, the content can retrieved from the ipfs network.

So I would imagine a “transaction” looking like the following.

Certificate: IPFS hash of review
Recipient: IPFS hash of document reviewed

Do you think this kind of transaction is something blockcerts could be used for, or is this wacky or just off base.

I know it might be unusual for the recipient of a certificate to be a document rather than a person, but it seem like it should still work, since the document has a hash just like a person has a public key.

Like I said, I’ve just started investigating Blockcert, so any references, pointers, would be much appreciated.



I’m also working with IPFS and Blockcerts (for my M.Sc Thesis) and I’ll try to offer some clarification on this topic.

One of the cool things about Blockcerts is that they are verifiable. You can learn more about the verification process here but essentially it checks the Blockcert’s integrity (that’s why we need blockchain), revocation status, authenticity and Issuer identity.

Verifier: For the use case you mentioned, what I believe would make sense would be for you to extend the verifier application to verify aspects that are specific to your use case (for instance, if the document that someone claims to be a peer review of a research paper actually concerns that research paper), in addition to the verification process that already takes place by default.

Blockchain Transaction: Blockcert’s blockchain transactions (that I believe currently only take place on Bitcoin) only have the Merkle Root of the certificates in that batch.
Blockcerts issuing process is as follows:

  1. Generate the Blockcert and sign it
  2. Generate a hash of the signed certificates
  3. Take the hashes of other signed certificates and generate a Merkle Tree
  4. Take the Merkle Tree’s root and put it on a Bitcoin transaction
  5. The certificates on that batch have now been issued. You can use the transaction id, merkle root and certificate hash to verify the integrity of every certificate in that batch.

All that to say that you should put the IPFS multihash of the document being reviewed on the Blockcert. Afterwards, you could take the signed certificate and add it to IPFS, then use the multihash to share the certificate through whatever channel you so desire. Since it has been issued on the blockchain, anyone can verify it using the Verifier.

Receiver Public Key: I’m not entirely sure on this one, so I’ll ask @kim to check this, but I guess the Issuer could use its own key, becoming the issuer and the receiver of the certificate.

I hope this helped :slight_smile:


Hi @jeffreycwitt,
I’m sorry for the delay. Thanks for the question and thanks @joaosantos for the response! I have a couple more thoughts to add.

One thing I was unsure about is if you wanted the transaction itself stored on a blockchain. If so, then the only work required would be modifying the structure of the certificate.

In this case, the flow would be:

  • Create certificate with review of document in IPFS
    • Your certificate contents will be slightly different than usual; the changes from the typical structure are described below
  • Issue the certificate(s) as usual with cert-issuer
    • This step adds the transaction details to the certificate (it contains the transaction id, etc)
    • We’ll refer to the resulting issued certificate as the “Blockcert”
  • Then you can store the Blockcert in IPFS. Anyone with that address can look up the content, from which they can see the Blockcert content, the IPFS address of the document being reviewed, and the blockchain transaction record.

Let me know if this sounds like what you’re trying to do (or if not). Assuming so, here are the details:

Certificate Structure Modifications

The Blockcerts open source includes a repo called cert-tool to help generate the certificates that will ultimately be issued on a blockchain. You would fork/modify this to generate certificates that look like what you need.

On the recipient side, you can simply omit the public key. In fact, I would omit the entire recipientProfile. The Blockcerts schema allows this.

The full list of changes are:


Refer to our Blockcert example in the cert-schema repo, specifically, this part:

  "recipient": {
    "hashed": false,
    "identity": "",
    "type": "email"
  "recipientProfile": {
    "type": [
    "publicKey": "ecdsa-koblitz-pubkey:mtr98kany9G1XYNU74pRnfBQmaCg2FZLmc",
    "name": "Eularia Landroth"

Instead of this, you would have:

  "recipient": {
    "hashed": false,
    "identity": <IPFS address of document being reviewed>,
    "type": "url"

Related info

We are working with the Open Badges community make Open Badges compatible with Verifiable Claims, a structure that might ultimately make more sense for your scenario. For example, this would allow you to identify the “recipient” via the JSON-LD @id, which would point to your IPFS address. This would also allow you to drop the identity field. But until then, the steps above will generate an Open Badges / Blockcerts compatible document. To future proof your work (to allow for changes like this), you can version your schemas.

Thanks again, and let us know if you have questions.


@kim, thanks so much. This is super helpful. Let me dig into this for a while and I’ll get back to you when I’ve made some progress (or hit a wall :slight_smile: )


Ok, I’ve had a little time to dig into things. Here’s a report on my progress with some questions.

I’m modified my review system so that when a user creates a review for a document, a certificate is also created.

Here’s the result of creating a test review:

(NOTE: discourse is only letting me embed two links because I’m new, so subsequent links are prefaced with "link

At the time of creating a review, the document being reviewed is pinned to the ipfs network and given an ipfs hash. (For example: link

This hash is then sent to my custom certificate creator, which uses the ipfs hash the identify of the “recipient”.

This unsigned-certificate is likewise pinned to the ipfs network and its hash is recorded.
(For example:

In creating the certificate, I think I’ve tried to follow the basic schema for making an assertions. I used one of the example unsigned certificates in the cert-issue examples folder (link

As an aside I tried running both this example certificate and my own certificate in the OpenBadges validator (link and there were various errors. I’m not yet sure if the blockcert certificates are supposed to pass OpenBadges validation or not.

With my unsigned certificate created, I then started testing verifying the certificate on a test blockchain with the cert-issue docker set up.

This basically worked.

Note that cert-issue rejected the certificate until I changed the recipient[“type”] to “email” instead of “url” or “string”. I’m not sure if I’m missing something there or the schema needs to be adjusted to allow a document to be a recipient.

But once I change recipient[“type”] to email, I was successfully able to sign the certificate.

Once, the certificate has been signed, I suppose I’ll need to add that to IPFS as well and the update the registry record with the hash of the signed certificate rather than the hash of the unsigned certificate. (Perhaps I’ll preserve both.)

This leads me to a few questions.

  1. Once I have the signed certificate, I have successfully been able to find the transaction using the

"sourceId": "79925bf920bfa56b428384de7a774e22e70f66a244cae2faee24f2a485f8bdc9"


bitcoin-cli gettransaction 79925bf920bfa56b428384de7a774e22e70f66a244cae2faee24f2a485f8bdc9

But I’m not able to find a new block that contains this transaction.

In fact, if I run bitcoin-cli getblockcount I continue to get 101. No matter how many times I run cert-issuer -c /etc/cert-issuer/conf.ini the count is still 101, which makes me think that no new blocks are being created. Should I expect a block to be created after running cert-issuer -c /etc/cert-issuer/conf.ini? Or am I missing something.

In any case, when the block is created, should I be able to easily move from the transaction id or the merkleRoot to the corresponding block or at least discover the hash of the block containing this merkleRoot or transactionid? What bitcoin-cli command would allow me to do that?

  1. Second, even if I succeed in signing the certificate and verifying it on blockchain, I still feel like I’m going to need some sort centralized index that allows a user to discover if a document has a certificate.

Certainly if a user begins with the signed certificate, they’ll easily be able to find the document that has been approved (through the recipient field). Likewise, they’ll be able to use signature to find the verification on the block chain.

This is I think is exactly what @kim describes

However, in general I expect the dominant use case to be the other way around. Namely, a user or client application will begin with a document or url to a document, and they will want to know are their any reviews/certificates for this precise document.

While the blockchain will be able to confirm that the certificate has been issued for a precise document, it won’t be able to answer the question, are there any certificates for this document?

Thus my feeling is I’m still going to need a kind of registry (as I have running now in production link that connects documents with certificates. This service allows a user to supply the ipfs hash or shasum of the document and the learn if there are reviews/certificates for this document.

Once they have the ipfs hash of the associated reviews that can then use the certificate and block chain for any further verification. But I think I still need a services that helps people discover these certificates.

FYI, here’s a link to a little more context of what I’m trying to do: This is a related discussion on the ipfs discussion board.

I’m be really grateful for any thoughts or feedback. I’d love to hear if you think i’m on the right track or I missing something obvious.


Quick follow up. I was thinking that in terms of long term sustainability it’s going to be much easier for me to use a cert-issuer service rather than maintaining my own.

I would imagine creating the unsigned certificate and sending that certificate (or certificates) to a services verifies this on the block chain and then sends me back and signed certificate.

I would then pin this signed certificate to ipfs (as described above).

Are there currently any sites that provide this service (aka, receiving unsigned certificates and returning signed certificates)?


Nice discussion and understaning.