summaryrefslogtreecommitdiffstats
path: root/test/ca-fingerprint.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--test/ca-fingerprint.js126
1 files changed, 126 insertions, 0 deletions
diff --git a/test/ca-fingerprint.js b/test/ca-fingerprint.js
new file mode 100644
index 0000000..f71063f
--- /dev/null
+++ b/test/ca-fingerprint.js
@@ -0,0 +1,126 @@
+'use strict'
+
+const crypto = require('crypto')
+const https = require('https')
+const { test } = require('tap')
+const { Client, buildConnector } = require('..')
+const pem = require('https-pem')
+
+const caFingerprint = getFingerprint(pem.cert.toString()
+ .split('\n')
+ .slice(1, -1)
+ .map(line => line.trim())
+ .join('')
+)
+
+test('Validate CA fingerprint with a custom connector', t => {
+ t.plan(2)
+
+ const server = https.createServer(pem, (req, res) => {
+ res.setHeader('Content-Type', 'text/plain')
+ res.end('hello')
+ })
+
+ server.listen(0, function () {
+ const connector = buildConnector({ rejectUnauthorized: false })
+ const client = new Client(`https://localhost:${server.address().port}`, {
+ connect (opts, cb) {
+ connector(opts, (err, socket) => {
+ if (err) {
+ cb(err)
+ } else if (getIssuerCertificate(socket).fingerprint256 !== caFingerprint) {
+ socket.destroy()
+ cb(new Error('Fingerprint does not match'))
+ } else {
+ cb(null, socket)
+ }
+ })
+ }
+ })
+
+ t.teardown(() => {
+ client.close()
+ server.close()
+ })
+
+ client.request({
+ path: '/',
+ method: 'GET'
+ }, (err, data) => {
+ t.error(err)
+
+ data.body
+ .resume()
+ .on('end', () => {
+ t.pass()
+ })
+ })
+ })
+})
+
+test('Bad CA fingerprint with a custom connector', t => {
+ t.plan(2)
+
+ const server = https.createServer(pem, (req, res) => {
+ res.setHeader('Content-Type', 'text/plain')
+ res.end('hello')
+ })
+
+ server.listen(0, function () {
+ const connector = buildConnector({ rejectUnauthorized: false })
+ const client = new Client(`https://localhost:${server.address().port}`, {
+ connect (opts, cb) {
+ connector(opts, (err, socket) => {
+ if (err) {
+ cb(err)
+ } else if (getIssuerCertificate(socket).fingerprint256 !== 'FO:OB:AR') {
+ socket.destroy()
+ cb(new Error('Fingerprint does not match'))
+ } else {
+ cb(null, socket)
+ }
+ })
+ }
+ })
+
+ t.teardown(() => {
+ client.close()
+ server.close()
+ })
+
+ client.request({
+ path: '/',
+ method: 'GET'
+ }, (err, data) => {
+ t.equal(err.message, 'Fingerprint does not match')
+ t.equal(data.body, undefined)
+ })
+ })
+})
+
+function getIssuerCertificate (socket) {
+ let certificate = socket.getPeerCertificate(true)
+ while (certificate && Object.keys(certificate).length > 0) {
+ // invalid certificate
+ if (certificate.issuerCertificate == null) {
+ return null
+ }
+
+ // We have reached the root certificate.
+ // In case of self-signed certificates, `issuerCertificate` may be a circular reference.
+ if (certificate.fingerprint256 === certificate.issuerCertificate.fingerprint256) {
+ break
+ }
+
+ // continue the loop
+ certificate = certificate.issuerCertificate
+ }
+ return certificate
+}
+
+function getFingerprint (content, inputEncoding = 'base64', outputEncoding = 'hex') {
+ const shasum = crypto.createHash('sha256')
+ shasum.update(content, inputEncoding)
+ const res = shasum.digest(outputEncoding)
+ return res.toUpperCase().match(/.{1,2}/g).join(':')
+}