Skip to content

Instantly share code, notes, and snippets.

@Lohann
Created April 26, 2023 02:42
Show Gist options
  • Save Lohann/9a65b68890611cac436c1863a7d28e9d to your computer and use it in GitHub Desktop.
Save Lohann/9a65b68890611cac436c1863a7d28e9d to your computer and use it in GitHub Desktop.
const webcrypto = require('crypto').webcrypto.subtle;
const enc = new TextEncoder();
function bytesToBase64url(bytes) {
return Buffer.from(bytes).toString('base64url');
}
function jsonToBase64url(obj) {
const bytes = enc.encode(JSON.stringify(obj));
return bytesToBase64url(bytes);
}
/**
* Verifies the signature of a JWT.
*/
async function verifyJWT(jwt, publicKey) {
const [header, payload, signature] = jwt.split('.');
const body = header + '.' + payload;
// Import the public key from JWK format
const pubkey = await webcrypto.importKey(
'jwk',
publicKey,
{
name: "RSASSA-PKCS1-v1_5",
namedCurve: publicKey.crv,
hash: "SHA-256",
},
true,
publicKey.key_ops,
);
// Verify the signature
return await webcrypto.verify(
{
name: "RSASSA-PKCS1-v1_5",
hash: { name: "SHA-256" },
},
pubkey,
Buffer.from(signature, 'base64url'),
enc.encode(body),
);
}
/**
* Sign and return JWT
*/
async function signJWT(header, payload, jwkPrivateKey) {
const headerbase64 = jsonToBase64url(header);
const payloadbase64 = jsonToBase64url(payload);
const body = headerbase64 + '.' + payloadbase64;
// Import the public key from JWK format
const privateKey = await webcrypto.importKey(
'jwk',
jwkPrivateKey,
{
name: "RSASSA-PKCS1-v1_5",
namedCurve: jwkPrivateKey.crv,
hash: "SHA-256",
},
true,
jwkPrivateKey.key_ops,
);
// Sign JWT
const signature = await webcrypto.sign(
{
name: "RSASSA-PKCS1-v1_5",
hash: { name: "SHA-256" },
},
privateKey,
enc.encode(body),
);
const signatureBase64 = bytesToBase64url(signature);
const jwt = body + '.' + signatureBase64;
return jwt;
}
async function main() {
// Generate a new keypair
const keyPair = await webcrypto.generateKey(
{
name: "RSASSA-PKCS1-v1_5",
modulusLength: 4096,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256",
},
true,
["sign", "verify"]
);
// Export the public key to JWK format
const jwkPrivateKey = await webcrypto.exportKey('jwk', keyPair.privateKey);
const jwkPublicKey = await webcrypto.exportKey('jwk', keyPair.publicKey);
console.log('PrivateKey:')
console.log(JSON.stringify(jwkPrivateKey));
console.log('\nPublicKey:')
console.log(JSON.stringify(jwkPublicKey));
// JWT Header and Payload
const header = {
"kid": "s16tqSm88pDJ8TfB_7kHKPRAPF85wUDTlmyo9ILTe7s",
"alg": "RS256"
};
const payload = {
"sub": "friendly-lynx@example.com",
"name": "Friendly Lynx",
"email": "friendly-lynx@example.com",
"iss": "https://pk-demo.okta.com/oauth2/default",
"aud": "LbSvMcaJ6iomfl7PcjSkmFSt",
"iat": 1682467047,
"exp": 1685059047,
"amr": [
"pwd"
]
};
// Sign JWT
const jwt = await signJWT(header, payload, jwkPrivateKey);
console.log('\nJWT:')
console.log(jwt);
// Verify signature
console.log('\nJWT Signature Verification:')
console.log(await verifyJWT(jwt, jwkPublicKey));
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment