KMS Generated Signature Is Too Large

Viewed 88

I am trying to use AWS KMS to generated a signature for a JWT but am getting back a signature that appears to be too large for the algorithm I've chosen. I am using Node.js and the AWS KMS Client to generate the signature.

const header64 = base64url.encode(JSON.stringify(header));
const payload64 = base64url.encode(JSON.stringify(payload));

const message = Buffer.from(`${header64}.${payload64}`);

const command = new SignCommand({
    KeyId: 'mrk-1234567890',
    Message: message,
    SigningAlgorithm: SigningAlgorithmSpec.ECDSA_SHA_256
});

const client = new KMSClient({});
const response = await client.send(command);

const signature64 = base64url.encode(Buffer.from(response.Signature), 'base64');

const jwt = `${header64}.${payload64}.${signature64}`;

When I think try and verify this JWT I get an error message saying

"ES256" signatures must be "64" bytes, saw "71"

This number of 71 bytes matches the size of the Uint8Array contained in the response.Signature. The verification is being done using the generated public key and the jsonwebtoken package.

Am I missing something glaringly obvious here?

1 Answers

This ended up being a misunderstanding of what was being returned by the AWS SDK. According to the docs:

When used with the ECDSA_SHA_256, ECDSA_SHA_384, or ECDSA_SHA_512 signing algorithms, this value is a DER-encoded object as defined by ANS X9.62–2005 and RFC 3279 Section 2.2.3.

What this means is that I needed to perform one final step on the returned signature before adding it to my JWT. Using the ecdsa-sig-formatter package my final code takes the following shape:

const header64 = base64url.encode(JSON.stringify(header));
const payload64 = base64url.encode(JSON.stringify(payload));

const message = Buffer.from(`${header64}.${payload64}`);

const command = new SignCommand({
    KeyId: 'mrk-1234567890',
    Message: message,
    SigningAlgorithm: SigningAlgorithmSpec.ECDSA_SHA_256
});

const client = new KMSClient({});
const response = await client.send(command);

const signature64 = base64url.encode(Buffer.from(response.Signature), 'base64');
const signatureJose = derToJose(signature64, 'ES256');

const jwt = `${header64}.${payload64}.${signatureJose}`;
Related