I am trying to create JWT Signature in NodeJS with ES256 algorithm using AWS KMS Customer Managed Keys.
The signature created using AWS KMS with cryptographic Signing Algorithms ECDSA_SHA_256 is not JWT accepted R || S format. As per AWS doc, Signature will be in DER encoded ANS X9.62–2005 format (https://docs.aws.amazon.com/kms/latest/APIReference/API_Sign.html#API_Sign_ResponseSyntax).
I tried to convert the AWS KMS Sign to JWT R||S format using below code in NodeJS with ans1js(https://www.npmjs.com/package/asn1js), But the R and S length is not consistent to be 32 + 32 rather it varies 33 most of the time.
function toArrayBuffer(buffer) { const ab = new ArrayBuffer(buffer.length); const view = new Uint8Array(ab); for (let i = 0; i < buffer.length; ++i) { view[i] = buffer[i]; } return ab; } //call this with your signature buffer function parseBERSignature(sig) { const { result } = asn1js.fromBER(toArrayBuffer(sig)); const part1 = result.valueBlock.value[0]; const part2 = result.valueBlock.value[1]; let r = Buffer.from(part1.valueBlock.valueHex); let s = Buffer.from(part2.valueBlock.valueHex); console.log("R value", r); console.log("S value", s); console.log("R value", r.toString('base64')); console.log("S value", s.toString('base64')); console.log("R length", r.length); console.log("S length", s.length); return base64url.fromBase64(Buffer.concat([r, s]).toString('base64')); }
Complete Code for Signature creation:
const base64url = require('base64url') const AWS = require('aws-sdk'); const kms = new AWS.KMS(); const asn1js = require('asn1js') const keyid = "9001e08c-b7bc-4f53-9eca-ec034904cdd5"; const header = { "typ": "JWT", "alg": "ES256", "kid": keyid } const payload = { "sub": "name", "status": "valid", "aud": "name" } exports.handler = async function (event, context, callback) { console.log("Hello, new World"); payload.iat = Math.floor(Date.now() / 1000); console.log("header", header); console.log("payload", payload); const jwtHeader = base64url(JSON.stringify(header)); const jwtPayload = base64url(JSON.stringify(payload)); console.log("jwtHeader", jwtHeader); console.log("jwtPayload", jwtPayload); const message = Buffer.from(jwtHeader + "." + jwtPayload); const messageDigest = createHash(message); let kmsResponse = await kms.sign({ Message: message, KeyId: keyid, SigningAlgorithm: 'ECDSA_SHA_256', MessageType: 'RAW' }).promise(); console.log("Signature RAW", kmsResponse.Signature); console.log("Signature String", kmsResponse.Signature.toString()); console.log("Signature base64", kmsResponse.Signature.toString('base64')); let response = parseBERSignature(kmsResponse.Signature); console.log("response", response); return jwtHeader + "." + jwtPayload + "." + response; }
Any NodeJs Javascript implementation to convert the DER encoded ANS format to R || S format base64url encoded?
Advertisement
Answer
Any NodeJs Javascript implementation to convert the DER encoded ANS format to R || S format base64url encoded?
Yes, ecdsa-sig-formatter will help you convert signatures back and forth between the formats.