This gem is an implementation of the JSON-LD Signatures specification in Ruby that supports the following encryption options:
- RSA
- Ed25519
See an example of the gem in action. The source code for the demo is here.
Add the gem to your Gemfile:
gem 'ruby-jsonld-signatures'
then run bundle install
Clone this repo, bundle and run the rspec tests:
% git clone https://github.com/johncallahan/ruby-jsonld-signatures.git
% bundle install
% rspec
You can experiment with the gem on a command line:
% irb -I lib -r 'json/ld/signature' -r 'ed25519'
> signer = JSON::LD::SIGNATURE::Ed25519Signer.new
> signer.pub = Ed25519::VerifyKey.new ["ff1a646cc8b69fcb522aa1ed162bc2816878252a634384ce46f7507bfc92f68f"].pack('H*')
> signer.priv = Ed25519::SigningKey.new ["7f702a609f842057be24b5297e451662876f03b047d660362cd123f71d2a3b63"].pack('H*')
> file = File.read('data/testdoc.jsonld')
> signed = signer.sign file, { 'creator' => 'did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk#authn-key-1'}
> CTL-D
You can bump the VERSION, update the ruby-jsonld-signatures.gemspec file, commit changes, and then
% gem build ruby-jsonld-signatures.gemspec
% gem push ruby-jsonld-signatures-0.0.xx.gem
for the gem file produced as a result of the build (this generated file is NOT add or committed to git).
Consider the following JSON-LD document:
{
"@context": [
"http://schema.org/",
"https://w3id.org/security/v2"
],
"name": "Manu Sporny",
"homepage": "https://manu.sporny.org/",
"image": "https://manu.sporny.org/images/manu.png"
}
The goal of Linked Data Signatures is to cryptographically "sign" a JSON-LD document such that the the order of key/pairs within the JSON-LD document does not matter. In other words, the signature value of the JSON content above would be:
qMt05czF5xKG+HwLrlqp+ja+8qtXDMINfY/eWKFIXYy0eqIj8S32OY0cYQHZqfZbTpA4v9J6vq+FaG5oSLvACg==
The following document is equivalent to the JSON-LD document above even though more whitespace is added and the key/value pairs (even in embedded blocks) are in different order but their values are equal:
{
"image": "https://manu.sporny.org/images/manu.png",
"name": "Manu Sporny",
"@context": [
"http://schema.org/",
"https://w3id.org/security/v2"
],
"homepage": "https://manu.sporny.org/"
}
After generating the signature value for the JSON-LD document, the signature value is appended to the document with additional metadata:
{
"@context":[
"http://schema.org/","https://w3id.org/security/v2"
],
"name":"Manu Sporny",
"homepage":"https://manu.sporny.org/",
"image":"https://manu.sporny.org/images/manu.png",
"signature":{
"type":"Ed25519Signature2018",
"creator":"did:v1:test:nym:JApJf12r82Pe6PBJ3gJAAwo8F7uDnae6B4ab9EFQ7XXk#authn-key-1",
"created":"2018-03-15T00:00:00Z",
"signatureValue":"qMt05czF5xKG+HwLrlqp+ja+8qtXDMINfY/eWKFIXYy0eqIj8S32OY0cYQHZqfZbTpA4v9J6vq+FaG5oSLvACg=="
}
}
This signed content can be presented to other parties such that any key/value pair change to the JSON content (not the order or whitespace) can be detected. It is useful in DID Auth where a user (via their browser or mobile device) holds a private key and needs to provide verifiable credentials to a replying party or service provider. In the case of DID Auth, the relying party can verify the signature by resolving the DID (via a universal resolver) to obtain the public key from a blockchain (Veres One in this case).
The process of signing a JSON-LD document includes:
- resolving the context vocabularies (i.e., fetching them via their URLs in the @context]
- normalizing (or sometimes called 'canonicalizing') the document
- determining the signature value with a private key (using RSA or Ed25519)
- embedding the signature JSON with the metadata and signature value (not part of the JSON-LD document)
Verifying a signed JSON-LD document includes:
- extracting the signature block from the JSON-LD document (remove it as well)
- normalizing (or sometimes called 'canonicalizing') the remaining JSON-LD document
- verifying the signature value with the public key (using RSA or Ed25519)
The ruby-jsonld-signatures gem relies on other gems to perform signing and verifying including:
NOTE: additional keys can be caught in generic schema.org and will still be part of the normalization process. Thus, the following JSON-LD document is equalivalent to the blocks shown above:
{
"@context": [
"http://schema.org/",
"https://w3id.org/security/v2"
],
"name": "Manu Sporny",
"foo": "bar",
"homepage": "https://manu.sporny.org/",
"image": "https://manu.sporny.org/images/manu.png"
}
The key "foo" will be put in the scheme.org context and included in the normalized content.
- Sign a basic string
- Sign a basic JSON-LD document
- Signatures of a second document that contains a non-vocabulary element are equivalent
- Signatures of a second document that contains a vocabulary element are NOT equivalent
- Signatures of a second document in different order are equivalent
- Verify signature of a signed JSON-LD document
- Detect when signature of a signed JSON-LD document is invalid