forked from IdentityModel/oidc-client-js
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request IdentityModel#775 from ivantm/rsa
Add swappable RSA-only crypto impl
- Loading branch information
Showing
11 changed files
with
776 additions
and
341 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Declare the ES6 features we're using | ||
|
||
// TODO: Consider using the local function versions of these, so that we | ||
// avoid modifying browser globals (potential for interop bugs with other libraries | ||
// on the page that might be polyfilling ES6 features) | ||
|
||
import 'core-js/es6/promise'; | ||
import 'core-js/fn/function/bind'; | ||
import 'core-js/fn/object/assign'; | ||
import 'core-js/fn/object/assign'; | ||
import 'core-js/fn/array/find'; | ||
import 'core-js/fn/array/some'; | ||
import 'core-js/fn/array/is-array'; | ||
import 'core-js/fn/array/splice'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,165 +1,4 @@ | ||
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. | ||
import { jws, KeyUtil, X509, crypto, hextob64u, b64tohex, AllowedSigningAlgs } from './crypto/jsrsasign'; | ||
import getJoseUtil from './JoseUtilImpl'; | ||
|
||
import { jws, KEYUTIL as KeyUtil, X509, crypto, hextob64u, b64tohex } from '../jsrsasign/dist/jsrsasign.js'; | ||
//import { jws, KEYUTIL as KeyUtil, X509, crypto, hextob64u, b64tohex } from 'jsrsasign'; | ||
import { Log } from './Log.js'; | ||
|
||
const AllowedSigningAlgs = ['RS256', 'RS384', 'RS512', 'PS256', 'PS384', 'PS512', 'ES256', 'ES384', 'ES512']; | ||
|
||
export class JoseUtil { | ||
|
||
static parseJwt(jwt) { | ||
Log.debug("JoseUtil.parseJwt"); | ||
try { | ||
var token = jws.JWS.parse(jwt); | ||
return { | ||
header: token.headerObj, | ||
payload: token.payloadObj | ||
} | ||
} | ||
catch (e) { | ||
Log.error(e); | ||
} | ||
} | ||
|
||
static validateJwt(jwt, key, issuer, audience, clockSkew, now, timeInsensitive) { | ||
Log.debug("JoseUtil.validateJwt"); | ||
|
||
try { | ||
if (key.kty === "RSA") { | ||
if (key.e && key.n) { | ||
key = KeyUtil.getKey(key); | ||
} | ||
else if (key.x5c && key.x5c.length) { | ||
var hex = b64tohex(key.x5c[0]); | ||
key = X509.getPublicKeyFromCertHex(hex); | ||
} | ||
else { | ||
Log.error("JoseUtil.validateJwt: RSA key missing key material", key); | ||
return Promise.reject(new Error("RSA key missing key material")); | ||
} | ||
} | ||
else if (key.kty === "EC") { | ||
if (key.crv && key.x && key.y) { | ||
key = KeyUtil.getKey(key); | ||
} | ||
else { | ||
Log.error("JoseUtil.validateJwt: EC key missing key material", key); | ||
return Promise.reject(new Error("EC key missing key material")); | ||
} | ||
} | ||
else { | ||
Log.error("JoseUtil.validateJwt: Unsupported key type", key && key.kty); | ||
return Promise.reject(new Error("Unsupported key type: " + key && key.kty)); | ||
} | ||
|
||
return JoseUtil._validateJwt(jwt, key, issuer, audience, clockSkew, now, timeInsensitive); | ||
} | ||
catch (e) { | ||
Log.error(e && e.message || e); | ||
return Promise.reject("JWT validation failed"); | ||
} | ||
} | ||
|
||
static validateJwtAttributes(jwt, issuer, audience, clockSkew, now, timeInsensitive) { | ||
if (!clockSkew) { | ||
clockSkew = 0; | ||
} | ||
|
||
if (!now) { | ||
now = parseInt(Date.now() / 1000); | ||
} | ||
|
||
var payload = JoseUtil.parseJwt(jwt).payload; | ||
|
||
if (!payload.iss) { | ||
Log.error("JoseUtil._validateJwt: issuer was not provided"); | ||
return Promise.reject(new Error("issuer was not provided")); | ||
} | ||
if (payload.iss !== issuer) { | ||
Log.error("JoseUtil._validateJwt: Invalid issuer in token", payload.iss); | ||
return Promise.reject(new Error("Invalid issuer in token: " + payload.iss)); | ||
} | ||
|
||
if (!payload.aud) { | ||
Log.error("JoseUtil._validateJwt: aud was not provided"); | ||
return Promise.reject(new Error("aud was not provided")); | ||
} | ||
var validAudience = payload.aud === audience || (Array.isArray(payload.aud) && payload.aud.indexOf(audience) >= 0); | ||
if (!validAudience) { | ||
Log.error("JoseUtil._validateJwt: Invalid audience in token", payload.aud); | ||
return Promise.reject(new Error("Invalid audience in token: " + payload.aud)); | ||
} | ||
if (payload.azp && payload.azp !== audience) { | ||
Log.error("JoseUtil._validateJwt: Invalid azp in token", payload.azp); | ||
return Promise.reject(new Error("Invalid azp in token: " + payload.azp)); | ||
} | ||
|
||
if (!timeInsensitive) { | ||
var lowerNow = now + clockSkew; | ||
var upperNow = now - clockSkew; | ||
|
||
if (!payload.iat) { | ||
Log.error("JoseUtil._validateJwt: iat was not provided"); | ||
return Promise.reject(new Error("iat was not provided")); | ||
} | ||
if (lowerNow < payload.iat) { | ||
Log.error("JoseUtil._validateJwt: iat is in the future", payload.iat); | ||
return Promise.reject(new Error("iat is in the future: " + payload.iat)); | ||
} | ||
|
||
if (payload.nbf && lowerNow < payload.nbf) { | ||
Log.error("JoseUtil._validateJwt: nbf is in the future", payload.nbf); | ||
return Promise.reject(new Error("nbf is in the future: " + payload.nbf)); | ||
} | ||
|
||
if (!payload.exp) { | ||
Log.error("JoseUtil._validateJwt: exp was not provided"); | ||
return Promise.reject(new Error("exp was not provided")); | ||
} | ||
if (payload.exp < upperNow) { | ||
Log.error("JoseUtil._validateJwt: exp is in the past", payload.exp); | ||
return Promise.reject(new Error("exp is in the past:" + payload.exp)); | ||
} | ||
} | ||
|
||
return Promise.resolve(payload); | ||
} | ||
|
||
static _validateJwt(jwt, key, issuer, audience, clockSkew, now, timeInsensitive) { | ||
|
||
return JoseUtil.validateJwtAttributes(jwt, issuer, audience, clockSkew, now, timeInsensitive).then(payload => { | ||
try { | ||
if (!jws.JWS.verify(jwt, key, AllowedSigningAlgs)) { | ||
Log.error("JoseUtil._validateJwt: signature validation failed"); | ||
return Promise.reject(new Error("signature validation failed")); | ||
} | ||
|
||
return payload; | ||
} | ||
catch (e) { | ||
Log.error(e && e.message || e); | ||
return Promise.reject(new Error("signature validation failed")); | ||
} | ||
}); | ||
} | ||
|
||
static hashString(value, alg) { | ||
try { | ||
return crypto.Util.hashString(value, alg); | ||
} | ||
catch (e) { | ||
Log.error(e); | ||
} | ||
} | ||
|
||
static hexToBase64Url(value) { | ||
try { | ||
return hextob64u(value); | ||
} | ||
catch (e) { | ||
Log.error(e); | ||
} | ||
} | ||
} | ||
export const JoseUtil = getJoseUtil({ jws, KeyUtil, X509, crypto, hextob64u, b64tohex, AllowedSigningAlgs }); |
Oops, something went wrong.