Last active
March 21, 2024 19:21
-
-
Save ncortines/7fa5fc66b50d28e9452f775780e6d918 to your computer and use it in GitHub Desktop.
Ephemeral SSL certificates for Electron based applications
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
'use strict'; | |
const https = require('https'); | |
const crypto = require('crypto'); | |
const tls = require('tls'); | |
const forge = require('node-forge'); | |
const pki = forge.pki; | |
const getRandomPortNumber = () => | |
Math.floor(Math.random() * (65535 - 49152)) + 49152 | |
const assignRandomPortToServer = server => | |
new Promise((resolve, reject) => { | |
server.listen(getRandomPortNumber()) | |
.on('error', error => { | |
if (error.code === 'EADDRINUSE') { | |
server.close(); | |
server.listen(getRandomPortNumber()); | |
} else { | |
reject(error); | |
} | |
}) | |
.on('listening', () => { | |
resolve(server); | |
}); | |
}); | |
const getCryptoKeysInPemFormat = () => | |
new Promise((resolve, reject) => { | |
console.info('http utils :: generating a new rsa key pair'); | |
crypto.generateKeyPair('rsa', { | |
modulusLength: 2048, | |
publicKeyEncoding: { | |
type: 'pkcs1', | |
format: 'pem' | |
}, | |
privateKeyEncoding: { | |
type: 'pkcs1', | |
format: 'pem' | |
} | |
}, | |
(error, publicKeyInPemFormat, privateKeyInPemFormat) => { | |
if (error) { | |
reject(error) | |
} else { | |
resolve({ | |
privateKeyInPemFormat, | |
publicKeyInPemFormat | |
}) | |
} | |
}); | |
}); | |
const getEcdheCiphers = () => | |
tls.getCiphers() | |
.filter(cipher => cipher.startsWith('ecdhe')) | |
.map(cipher => cipher.toUpperCase()) | |
.join(', '); | |
const getCertificateInPemFormat = (privateKeyInPemFormat, publicKeyInPemFormat) => { | |
console.info('http utils :: creating a new X.509 self-signed certificate'); | |
const privateKey = pki.privateKeyFromPem(privateKeyInPemFormat) | |
const publicKey = pki.publicKeyFromPem(publicKeyInPemFormat) | |
const cert = pki.createCertificate(); | |
cert.publicKey = publicKey; | |
cert.serialNumber = '01' + crypto.randomBytes(19).toString('hex'); | |
cert.validity.notBefore = new Date(); | |
cert.validity.notAfter = new Date(); | |
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1); | |
const attrs = [{ | |
name: 'commonName', | |
value: 'localhost' | |
}, { | |
name: 'countryName', | |
value: 'SP' | |
}, { | |
shortName: 'ST', | |
value: 'Madrid' | |
}, { | |
name: 'localityName', | |
value: 'Madrid' | |
}, { | |
name: 'organizationName', | |
value: 'My Org' | |
}, { | |
shortName: 'OU', | |
value: 'My Unit' | |
}]; | |
cert.setSubject(attrs); | |
cert.setIssuer(attrs); | |
cert.setExtensions([{ | |
name: 'basicConstraints', | |
cA: true | |
}, { | |
name: 'keyUsage', | |
keyCertSign: true, | |
digitalSignature: true, | |
nonRepudiation: true, | |
keyEncipherment: true, | |
dataEncipherment: true | |
}, { | |
name: 'extKeyUsage', | |
serverAuth: true, | |
clientAuth: true, | |
codeSigning: true, | |
emailProtection: true, | |
timeStamping: true | |
}, { | |
name: 'nsCertType', | |
client: true, | |
server: true, | |
email: true, | |
objsign: true, | |
sslCA: true, | |
emailCA: true, | |
objCA: true | |
}, { | |
name: 'subjectAltName', | |
altNames: [ | |
{ | |
type: 7, | |
ip: '127.0.0.1' | |
}, | |
{ | |
type: 2, | |
value: 'localhost' | |
} | |
] | |
}, { | |
name: 'subjectKeyIdentifier' | |
}]); | |
cert.sign(privateKey); | |
return pki.certificateToPem(cert); | |
} | |
const startLocalHttpsServer = (requestCert = false, rejectUnauthorized = true) => | |
getCryptoKeysInPemFormat() | |
.then(({ privateKeyInPemFormat, publicKeyInPemFormat }) => { | |
const secureCiphers = getEcdheCiphers(); | |
const certificateInPemFormat = getCertificateInPemFormat(privateKeyInPemFormat, publicKeyInPemFormat); | |
console.info('http utils :: starting new local https server'); | |
const options = { | |
key: privateKeyInPemFormat, | |
cert: certificateInPemFormat, | |
ciphers: secureCiphers, | |
requestCert: requestCert, | |
rejectUnauthorized: rejectUnauthorized | |
} | |
const server = https.createServer(options); | |
return assignRandomPortToServer(server); | |
}); | |
module.exports = Object.freeze({ | |
startLocalHttpsServer | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment