@guarani/jose
Version:
Implementation of the RFCs of the JOSE Working Group.
133 lines (132 loc) • 6.15 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RsaKey = void 0;
const crypto_1 = require("crypto");
const util_1 = require("util");
const invalid_json_web_key_exception_1 = require("../../../exceptions/invalid-json-web-key.exception");
const unsupported_algorithm_exception_1 = require("../../../exceptions/unsupported-algorithm.exception");
const jsonwebkey_1 = require("../../jsonwebkey");
const generateKeyPairAsync = (0, util_1.promisify)(crypto_1.generateKeyPair);
/**
* Implementation of {@link https://www.rfc-editor.org/rfc/rfc7518.html#section-6.3 RFC 7518 Section 6.3}.
*/
class RsaKey extends jsonwebkey_1.JsonWebKey {
/**
* Instantiates an RSA JSON Web Key based on the provided Parameters.
*
* @param key Parameters of the RSA JSON Web Key.
* @param options Optional JSON Web Key Parameters.
*/
constructor(key, options = {}) {
if (key instanceof RsaKey) {
return key;
}
const params = { ...key, ...options };
if (typeof params.kty !== 'string') {
throw new invalid_json_web_key_exception_1.InvalidJsonWebKeyException('Invalid parameter "kty".');
}
if (params.kty !== 'RSA') {
throw new unsupported_algorithm_exception_1.UnsupportedAlgorithmException(`Invalid JSON Web Key Type. Expected "RSA", got "${params.kty}".`);
}
if (typeof params.n !== 'string') {
throw new invalid_json_web_key_exception_1.InvalidJsonWebKeyException('Invalid key parameter "n".');
}
if (Buffer.from(params.n, 'base64url').length < 256) {
throw new invalid_json_web_key_exception_1.InvalidJsonWebKeyException('The modulus MUST have AT LEAST 2048 bits.');
}
if (typeof params.e !== 'string') {
throw new invalid_json_web_key_exception_1.InvalidJsonWebKeyException('Invalid key parameter "e".');
}
// TODO: Validate the following values based on the previous ones.
if (['d', 'p', 'q', 'dp', 'dq', 'qi'].some((param) => params[param] !== undefined)) {
if (typeof params.d !== 'string') {
throw new invalid_json_web_key_exception_1.InvalidJsonWebKeyException('Invalid key parameter "d".');
}
if (typeof params.p !== 'string') {
throw new invalid_json_web_key_exception_1.InvalidJsonWebKeyException('Invalid key parameter "p".');
}
if (typeof params.q !== 'string') {
throw new invalid_json_web_key_exception_1.InvalidJsonWebKeyException('Invalid key parameter "q".');
}
if (typeof params.dp !== 'string') {
throw new invalid_json_web_key_exception_1.InvalidJsonWebKeyException('Invalid key parameter "dp".');
}
if (typeof params.dq !== 'string') {
throw new invalid_json_web_key_exception_1.InvalidJsonWebKeyException('Invalid key parameter "dq".');
}
if (typeof params.qi !== 'string') {
throw new invalid_json_web_key_exception_1.InvalidJsonWebKeyException('Invalid key parameter "qi".');
}
}
super(params);
}
/**
* Generates a new RSA JSON Web Key.
*
* @param options Options for the generation of the RSA JSON Web Key.
* @param params Optional JSON Web Key Parameters.
* @returns Generated RSA JSON Web Key.
*/
static async generate(options, params = {}) {
const { modulus, publicExponent } = options;
if (!Number.isInteger(modulus)) {
throw new TypeError('Invalid parameter "modulus".');
}
if (modulus < 2048) {
throw new Error('The modulus must be at least 2048 bits.');
}
if (publicExponent !== undefined && !Number.isInteger(publicExponent)) {
throw new TypeError('Invalid parameter "publicExponent".');
}
const { privateKey } = await generateKeyPairAsync('rsa', { modulusLength: modulus, publicExponent });
return new RsaKey(privateKey.export({ format: 'jwk' }), params);
}
/**
* Loads the provided JSON Web Key into a NodeJS Crypto Key.
*
* @param params Parameters of the JSON Web Key.
* @returns NodeJS Crypto Key.
*/
loadCryptoKey(params) {
const input = { format: 'jwk', key: params };
return params.d === undefined ? (0, crypto_1.createPublicKey)(input) : (0, crypto_1.createPrivateKey)(input);
}
/**
* Exports the data of the RSA JSON Web Key.
*
* @param options Options for exporting the data of the RSA JSON Web Key.
* @returns Encoded data of the RSA JSON Web Key.
*/
export(options) {
const { encoding, format, type } = options;
if (encoding !== 'der' && encoding !== 'pem') {
throw new TypeError('Invalid option "encoding".');
}
if (format !== 'pkcs1' && format !== 'pkcs8' && format !== 'spki') {
throw new TypeError('Invalid option "format".');
}
if (type !== 'private' && type !== 'public') {
throw new TypeError('Invalid option "type".');
}
if (type === 'private' && format !== 'pkcs1' && format !== 'pkcs8') {
throw new TypeError(`Unsupported format "${format}" for type "${type}".`);
}
if (type === 'public' && format !== 'pkcs1' && format !== 'spki') {
throw new TypeError(`Unsupported format "${format}" for type "${type}".`);
}
if (this.cryptoKey.type === 'public' && type === 'private') {
throw new TypeError('Cannot export private data from a public key.');
}
let { cryptoKey } = this;
const input = { format: encoding, type: format };
if (this.cryptoKey.type === 'private' && type === 'public') {
cryptoKey = (0, crypto_1.createPublicKey)(cryptoKey);
}
let exported = cryptoKey.export(input);
if (encoding === 'pem') {
exported = exported.slice(0, -1);
}
return exported;
}
}
exports.RsaKey = RsaKey;