packages/fxa-auth-server/scripts/gen_keys.js (63 lines of code) (raw):
#!/usr/bin/env node -r esbuild-register
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* scripts/gen_keys.js creates public and private keys suitable for
key signing Persona Primary IdP's.
Usage:
scripts/gen_keys.js
Will create these files
./config/public-key.json
./config/secret-key.json
If these files already exist, this script will show an error message
and exit. You must remove both keys if you want to generate a new
keypair.
*/
'use strict';
const fs = require('fs');
const cp = require('child_process');
const assert = require('assert');
const crypto = require('crypto');
const { pem2jwk } = require('@fxa/shared/pem-jwk');
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = 'dev';
}
const { config } = require('../config');
const pubKeyFile = config.get('publicKeyFile');
const secretKeyFile = config.get('secretKeyFile');
try {
const keysExist = fs.existsSync(pubKeyFile) && fs.existsSync(secretKeyFile);
assert(!keysExist, 'keys already exists');
} catch (e) {
process.exit();
}
// We tag our keys with their creation time, and a unique key id
// based on a hash of the public key and the timestamp. The result
// comes out like:
// {
// kid: "2017-03-16-ebe69008de771d62cd1cadf9faa6daae"
// "fxa-createdAt": 1489716000,
// }
function addKeyProperties(key) {
const now = new Date();
key.kty = 'RSA';
key.kid = `${now.toISOString().slice(0, 10)}-${crypto
.createHash('sha256')
.update(key.n)
.update(key.e)
.digest('hex')
.slice(0, 32)}`;
// Timestamp to nearest hour; consumers don't need to know the precise time.
key['fxa-createdAt'] = Math.round(now / 1000 / 3600) * 3600;
return key;
}
function convertPem2Jwk(pem) {
const s = pem2jwk(pem);
addKeyProperties(s);
fs.writeFileSync(secretKeyFile, JSON.stringify(s));
console.error('Secret Key saved:', secretKeyFile);
const pub = {
kid: s.kid,
kty: s.kty,
'fxa-createdAt': s['fxa-createdAt'],
n: s.n,
e: s.e,
};
fs.writeFileSync(pubKeyFile, JSON.stringify(pub));
console.error('Public Key saved:', pubKeyFile);
}
console.log('Generating keypair');
cp.exec(`openssl genrsa 2048`, (err, stdout, stderr) => {
try {
convertPem2Jwk(stdout);
} catch (err) {
// As of OpenSSL v3.0 genrsa uses format PKCS#8, which is not supported by pem-jwk
// As a work around, use the --traditional flag which outputs format PKCS#1
// https://github.com/dannycoates/pem-jwk/issues/15
if (err.message === 'Failed to match tag: "bitstr" at: ["privateKey"]') {
cp.exec(`openssl genrsa --traditional 2048`, (err, stdout, stderr) => {
convertPem2Jwk(stdout);
});
} else {
throw err;
}
}
});