2

I am currently using the jsonwebtokens package for generating signed JWTs with a RSA signing key. I was experimenting to see how I can move to Azure Key Vault. One way I can do is to store the private and public keys as Vault Secrets. But I noticed that there's also a Vault Keys API which generates a key in Vault and signs data which I provide it.

I've been trying with the @azure/keyvault-keys package and this is where I've got:

async signJsonWebToken(data: object, expiry: string): Promise<string> {
  const headerB64 = this.base64url(JSON.stringify(this.keyHeader), 'binary');
  const payloadB64 = this.base64url(this.getTokenData(data, expiry), 'utf8');
  const payload = `${headerB64}.${payloadB64}`;

  const key = await this.keyClient.getKey(this.KEY_NAME);
  const cryptClient = new CryptographyClient(key, new DefaultAzureCredential());

  const hash = crypto.createHash('sha256');
  const digest = hash.update(payload).digest();

  const signResult = await cryptClient.sign('RS256', digest);
  const signResultB64 = this.base64url(signResult.result.toString(), 'utf8');
  const result = `${payload}.${signResultB64}`;
  this.logger.log('Key: ' + key.key);
  this.logger.log('Sign result: ' + result);
  return result;
}

private base64url(data: string, encoding: string) {
  return SBuffer
    .from(data, encoding)
    .toString('base64')
    .replace(/=/g, '')
    .replace(/\+/g, '-')
    .replace(/\//g, '_');
}

private getTokenData(data: object, expiry: string): string {
  const now = Date.now();
  const expiresIn = new Date();
  if (expiry.endsWith('d')) {
    expiresIn.setDate(expiresIn.getDate() + parseInt(expiry));
  } else if (expiry.endsWith('h')) {
    expiresIn.setHours(expiresIn.getHours() + parseInt(expiry));
  } else if (expiry.endsWith('m')) {
    expiresIn.setMinutes(expiresIn.getMinutes() + parseInt(expiry));
  }
  const tokenData = Object.assign({
    iat: now,
    exp: expiresIn.getTime()
  }, data);
  return JSON.stringify(tokenData);
}

The generated signed signature do not look anywhere close to what I'd usually be getting with the jsonwebtoken package. My intention is that I'd like to sign my tokens with Vault but would like to verify with jsonwebtoken.verify(). Is this even possible? What am I doing wrong in my code?

Rajshri Mohan K S
  • 1,557
  • 17
  • 30

1 Answers1

4

Since you use the Azure kay vault to sign jwt, we also can use Azure key vault to Verify the jwt

For example

 const key = await this.keyClient.getKey(this.KEY_NAME);
 const cryptClient = new CryptographyClient(key, new DefaultAzureCredential());
const util =require('util')
const base64 = require('base64url');
const JWT=""
    const jwtHeader = JWT.split('.')[0];
    const jwtPayload = JWT.split('.')[1];
    const jwtSignature = JWT.split('.')[2];
    const signature = base64.toBuffer(jwtSignature)
    const data = util.format('%s.%s', jwtHeader, jwtPayload);
    const hash = crypto.createHash('sha256');
    const digest = hash.update(data).digest()
const verified =await cryptClient.verify("RS256",digest,signature)

Besides If you want to use jsonwebtoken package to verify jet, please refer to the following code

const util =require('util')
const base64 = require('base64url');
const forge = require('node-forge');
const jwt = require('jsonwebtoken')
async Function test{
// gerenrate jwt
const headerObj = {
        alg: 'RS256',
        typ: 'JWT'
    };
    
    const payloadObj = {
        sub: '1234567890',
        name: 'John Doe'
       
    };
    
    const encodedHeader = base64(JSON.stringify(headerObj));
    const encodedPayload = base64(JSON.stringify(payloadObj));
    const data = util.format('%s.%s', encodedHeader, encodedPayload);

    const hash = crypto.createHash('sha256');
    const digest = hash.update(data).digest()
   
    
    const keyClient =new KeyClient("https://testsql.vault.azure.net/",new DefaultAzureCredential());
    const key =await keyClient.getKey("test");
    const cryptClient = new CryptographyClient(key.id, new DefaultAzureCredential());
    const signResult = await cryptClient.sign("RS256",digest)     
    const jwts =util.format('%s.%s.%s', encodedHeader, encodedPayload,base64(signResult.result));
    console.log(jwts)

// verify

// convert azure key vault ket to public key
var n =Buffer.from(key.key.n).toString("base64")   
    var e =Buffer.from(key.key.e).toString("base64")
    var publicKey = forge.pki.setRsaPublicKey(
        base64urlToBigInteger(n),
        base64urlToBigInteger(e)); 
// convert public key to pem file
     var pem = forge.pki.publicKeyToPem(publicKey);
    var d= jwt.decode(jwts,pem.toString())
    console.log(d)
}




function base64urlToBigInteger(str) {
   
    var bytes = forge.util.decode64(
      (str + '==='.slice((str.length + 3) % 4))
        .replace(/\-/g, '+')
        .replace(/_/g, '/'));
    return new forge.jsbn.BigInteger(forge.util.bytesToHex(bytes), 16);
  };

Jim Xu
  • 21,610
  • 2
  • 19
  • 39
  • This is the direction where I was heading to, I guess. Thanks for the pointers. I'll try this when I head back home and update the status. :) – Rajshri Mohan K S Jul 22 '20 at 13:37