Signature Validation

The Identity Profile Report JSON contains a proof object that can be used to verify the integrity of the data. To keep the JSON structure in human-readable form, we use a variation of JSON Web Signature (JWS) with detached payload format. This means that instead of carrying the JSON payload in the JWS, it can be attached to the payload as the proof.jws field value, while keeping the payload as it was signed.

Below is the proof object structure:

"proof": {
"jws": "<encoded_signature>",
"public_key_url": "<url_for_public_key>"
}

Verification process

Verifying the signature and data integrity can be achieved by the following:

  1. Retain the proof.jws value from the Identity Profile Report.

  2. Change the proof.jws value to an empty string (““) in the Report JSON.

  3. Canonicalise the report according to RFC 8785.

  4. Verify the signature against the key that’s obtained by resolving proof.public_key_url .

Example code

const fs = require('fs');
const axios = require('axios');
const jose = require('jose');
const { canonicalize } = require('json-canonicalize');

// Load the JSON data from the file
const report = JSON.parse(fs.readFileSync('identity_profile_report.json', 'utf8'));

// Extract the proof.jws value and the proof.public_key_url
const jwsToken = report.proof.jws;
const publicKeyUrl = report.proof.public_key_url;

// Set the proof.jws value to an empty string
report.proof.jws = "";

// Canonicalize the JSON data according to RFC 8785
const canonicalizedReport = canonicalize(report);

// Base64url encode the canonicalized JSON data
const encodedPayload = Buffer.from(canonicalizedReport, 'utf8').toString('base64url');
import json
import requests
from jwcrypto import jwk, jws
import rfc8785

# Load the JSON data from the file
with open('identity_profile_report.json', 'r') as file:
report = json.load(file)

# Extract the proof.jws value and the proof.public_key_url
jws_token = report['proof']['jws']
public_key_url = report['proof']['public_key_url']

# Set the proof.jws value to an empty string
report['proof']['jws'] = ""

# Canonicalize the JSON data according to RFC 8785
canonicalized_report = rfc8785.dumps(report)

# Fetch the public key from the proof.public_key_url
response = requests.get(public_key_url)
public_key_jwk_set = response.json()

# Extract the first key from the keys array
public_key_jwk = public_key_jwk_set['keys'][0]
package main

import (…)

func main() {…}
<?php
require 'vendor/autoload.php';

use Jose\Component\Core\JWK;
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Signature\JWSVerifier;
use Jose\Component\Signature\Serializer\CompactSerializer;
use Jose\Component\Signature\Algorithm\EdDSA;
use GuzzleHttp\Client;
use Root23\JsonCanonicalizer\JsonCanonicalizer;

// Load the JSON data from the file
$report = json_decode(file_get_contents('identity_profile_report.json'), true);

// Extract the proof.jws value and the proof.public_key_url
$jws_token = $report['proof']['jws'];
$public_key_url = $report['proof']['public_key_url'];

// Set the proof.jws value to an empty string
$report['proof']['jws'] = "";

// Canonicalize the JSON data according to RFC 8785
$canonicalizer = new JsonCanonicalizer();
package com.example;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.lang.JoseException;
import org.erdtman.jcs.JsonCanonicalizer;

import java.io.File;
import java.io.IOException;

public class SignatureVerification {
public static void main(String[] args) {
try {
// Load the JSON data from the file
ObjectMapper mapper = new ObjectMapper();
JsonNode report = mapper.readTree(new File("identity_profile_report.json"));

// Extract the proof.jws value and the proof.public_key_url
using System.Text;
using Newtonsoft.Json.Linq;
using Org.Webpki.JsonCanonicalizer;
using NSec.Cryptography;

class Program
{
static void Main()
{
string jsonReport = File.ReadAllText("payload/identity_profile_report.json");
JObject report = JObject.Parse(jsonReport);

string jws = report["proof"]?["jws"]?.ToString() ?? throw new InvalidOperationException("JWS is missing");
string publicKeyUrl = report["proof"]?["public_key_url"]?.ToString() ?? throw new InvalidOperationException("Public key URL is missing");

report["proof"]!["jws"] = "";

string canonicalizedReport = new JsonCanonicalizer(report.ToString()).GetEncodedString();

string publicKey = FetchPublicKey(publicKeyUrl);

bool isValid = VerifySignature(publicKey, jws, canonicalizedReport);

Reference