Skip to content

Commit

Permalink
feat: Use Jose & @tsed/jwks to generate keys
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakita committed May 18, 2021
1 parent 1861319 commit 1fa8b47
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 13,169 deletions.
49 changes: 7 additions & 42 deletions boot/keys.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,14 @@
/* global process */

/**
* Module dependencies
*/

const AnvilConnectKeys = require('anvil-connect-keys')

/**
* Create a keypair client
*/

const keygen = new AnvilConnectKeys()

const loadKeyPairs = () => {
keys = keygen.loadKeyPairs()
keys.jwks.keys = keys.jwks.keys.map((key, index) => {
return {
...key,
kid: 'key-' + index
}
})

return keys
}

/**
* Attempt to load the key pairs
*/
const { KeyGen } = require('../lib/keygen')

let keys
try { keys = loadKeyPairs() } catch (e) {}
try {
const keyGen = new KeyGen()

/**
* If the keypairs don't exist, try to create them
*/
if (!keys) {
try {
keygen.generateKeyPairs()
keys = loadKeyPairs()
} catch (e) {
console.log(e)
process.exit(1)
}
keys = keyGen.getKeys()
} catch (e) {
console.log(e)
process.exit(1)
}

/**
* Export
*/
Expand Down
47 changes: 9 additions & 38 deletions boot/setup.js
Original file line number Diff line number Diff line change
@@ -1,55 +1,26 @@
/**
* Module dependencies
*/

var User = require('../models/User')
var AnvilConnectKeys = require('anvil-connect-keys')
var keygen = new AnvilConnectKeys()
const User = require('../models/User')
const { KeyGen } = require('../lib/keygen')

/**
* Check if server is in out-of-box mode
*/

function isOOB (cb) {
User.listByRoles('authority', function (err, users) {
if (err) { return cb(err) }
if (err) {
return cb(err)
}
// return true if there are no authority users
return cb(null, !users || !users.length)
})
}

exports.isOOB = isOOB

/**
* Read setup token from filesystem or create if missing
*/

function readSetupToken (cb) {
var token
var write = false

exports.readSetupToken = (cb) => {
try {
// try to read setup token from filesystem
token = keygen.loadSetupToken()
// if token is blank, try to generate a new token and save it
if (!token.trim()) {
write = true
}
const keyGen = new KeyGen()
cb(null, keyGen.getSetupToken())
} catch (err) {
// if unable to read, try to generate a new token and save it
write = true
}

if (write) {
try {
token = keygen.generateSetupToken()
} catch (err) {
// if we can't write the token to disk, something is very wrong
return cb(err)
}
cb(err)
}

// return the token
cb(null, token)
}
exports.readSetupToken = readSetupToken
101 changes: 101 additions & 0 deletions lib/keygen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
const fs = require('fs-extra')
const { join, dirname } = require('path')
const { JWK, generateJwks } = require('@tsed/jwks')
const crypto = require('crypto')

class Keygen {
constructor (directory) {
// base directory for keys to be read from and written to
this.directory = join(directory || process.cwd(), 'keys')

// signature key pair file paths
this.sig = {
pub: join(this.directory, 'sig.rsa.pub.pem'),
prv: join(this.directory, 'sig.rsa.prv.pem')
}

// encryption key pair file paths
this.enc = {
pub: join(this.directory, 'enc.rsa.pub.pem'),
prv: join(this.directory, 'enc.rsa.prv.pem')
}

// setup token
this.setup = join(this.directory, 'setup.token')
}

generateKeyPairs () {
const sigKeys = JWK.generateSync('RSA', 4096, { use: 'sig' })
fs.ensureDirSync(dirname(this.sig.pub))

fs.writeFileSync(this.sig.prv, sigKeys.toPEM(true))
fs.writeFileSync(this.sig.pub, sigKeys.toPEM(false))

const encKeys = JWK.generateSync('RSA', 4096, { use: 'sig' })
fs.writeFileSync(this.enc.prv, encKeys.toPEM(true))
fs.writeFileSync(this.enc.pub, encKeys.toPEM(false))
}

loadKeyPairs () {
const jwks = generateJwks({
certificates: [
{ path: this.sig.pub, alg: 'RS256', use: 'sig', kid: 'key-0' },
{ path: this.enc.pub, alg: 'RS256', use: 'enc', kid: 'key-1' }
]
})

return {
sig: {
pub: fs.readFileSync(this.sig.pub, { encoding: 'utf8' }),
prv: fs.readFileSync(this.sig.prv, { encoding: 'utf8' })
},
enc: {
pub: fs.readFileSync(this.enc.pub, { encoding: 'utf8' }),
prv: fs.readFileSync(this.enc.prv, { encoding: 'utf8' })
},
jwks
}
}

loadSetupToken () {
return fs.readFileSync(this.setup, { encoding: 'utf8' }).toString().trim()
}

generateSetupToken () {
fs.ensureDirSync(dirname(this.setup))

const token = crypto.randomBytes(256).toString('hex')
try {
fs.writeFileSync(this.setup, token, 'utf8')
} catch (e) {
throw new Error(`Unable to save setup token to ${this.setup}`)
}
return token
}

getSetupToken () {
try {
// try to read setup token from filesystem
const token = this.loadSetupToken()
// if token is blank, try to generate a new token and save it
if (token) {
return token
}
} catch (err) {
}

return this.generateSetupToken()
}

getKeys () {
const exists = fs.existsSync(this.sig.pub)

if (!exists) {
this.generateKeyPairs()
}

return this.loadKeyPairs()
}
}

exports.KeyGen = Keygen
Loading

0 comments on commit 1fa8b47

Please sign in to comment.