From a19e334fe1484390b6e389b3265bcb999eaeac0f Mon Sep 17 00:00:00 2001 From: Robert Cronin Date: Mon, 7 Sep 2020 17:59:23 +0800 Subject: [PATCH] wip --- src/lib/keys/pki/PublicKeyInfrastructure.ts | 92 ++------------- .../peers/peer-connection/PeerConnection.ts | 2 +- src/lib/peers/peer-connection/PeerServer.ts | 6 +- tests/lib/peers/PKI.test.ts | 110 ++++++++++++++---- 4 files changed, 101 insertions(+), 109 deletions(-) diff --git a/src/lib/keys/pki/PublicKeyInfrastructure.ts b/src/lib/keys/pki/PublicKeyInfrastructure.ts index c0f990219f..f8ccdf4aed 100644 --- a/src/lib/keys/pki/PublicKeyInfrastructure.ts +++ b/src/lib/keys/pki/PublicKeyInfrastructure.ts @@ -1,13 +1,7 @@ -import path from 'path'; import { pki } from 'node-forge'; import Vault from '../../vaults/Vault'; -type EDKeyPair = { - publicKey: pki.ed25519.NativeBuffer; - privateKey: pki.ed25519.NativeBuffer; -} - -type TLSCredentials = { privateKey: string, certificate: string, caCertificate: string } +type TLSCredentials = { privateKey: string, certificate: string, rootCertificate: string } /** * This class manages X.509 certificates for secure and authenticated communication between peers. */ @@ -19,7 +13,7 @@ class PublicKeyInfrastructure { pkiVault: Vault // certificate signed by another - private keypair: EDKeyPair + private keypair: pki.rsa.KeyPair private certificate?: pki.Certificate @@ -31,22 +25,24 @@ class PublicKeyInfrastructure { public get TLSClientCredentials(): TLSCredentials | undefined { + console.log(this.certificate); if (this.certificate) { return { privateKey: pki.privateKeyToPem(this.keypair.privateKey), certificate: pki.certificateToPem(this.certificate), - caCertificate: this.CACertificates + rootCertificate: this.CACertificates } } } public get TLSServerCredentials(): TLSCredentials | undefined { + console.log(this.certificate); if (this.certificate) { return { privateKey: pki.privateKeyToPem(this.keypair.privateKey), certificate: pki.certificateToPem(this.certificate), - caCertificate: this.CACertificates + rootCertificate: this.CACertificates } } } @@ -56,7 +52,7 @@ class PublicKeyInfrastructure { // root CA private rootCertificate: pki.Certificate - private rootKeypair: EDKeyPair + private rootKeypair: pki.rsa.KeyPair public get RootCert(): string { return pki.certificateToPem(this.rootCertificate) } @@ -78,8 +74,6 @@ class PublicKeyInfrastructure { ) { const certificate = pki.createCertificate(); certificate.publicKey = this.rootKeypair.publicKey; - // alternatively set public key from a csr - //cert.publicKey = csr.publicKey; certificate.serialNumber = '01'; certificate.validity.notBefore = new Date(); certificate.validity.notAfter = new Date(); @@ -96,8 +90,6 @@ class PublicKeyInfrastructure { }, ]; certificate.setSubject(attrs); - // alternatively set subject from a csr - //cert.setSubject(csr.subject.attributes); certificate.setIssuer(attrs); certificate.setExtensions([ { @@ -260,14 +252,14 @@ class PublicKeyInfrastructure { this.keypair = JSON.parse(this.pkiVault.getSecret('keypair').toString()) } else { // create the keypair if it doesn't exist - this.keypair = pki.ed25519.generateKeyPair() + this.keypair = pki.rsa.generateKeyPair() } if (this.pkiVault.secretExists('root_keypair')) { this.rootKeypair = JSON.parse(this.pkiVault.getSecret('root_keypair').toString()) } else { // create the keys if it doesn't exist - this.rootKeypair = pki.ed25519.generateKeyPair() + this.rootKeypair = pki.rsa.generateKeyPair() } // load certificates @@ -330,69 +322,3 @@ class PublicKeyInfrastructure { export default PublicKeyInfrastructure; export { TLSCredentials } - - - - -// const peer1 = new PublicKeyInfrastructure() - -// const peer2 = new PublicKeyInfrastructure() -// const peer2Cert = peer1.handleCSR(peer2.createCSR('localhost', 'pass')) -// console.log(peer2Cert); - -// const key = pki.privateKeyToPem(peer2.keys.privateKey) -// const cert = peer2Cert - - - -// // const cert = PublicKeyInfrastructure.createX509Certificate() -// // const csr = PublicKeyInfrastructure.createCSR() -// // console.log(csr); - -// // console.log(cert.certPem.toString()); -// // console.log(cert.keyPem.toString()); -// // console.log(fs.readFileSync('./tmp/secrets/peer1/server.key').toString()); -// // console.log(fs.readFileSync('./tmp/secrets/peer1/server.crt').toString()); - - -// // const options1: http.ServerOptions = { -// // key: fs.readFileSync('./tmp/secrets/peer1/server.key'), -// // cert: fs.readFileSync('./tmp/secrets/peer1/server.crt') -// // } - -// const options2: http.ServerOptions = { -// key, -// cert, -// } - -// // http.createServer(options1, (req, res) => { -// // res.writeHead(200); -// // res.end("hello world 1\n"); -// // }).listen(8000) - -// // set up the mock ca server -// http.createServer(options2, (req, res) => { -// res.writeHead(200); -// res.end("hello world 2\n"); -// }).listen(8002) - -// const req = http.request({ -// host: 'localhost', -// port: 8002, -// path: '/', -// method: 'GET', -// ca: [peer1.RootCert] -// }, (res) => { -// console.log('statusCode:', res.statusCode); -// console.log('headers:', res.headers); - -// res.on('data', (d) => { -// process.stdout.write(d); -// }); -// }) - -// req.on('error', (e) => { -// console.error(e); -// }); - -// req.end() diff --git a/src/lib/peers/peer-connection/PeerConnection.ts b/src/lib/peers/peer-connection/PeerConnection.ts index 8317332b16..a38746b879 100644 --- a/src/lib/peers/peer-connection/PeerConnection.ts +++ b/src/lib/peers/peer-connection/PeerConnection.ts @@ -24,7 +24,7 @@ class PeerConnection { const credentials = keyManager.pki.TLSClientCredentials; if (credentials) { this.credentials = grpc.credentials.createSsl( - Buffer.from(credentials.caCertificate), + Buffer.from(credentials.rootCertificate), Buffer.from(credentials.privateKey), Buffer.from(credentials.certificate) ); diff --git a/src/lib/peers/peer-connection/PeerServer.ts b/src/lib/peers/peer-connection/PeerServer.ts index fbe37f6c6b..2f33d4a024 100644 --- a/src/lib/peers/peer-connection/PeerServer.ts +++ b/src/lib/peers/peer-connection/PeerServer.ts @@ -32,14 +32,14 @@ class PeerServer { }); // Create the server credentials. SSL only if ca cert exists - const credentials = keyManager.pki.TLSClientCredentials; + const credentials = keyManager.pki.TLSServerCredentials; if (credentials) { this.credentials = grpc.ServerCredentials.createSsl( - Buffer.from(credentials.caCertificate), + Buffer.from(credentials.rootCertificate), [ { private_key: Buffer.from(credentials.privateKey), - cert_chain: Buffer.from(credentials.caCertificate), + cert_chain: Buffer.from(credentials.certificate), }, ], true diff --git a/tests/lib/peers/PKI.test.ts b/tests/lib/peers/PKI.test.ts index 842a839b27..10cc0ad8e0 100644 --- a/tests/lib/peers/PKI.test.ts +++ b/tests/lib/peers/PKI.test.ts @@ -1,37 +1,48 @@ import fs from 'fs' import os from 'os' -import Polykey from "../../../src/lib/Polykey" +import net from 'net' +import http from 'https' +import crypto from 'crypto' import { randomString } from '../../../src/lib/utils' -import KeyManager from '../../../src/lib/keys/KeyManager' +import PublicKeyInfrastructure, { TLSCredentials } from '../../../src/lib/keys/pki/PublicKeyInfrastructure' // TODO: part of adding PKI functionality to polykey describe('PKI', () => { + let tempDirPeerCA: string + let pkiCA: PublicKeyInfrastructure + let tempDirPeerA: string - let peerA: Polykey + let pkiA: PublicKeyInfrastructure let tempDirPeerB: string - let peerB: Polykey + let pkiB: PublicKeyInfrastructure beforeAll(async () => { + // ======== CA PEER ======== // + // Define temp directory + tempDirPeerCA = fs.mkdtempSync(`${os.tmpdir}/pktest${randomString()}`) + + // Create pki + const keyCA = crypto.pbkdf2Sync('passphrase', crypto.randomBytes(16), 10000, 256 / 8, 'sha256') + pkiCA = new PublicKeyInfrastructure(tempDirPeerCA, keyCA, 'localhost') + // ======== PEER A ======== // // Define temp directory tempDirPeerA = fs.mkdtempSync(`${os.tmpdir}/pktest${randomString()}`) - // Create keyManager - const keyManagerA = new KeyManager(tempDirPeerA, fs) - await keyManagerA.generateKeyPair('John Smith', 'john.smith@email.com', 'some passphrase', 1024, true) - - // Initialize polykey - peerA = new Polykey( - tempDirPeerA, - fs, - keyManagerA - ) - while (!peerA.peerManager.peerServer.started) { - await new Promise((resolve, reject) => { - setTimeout(() => resolve(), 500) - }) - } + // Create pki + const keyA = crypto.pbkdf2Sync('passphrase', crypto.randomBytes(16), 10000, 256 / 8, 'sha256') + pkiA = new PublicKeyInfrastructure(tempDirPeerA, keyA, 'localhost') + pkiA.addCA(pkiCA.RootCert) + + // ======== PEER B ======== // + // Define temp directory + tempDirPeerB = fs.mkdtempSync(`${os.tmpdir}/pktest${randomString()}`) + + // Create pki + const keyB = crypto.pbkdf2Sync('passphrase', crypto.randomBytes(16), 10000, 256 / 8, 'sha256') + pkiB = new PublicKeyInfrastructure(tempDirPeerB, keyB, 'localhost') + pkiB.addCA(pkiCA.RootCert) }) afterAll(() => { @@ -39,9 +50,64 @@ describe('PKI', () => { fs.rmdirSync(tempDirPeerB, { recursive: true }) }) - describe('Peer Connections', () => { - test('can connect securely to another peer and send data back and forth', async done => { - done() + test('can request a certificate from a ca peer', () => { + const csr = pkiA.createCSR('localhost', 'passphrase') + const certificate = pkiCA.handleCSR(csr) + expect(certificate).not.toEqual(undefined) + }) + + describe('Transport Layer Security', () => { + let tlsServerCredentials: TLSCredentials | undefined + let tlsClientCredentials: TLSCredentials | undefined + + beforeAll(() => { + // request certificates from CA for both pkiA and pkiB + // ==== PEER A ==== // + const csrA = pkiA.createCSR('localhost', 'passphrase') + pkiA.importCertificate(pkiCA.handleCSR(csrA)) + // ==== PEER B ==== // + const csrB = pkiB.createCSR('localhost', 'passphrase') + pkiB.importCertificate(pkiCA.handleCSR(csrB)) + + // pkiA will provide the server credentials and pkiB will provide the client credentials + tlsServerCredentials = pkiA.TLSServerCredentials + tlsClientCredentials = pkiB.TLSClientCredentials + }) + + test('can use certificates to create an mtls connection', done => { + // set up the mock server + const randomSecureMessage = `random-secure-message: ${randomString()}\n` + const server = http.createServer({ + key: tlsServerCredentials!.privateKey, + cert: tlsServerCredentials!.certificate, + ca: [tlsServerCredentials!.rootCertificate] + }, (req, res) => { + res.writeHead(200); + res.end(randomSecureMessage); + }).listen(0) + const serverAddress = server.address() + + const req = http.request({ + host: serverAddress.address, + port: serverAddress.port, + path: '/', + method: 'GET', + key: tlsClientCredentials!.privateKey, + cert: tlsClientCredentials!.certificate, + ca: [pkiCA!.RootCert] + }, (res) => { + res.on('data', (d) => { + expect(d).toEqual(randomSecureMessage) + done() + }); + }) + + req.on('error', (e) => { + expect(e).toBeUndefined() + done() + }); + + req.end() }) }) })