[skip ci]
This commit is contained in:
Filip Skokan 2022-02-15 15:44:22 +01:00
parent 3ffb52b19e
commit 7bde9ff239
5 changed files with 77 additions and 17 deletions

View file

@ -12,6 +12,7 @@ The following specifications are implemented by `jose`
- JSON Web Algorithms (JWA) - [RFC7518][spec-jwa]
- JSON Web Token (JWT) - [RFC7519][spec-jwt]
- JSON Web Key Thumbprint - [RFC7638][spec-thumbprint]
- JSON Web Key Thumbprint URI - [RFCXXXX][spec-thumbprint-uri]
- JWS Unencoded Payload Option - [RFC7797][spec-b64]
- CFRG Elliptic Curve ECDH and Signatures - [RFC8037][spec-okp]
- secp256k1 EC Key curve support - [JOSE Registrations for WebAuthn Algorithms][spec-secp256k1]
@ -58,8 +59,9 @@ import * as jose from 'https://deno.land/x/jose/index.ts'
- Signing - [Compact](docs/classes/jws_compact_sign.CompactSign.md#readme), [Flattened](docs/classes/jws_flattened_sign.FlattenedSign.md#readme), [General](docs/classes/jws_general_sign.GeneralSign.md#readme)
- Verification - [Compact](docs/functions/jws_compact_verify.compactVerify.md#readme), [Flattened](docs/functions/jws_flattened_verify.flattenedVerify.md#readme), [General](docs/functions/jws_general_verify.generalVerify.md#readme)
- JSON Web Key (JWK)
- [Thumbprints](docs/functions/jwk_thumbprint.calculateJwkThumbprint.md#readme)
- [EmbeddedJWK](docs/functions/jwk_embedded.EmbeddedJWK.md#readme)
- [Calculating JWK Thumbprint](docs/functions/jwk_thumbprint.calculateJwkThumbprint.md#readme)
- [Calculating JWK Thumbprint URI](docs/functions/jwk_thumbprint.calculateJwkThumbprintUri.md#readme)
- [Verification using a JWK Embedded in a JWT Header](docs/functions/jwk_embedded.EmbeddedJWK.md#readme)
- JSON Web Key Set (JWKS)
- [Verify using a local JWKSet](docs/functions/jwks_local.createLocalJWKSet.md#readme)
- [Verify using a remote JWKSet](docs/functions/jwks_remote.createRemoteJWKSet.md#readme)
@ -146,6 +148,7 @@ install size should not be a cause for concern.
[spec-okp]: https://www.rfc-editor.org/rfc/rfc8037
[spec-secp256k1]: https://www.rfc-editor.org/rfc/rfc8812
[spec-thumbprint]: https://www.rfc-editor.org/rfc/rfc7638
[spec-thumbprint-uri]: https://www.rfc-editor.org/rfc/rfcXXXX
[support-sponsor]: https://github.com/sponsors/panva
[conditional-exports]: https://nodejs.org/api/packages.html#packages_conditional_exports
[webcrypto]: https://www.w3.org/TR/WebCryptoAPI/

View file

@ -41,8 +41,9 @@ import * as jose from 'https://deno.land/x/jose/index.ts'
- Signing - [Compact](classes/jws_compact_sign.CompactSign.md#readme), [Flattened](classes/jws_flattened_sign.FlattenedSign.md#readme), [General](classes/jws_general_sign.GeneralSign.md#readme)
- Verification - [Compact](functions/jws_compact_verify.compactVerify.md#readme), [Flattened](functions/jws_flattened_verify.flattenedVerify.md#readme), [General](functions/jws_general_verify.generalVerify.md#readme)
- JSON Web Key (JWK)
- [Thumbprints](functions/jwk_thumbprint.calculateJwkThumbprint.md#readme)
- [EmbeddedJWK](functions/jwk_embedded.EmbeddedJWK.md#readme)
- [Calculating JWK Thumbprint](functions/jwk_thumbprint.calculateJwkThumbprint.md#readme)
- [Calculating JWK Thumbprint URI](functions/jwk_thumbprint.calculateJwkThumbprintUri.md#readme)
- [Verification using a JWK Embedded in a JWT Header](functions/jwk_embedded.EmbeddedJWK.md#readme)
- JSON Web Key Set (JWKS)
- [Verify using a local JWKSet](functions/jwks_local.createLocalJWKSet.md#readme)
- [Verify using a remote JWKSet](functions/jwks_remote.createRemoteJWKSet.md#readme)

View file

@ -31,7 +31,7 @@ export type { Signature } from './jws/general/sign.js'
export { SignJWT } from './jwt/sign.js'
export { EncryptJWT } from './jwt/encrypt.js'
export { calculateJwkThumbprint } from './jwk/thumbprint.js'
export { calculateJwkThumbprint, calculateJwkThumbprintUri } from './jwk/thumbprint.js'
export { EmbeddedJWK } from './jwk/embedded.js'
export { createLocalJWKSet } from './jwks/local.js'

View file

@ -18,17 +18,18 @@ const check = (value: unknown, description: string) => {
*
* @param jwk JSON Web Key.
* @param digestAlgorithm Digest Algorithm to use for calculating the thumbprint.
* Default is sha256. Accepted is "sha256", "sha384", "sha512".
*
* @example Usage
* ```js
* const thumbprint = await jose.calculateJwkThumbprint({
* kty: 'RSA',
* e: 'AQAB',
* n: '12oBZRhCiZFJLcPg59LkZZ9mdhSMTKAQZYq32k_ti5SBB6jerkh-WzOMAO664r_qyLkqHUSp3u5SbXtseZEpN3XPWGKSxjsy-1JyEFTdLSYe6f9gfrmxkUF_7DTpq0gn6rntP05g2-wFW50YO7mosfdslfrTJYWHFhJALabAeYirYD7-9kqq9ebfFMF4sRRELbv9oi36As6Q9B3Qb5_C1rAzqfao_PCsf9EPsTZsVVVkA5qoIAr47lo1ipfiBPxUCCNSdvkmDTYgvvRm6ZoMjFbvOtgyts55fXKdMWv7I9HMD5HwE9uW839PWA514qhbcIsXEYSFMPMV6fnlsiZvQQ'
* kty: 'EC',
* crv: 'P-256',
* x: 'jJ6Flys3zK9jUhnOHf6G49Dyp5hah6CNP84-gY-n9eo',
* y: 'nhI6iD5eFXgBTLt_1p3aip-5VbZeMhxeFSpjfEAf7Ww'
* })
*
* console.log(thumbprint)
* // 'w9eYdC6_s_tLQ8lH6PUpc0mddazaqtPgeC2IgWDiqY8'
* ```
*/
export async function calculateJwkThumbprint(
@ -76,3 +77,31 @@ export async function calculateJwkThumbprint(
const data = encoder.encode(JSON.stringify(components))
return base64url(await digest(digestAlgorithm, data))
}
/**
* Calculates a JSON Web Key (JWK) Thumbprint URI as per
* [RFCXXXX](https://www.rfc-editor.org/rfc/rfcXXXX).
*
* @param jwk JSON Web Key.
* @param digestAlgorithm Digest Algorithm to use for calculating the thumbprint.
*
* @example Usage
* ```js
* const thumbprintUri = await jose.calculateJwkThumbprintUri({
* kty: 'EC',
* crv: 'P-256',
* x: 'jJ6Flys3zK9jUhnOHf6G49Dyp5hah6CNP84-gY-n9eo',
* y: 'nhI6iD5eFXgBTLt_1p3aip-5VbZeMhxeFSpjfEAf7Ww'
* })
*
* console.log(thumbprint)
* // 'urn:ietf:params:oauth:jwk-thumbprint:sha-256:w9eYdC6_s_tLQ8lH6PUpc0mddazaqtPgeC2IgWDiqY8'
* ```
*/
export async function calculateJwkThumbprintUri(
jwk: JWK,
digestAlgorithm: 'sha256' | 'sha384' | 'sha512' = 'sha256',
): Promise<string> {
const thumbprint = await calculateJwkThumbprint(jwk, digestAlgorithm)
return `urn:ietf:params:oauth:jwk-thumbprint:sha-${digestAlgorithm.slice(-3)}:${thumbprint}`
}

View file

@ -1,17 +1,44 @@
import test from 'ava'
import { keyRoot } from '../dist.mjs'
const { calculateJwkThumbprint } = await import(keyRoot)
const { calculateJwkThumbprint, calculateJwkThumbprintUri } = await import(keyRoot)
const jwk = {
kty: 'RSA',
n: '0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw',
e: 'AQAB',
alg: 'RS256',
}
test('https://www.rfc-editor.org/rfc/rfc7638#section-3.1', async (t) => {
t.is(await calculateJwkThumbprint(jwk), 'NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs')
t.is(await calculateJwkThumbprint(jwk, 'sha256'), 'NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs')
t.is(
await calculateJwkThumbprint({
kty: 'RSA',
n: '0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw',
e: 'AQAB',
alg: 'RS256',
}),
'NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs',
await calculateJwkThumbprint(jwk, 'sha384'),
'R9_OfJjSjaw8Fuum86UzK5ixTdN9bo9BaqPSiseq89DWfmqCdpSgUHus-cxDUNc8',
)
t.is(
await calculateJwkThumbprint(jwk, 'sha512'),
'DpvEwocfn3FjeWWQjcJHzWrpKTIymKwgoL1xVgQcud48-qZDSRCr1zfWZQdHAJn_ciqXqPTSARyg-L-NyNGpVA',
)
})
test('https://www.rfc-editor.org/rfc/rfcXXXX', async (t) => {
t.is(
await calculateJwkThumbprintUri(jwk),
'urn:ietf:params:oauth:jwk-thumbprint:sha-256:NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs',
)
t.is(
await calculateJwkThumbprintUri(jwk, 'sha256'),
'urn:ietf:params:oauth:jwk-thumbprint:sha-256:NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs',
)
t.is(
await calculateJwkThumbprintUri(jwk, 'sha384'),
'urn:ietf:params:oauth:jwk-thumbprint:sha-384:R9_OfJjSjaw8Fuum86UzK5ixTdN9bo9BaqPSiseq89DWfmqCdpSgUHus-cxDUNc8',
)
t.is(
await calculateJwkThumbprintUri(jwk, 'sha512'),
'urn:ietf:params:oauth:jwk-thumbprint:sha-512:DpvEwocfn3FjeWWQjcJHzWrpKTIymKwgoL1xVgQcud48-qZDSRCr1zfWZQdHAJn_ciqXqPTSARyg-L-NyNGpVA',
)
})