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');
// Decode the JWS token
const [protectedHeader, , signature] = jwsToken.split('.');
// Fetch the public key from the proof.public_key_url
axios.get(publicKeyUrl)
.then(async response => {
const publicKeyJwk = response.data.keys[0];
// Import the public key using jose.importJWK
const key = await jose.importJWK(publicKeyJwk, 'EdDSA');
// Construct the JWS object
const jws = {
protected: protectedHeader,
payload: encodedPayload,
signature: signature
};
// Verify the JWS signature using the flattenedVerify method
try {
await jose.flattenedVerify(jws, key, {
algorithms: ['EdDSA']
});
console.log("Signature is valid.");
} catch (err) {
console.error("Signature verification failed:", err);
}
})
.catch(err => {
console.error("Failed to fetch public key:", err);
});
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]
# Verify the JWS signature using the detached payload method
key = jwk.JWK(**public_key_jwk)
jws_token_obj = jws.JWS()
jws_token_obj.deserialize(jws_token)
try:
jws_token_obj.verify(key, detached_payload=canonicalized_report)
print("Signature is valid.")
except Exception as e:
print(f"Signature verification failed: {e}")
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"github.com/go-jose/go-jose/v4"
"github.com/gowebpki/jcs"
)
func main() {
jsonFile := "identity_profile_report.json"
b, err := os.ReadFile(jsonFile)
if err != nil {
fmt.Printf("Failed to read input report file (%v): %v\n", jsonFile, err)
os.Exit(1)
}
// Unmarshal the Identity Profile Report into a map.
var report map[string]interface{}
err = json.Unmarshal(b, &report)
if err != nil {
fmt.Printf("Failed to unmarshal JSON: %v\n", err)
os.Exit(1)
}
// Capture the original jws proof value.
proof, ok := report["proof"].(map[string]interface{})
if !ok {
fmt.Printf("Failed to get proof from report\n")
os.Exit(1)
}
jws, ok := proof["jws"].(string)
if !ok {
fmt.Printf("Failed to get jws from proof\n")
os.Exit(1)
}
// Change the jws value in the original report to an empty string.
proof["jws"] = ""
// Marshal the modified report back to JSON.
modifiedJson, err := json.Marshal(report)
if err != nil {
fmt.Printf("Failed to marshal modified report: %v\n", err)
os.Exit(1)
}
// Canonicalize the JSON.
canonicalJson, err := jcs.Transform(modifiedJson)
if err != nil {
fmt.Printf("Failed to canonicalize the JSON: %v\n", err)
os.Exit(1)
}
// Verify the original signature against the canonicalized payload and the public key.
js, err := jose.ParseDetached(jws, canonicalJson, []jose.SignatureAlgorithm{jose.EdDSA})
if err != nil {
fmt.Printf("Failed to parse detached signature: %v\n", err)
os.Exit(1)
}
keyID := js.Signatures[0].Header.KeyID
pk, err := getPublicKey(proof["public_key_url"].(string), keyID)
if err != nil {
fmt.Printf("Failed to get the public key: %v\n", err)
os.Exit(1)
}
_, err = js.Verify(pk)
if err != nil {
fmt.Printf("Signature verification failed: %v\n", err)
os.Exit(1)
}
fmt.Printf("Signature is valid.\n")
}
func getPublicKey(keyUrl, keyID string) (*jose.JSONWebKey, error) {
resp, err := http.Get(keyUrl)
if err != nil {
return nil, fmt.Errorf("failed to GET key: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, fmt.Errorf("unexpected HTTP code %d", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %v", err)
}
var keySet jose.JSONWebKeySet
err = json.Unmarshal(body, &keySet)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal (%T): %v", keySet, err)
}
for _, k := range keySet.Keys {
if k.KeyID == keyID {
return &k, nil
}
}
return nil, fmt.Errorf("key with keyID %q not found", keyID)
}
<?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();
$canonicalized_report = $canonicalizer->canonicalize($report);
// Fetch the public key from the proof.public_key_url
$client = new Client();
$response = $client->get($public_key_url);
$public_key_jwk_set = json_decode($response->getBody(), true);
// Extract the first key from the keys array
$public_key_jwk = $public_key_jwk_set['keys'][0];
// Verify the JWS signature using the detached payload method
$jwk = new JWK($public_key_jwk);
$algorithmManager = new AlgorithmManager([new EdDSA()]);
$jwsVerifier = new JWSVerifier($algorithmManager);
$serializer = new CompactSerializer();
$jws = $serializer->unserialize($jws_token);
try {
$isValid = $jwsVerifier->verifyWithKey($jws, $jwk, 0, $canonicalized_report);
if ($isValid) {
echo "Signature is valid.";
} else {
echo "Signature verification failed.";
}
} catch (Exception $e) {
echo "Signature verification failed: " . $e->getMessage();
}
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
String jwsToken = report.get("proof").get("jws").asText();
String publicKeyUrl = report.get("proof").get("public_key_url").asText();
// Set the proof.jws value to an empty string
((ObjectNode) report.get("proof")).put("jws", "");
// Canonicalize the JSON data according to RFC 8785
JsonCanonicalizer canonicalizer = new JsonCanonicalizer(mapper.writeValueAsString(report));
String canonicalizedReport = canonicalizer.getEncodedString();
// Fetch the public key from the proof.public_key_url
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(publicKeyUrl).build();
Response response = client.newCall(request).execute();
String publicKeyJson = response.body().string();
// Parse the JWK set and extract the first key
JsonNode keysNode = mapper.readTree(publicKeyJson).get("keys");
JsonNode publicKeyNode = keysNode.get(0);
JsonWebKey publicKeyJwk = JsonWebKey.Factory.newJwk(publicKeyNode.toString());
// Construct the JWS object with detached payload
JsonWebSignature jws = new JsonWebSignature();
jws.setAlgorithmHeaderValue("EdDSA");
jws.setKey(publicKeyJwk.getKey());
jws.setCompactSerialization(jwsToken);
jws.setPayload(canonicalizedReport);
// Verify the JWS signature
if (jws.verifySignature()) {
System.out.println("Signature is valid.");
} else {
System.out.println("Signature verification failed.");
}
} catch (IOException | JoseException e) {
e.printStackTrace();
}
}
}
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);
Console.WriteLine($"Signature is valid: {isValid}");
}
static string FetchPublicKey(string url)
{
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = client.GetAsync(url).Result;
response.EnsureSuccessStatusCode();
string responseBody = response.Content.ReadAsStringAsync().Result;
JObject keyData = JObject.Parse(responseBody);
return keyData["keys"]?[0]?["x"]?.ToString() ?? throw new InvalidOperationException("Public key is missing");
}
}
static bool VerifySignature(string publicKey, string jws, string payload)
{
string[] jwsParts = jws.Split('.');
if (jwsParts.Length != 3)
{
throw new ArgumentException("Invalid JWS format");
}
string header = jwsParts[0];
string signature = jwsParts[2];
byte[] signatureBytes = Base64UrlDecode(signature);
byte[] payloadBytes = Encoding.UTF8.GetBytes(payload);
var publicKeyBytes = Base64UrlDecode(publicKey);
var key = PublicKey.Import(SignatureAlgorithm.Ed25519, publicKeyBytes, KeyBlobFormat.RawPublicKey);
// Create the data to verify (header + "." + payload)
string dataToVerify = $"{header}.{Base64UrlEncode(payloadBytes)}";
byte[] dataToVerifyBytes = Encoding.UTF8.GetBytes(dataToVerify);
return SignatureAlgorithm.Ed25519.Verify(key, dataToVerifyBytes, signatureBytes);
}
static byte[] Base64UrlDecode(string input)
{
string base64 = input.Replace('-', '+').Replace('_', '/');
switch (base64.Length % 4)
{
case 2: base64 += "=="; break;
case 3: base64 += "="; break;
}
return Convert.FromBase64String(base64);
}
static string Base64UrlEncode(byte[] input)
{
string base64 = Convert.ToBase64String(input);
return base64.Replace('+', '-').Replace('/', '_').TrimEnd('=');
}
}