JWS Java algorithm to Deno's jose (node.js)

83 views Asked by At

I have the following code for JWS verification in Java:

/**
 * Step 1 - Load signing keys via jwks file
 */
 
String JWKS_URL = "https://vf11gtostorage1.blob.core.windows.net/test-webhook-sign-keys/test-webhook-sign-keys.jwks";
 
// on first startup we need to download the signing keys
// NOTE: this file should be cached locally and not downloaded each time
Path jwksLocalPath = Path.of("./test-webhook-sign-keys.jwks");
if (!Files.exists(jwksLocalPath)) {
    try (InputStream in = new URL(JWKS_URL).openStream()) {
        Files.copy(in, jwksLocalPath, StandardCopyOption.REPLACE_EXISTING);
    }
}
 
// load keys from file
String jkws = Files.readString(jwksLocalPath);
JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(jkws);
 
/**
 * Step 2 - Convert webhook json body to canonicalized form
 */
 
String originalJsonBody = *JSON BODY FROM WEBHOOK*;
JsonCanonicalizer jsonCanonicalizer = new JsonCanonicalizer(originalJsonBody);
String canonicalizedJson = jsonCanonicalizer.getEncodedString();
 
/**
 * Step 3 and 4- Select matching key for x-vfi-jws and validate signature
 */
 
JsonWebSignature verifierJws = new JsonWebSignature();
 
// set contents from x-vfi-jws header
verifierJws.setCompactSerialization(detachedContentJws);
 
// The canonicalized content is the payload
verifierJws.setPayload(canonicalizedJson);
 
// Pick the key to use for checking the signature. The key selected
// should have the same key id "kid" as the x-vfi-jws header
VerificationJwkSelector jwkSelector = new VerificationJwkSelector();
JsonWebKey jwkSelected = jwkSelector.select(verifierJws, jsonWebKeySet.getJsonWebKeys());
 
if (jwkSelected == null) {
    // if key can't be found then refresh signing keys as
    // a new key id may have been added
    try (InputStream in = new URL(JWKS_URL).openStream()) {
        Files.copy(in, jwksLocalPath, StandardCopyOption.REPLACE_EXISTING);
    }
    jkws = Files.readString(jwksLocalPath);
    jsonWebKeySet = new JsonWebKeySet(jkws);
    jwkSelected = jwkSelector.select(verifierJws, jsonWebKeySet.getJsonWebKeys());
}
 
// set key based on keyid selected from jwks
verifierJws.setKey(jwkSelected.getKey());
 
// Check the signature
boolean signatureVerified = verifierJws.verifySignature();

I need to transform this code to Deno's jose library https://deno.land/x/[email protected] .

For my understanding, I need to create a new JWS by setting the header from the request's headers, the payload from the request's body an the key from the header that matches the kid key of the file that I am loading and then verify it.

I just don't understand how to convert this Java code to js using jose.

Edit: I tried to convert by myself and not sure I used the right way to verify the jws. I used the function compactVerify and not jwtVerify. Any toughts?

import axiod from "axiod";
import * as mod from "https://deno.land/[email protected]/fs/mod.ts";
import * as jose from 'https://deno.land/x/[email protected]/index.ts';
import { canonicalize } from "https://deno.land/x/json_hash/canon.ts";

const jwksLocalPath = "./test-webhook-sign-keys.jwks";

const getCachedJwks = async (reload?: boolean) => {
    const JWKS_URL = Deno.env.get('JWKS_URL') as string;

    if (!await mod.exists(jwksLocalPath) || reload) {
        const response = await axiod.get(JWKS_URL, { responseType: 'text' });
        
        await Deno.writeFile(jwksLocalPath, response.data);
    }

    // Load keys from file
    const jkws = await Deno.readFile(jwksLocalPath);
    
    return JSON.parse(new TextDecoder('utf-8').decode(jkws)) as jose.JSONWebKeySet;
}

export const verifyVerifone = async (req: Request) => {
    /**
     * Step 1 - Load signing keys via jwks file
     * On first startup, we need to download the signing keys
     * this file should be cached locally and not downloaded each time
     */
    let jsonWebKeySet = await getCachedJwks();

    /**
     * Step 2 - Convert webhook json body to canonicalized form
     */
    const originalJsonBody = await req.json(); // Replace this with the actual JSON body
    const canonicalizedJson = canonicalize(originalJsonBody);
    
    /**
     * Step 3 and 4 - Select matching key for x-vfi-jws and validate signature
     * In order to verify the signature, we need to find the matching key in the jwks file
     */
    const headerKey = req.headers.get('x-vfi-jws');

    if (!headerKey) {
        throw new Error('x-vfi-jws header is missing');
    }

    const detachedContentJws = JSON.parse(headerKey) as jose.JWK;

    // Pick the key to use for checking the signature. The key selected
    // should have the same key id "kid" as the x-vfi-jws header
    let jwkSelected = jsonWebKeySet.keys.find(key => key.kid === detachedContentJws.kid);

    if (!jwkSelected) {
        jsonWebKeySet = await getCachedJwks(true);

        jwkSelected = jsonWebKeySet.keys.find(key => key.kid === detachedContentJws.kid);
    }

    const verifierJws = await new jose.CompactSign(new TextEncoder().encode(canonicalizedJson))
        .sign(new TextEncoder().encode(JSON.stringify(detachedContentJws)))

    await jose.compactVerify(verifierJws, new TextEncoder().encode(JSON.stringify(jwkSelected)));
}
0

There are 0 answers