Skip to content

Commit

Permalink
Merge pull request #1277 from cplussharp/jwk
Browse files Browse the repository at this point in the history
  • Loading branch information
a3957273 authored Apr 13, 2024
2 parents 4619a51 + cbf990f commit 67195f6
Show file tree
Hide file tree
Showing 5 changed files with 530 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/core/config/Categories.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@
"Hex to PEM",
"Hex to Object Identifier",
"Object Identifier to Hex",
"PEM to JWK",
"JWK to PEM",
"Generate PGP Key Pair",
"PGP Encrypt",
"PGP Decrypt",
Expand Down
80 changes: 80 additions & 0 deletions src/core/operations/JWKToPem.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* @author cplussharp
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/

import r from "jsrsasign";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";

/**
* PEM to JWK operation
*/
class PEMToJWK extends Operation {

/**
* PEMToJWK constructor
*/
constructor() {
super();

this.name = "JWK to PEM";
this.module = "PublicKey";
this.description = "Converts Keys in JSON Web Key format to PEM format (PKCS#8).";
this.infoURL = "https://datatracker.ietf.org/doc/html/rfc7517";
this.inputType = "string";
this.outputType = "string";
this.args = [];
this.checks = [
{
"pattern": "\"kty\":\\s*\"(EC|RSA)\"",
"flags": "gm",
"args": []
}
];
}

/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const inputJson = JSON.parse(input);

let keys = [];
if (Array.isArray(inputJson)) {
// list of keys => transform all keys
keys = inputJson;
} else if (Array.isArray(inputJson.keys)) {
// JSON Web Key Set => transform all keys
keys = inputJson.keys;
} else if (typeof inputJson === "object") {
// single key
keys.push(inputJson);
} else {
throw new OperationError("Input is not a JSON Web Key");
}

let output = "";
for (let i=0; i<keys.length; i++) {
const jwk = keys[i];
if (typeof jwk.kty !== "string") {
throw new OperationError("Invalid JWK format");
} else if ("|RSA|EC|".indexOf(jwk.kty) === -1) {
throw new OperationError(`Unsupported JWK key type '${inputJson.kty}'`);
}

const key = r.KEYUTIL.getKey(jwk);
const pem = key.isPrivate ? r.KEYUTIL.getPEM(key, "PKCS8PRV") : r.KEYUTIL.getPEM(key);

// PEM ends with '\n', so a new key always starts on a new line
output += pem;
}

return output;
}
}

export default PEMToJWK;
88 changes: 88 additions & 0 deletions src/core/operations/PEMToJWK.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* @author cplussharp
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/

import r from "jsrsasign";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";

/**
* PEM to JWK operation
*/
class PEMToJWK extends Operation {

/**
* PEMToJWK constructor
*/
constructor() {
super();

this.name = "PEM to JWK";
this.module = "PublicKey";
this.description = "Converts Keys in PEM format to a JSON Web Key format.";
this.infoURL = "https://datatracker.ietf.org/doc/html/rfc7517";
this.inputType = "string";
this.outputType = "string";
this.args = [];
this.checks = [
{
"pattern": "-----BEGIN ((RSA |EC )?(PRIVATE|PUBLIC) KEY|CERTIFICATE)-----",
"args": []
}
];
}

/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
let output = "";
let match;
const regex = /-----BEGIN ([A-Z][A-Z ]+[A-Z])-----/g;
while ((match = regex.exec(input)) !== null) {
// find corresponding end tag
const indexBase64 = match.index + match[0].length;
const header = input.substring(match.index, indexBase64);
const footer = `-----END ${match[1]}-----`;
const indexFooter = input.indexOf(footer, indexBase64);
if (indexFooter === -1) {
throw new OperationError(`PEM footer '${footer}' not found`);
}

const pem = input.substring(match.index, indexFooter + footer.length);
if (match[1].indexOf("KEY") !== -1) {
if (header === "-----BEGIN RSA PUBLIC KEY-----") {
throw new OperationError("Unsupported RSA public key format. Only PKCS#8 is supported.");
}

const key = r.KEYUTIL.getKey(pem);
if (key.type === "DSA") {
throw new OperationError("DSA keys are not supported for JWK");
}
const jwk = r.KEYUTIL.getJWKFromKey(key);
if (output.length > 0) {
output += "\n";
}
output += JSON.stringify(jwk);
} else if (match[1] === "CERTIFICATE") {
const cert = new r.X509();
cert.readCertPEM(pem);
const key = cert.getPublicKey();
const jwk = r.KEYUTIL.getJWKFromKey(key);
if (output.length > 0) {
output += "\n";
}
output += JSON.stringify(jwk);
} else {
throw new OperationError(`Unsupported PEM type '${match[1]}'`);
}
}
return output;
}
}

export default PEMToJWK;
1 change: 1 addition & 0 deletions tests/operations/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ import "./tests/JSONBeautify.mjs";
import "./tests/JSONMinify.mjs";
import "./tests/JSONtoCSV.mjs";
import "./tests/Jump.mjs";
import "./tests/JWK.mjs";
import "./tests/JWTDecode.mjs";
import "./tests/JWTSign.mjs";
import "./tests/JWTVerify.mjs";
Expand Down
Loading

0 comments on commit 67195f6

Please sign in to comment.