From 4b5bcf7846bce6dcda8273821ca7931031442cdb Mon Sep 17 00:00:00 2001 From: Josh Mock Date: Wed, 21 Aug 2024 10:52:49 -0500 Subject: [PATCH] Allow CA fingerprints in different formats --- src/connection/BaseConnection.ts | 9 +++++++++ src/connection/HttpConnection.ts | 5 +++-- src/connection/UndiciConnection.ts | 5 +++-- test/unit/http-connection.test.ts | 23 +++++++++++++++++++++++ test/unit/undici-connection.test.ts | 23 +++++++++++++++++++++++ 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/connection/BaseConnection.ts b/src/connection/BaseConnection.ts index dcdeefb..6104f3c 100644 --- a/src/connection/BaseConnection.ts +++ b/src/connection/BaseConnection.ts @@ -236,3 +236,12 @@ export function getIssuerCertificate (socket: TLSSocket): DetailedPeerCertificat } return certificate } + +export function isCaFingerprintMatch (cert1: string | null, cert2: string | null): boolean { + if (typeof cert1 === 'string' && typeof cert2 === 'string') { + const c1 = cert1.toLowerCase().replace(/:/g, '') + const c2 = cert2.toLowerCase().replace(/:/g, '') + return c1 === c2 + } + return cert1 === cert2 +} diff --git a/src/connection/HttpConnection.ts b/src/connection/HttpConnection.ts index 6e67b48..da30e41 100644 --- a/src/connection/HttpConnection.ts +++ b/src/connection/HttpConnection.ts @@ -32,7 +32,8 @@ import BaseConnection, { ConnectionRequestOptionsAsStream, ConnectionRequestResponse, ConnectionRequestResponseAsStream, - getIssuerCertificate + getIssuerCertificate, + isCaFingerprintMatch } from './BaseConnection' import { kCaFingerprint } from '../symbols' import { Readable as ReadableStream, pipeline } from 'stream' @@ -256,7 +257,7 @@ export default class HttpConnection extends BaseConnection { // Check if fingerprint matches /* istanbul ignore else */ - if (this[kCaFingerprint] !== issuerCertificate.fingerprint256) { + if (!isCaFingerprintMatch(this[kCaFingerprint], issuerCertificate.fingerprint256)) { onError(new Error('Server certificate CA fingerprint does not match the value configured in caFingerprint')) request.once('error', () => {}) // we need to catch the request aborted error return request.destroy() diff --git a/src/connection/UndiciConnection.ts b/src/connection/UndiciConnection.ts index 7606d6a..31ad8db 100644 --- a/src/connection/UndiciConnection.ts +++ b/src/connection/UndiciConnection.ts @@ -30,7 +30,8 @@ import BaseConnection, { ConnectionRequestOptionsAsStream, ConnectionRequestResponse, ConnectionRequestResponseAsStream, - getIssuerCertificate + getIssuerCertificate, + isCaFingerprintMatch } from './BaseConnection' import { Pool, buildConnector, Dispatcher } from 'undici' import { @@ -95,7 +96,7 @@ export default class Connection extends BaseConnection { // Check if fingerprint matches /* istanbul ignore else */ - if (caFingerprint !== issuerCertificate.fingerprint256) { + if (!isCaFingerprintMatch(caFingerprint, issuerCertificate.fingerprint256)) { socket.destroy() return cb(new Error('Server certificate CA fingerprint does not match the value configured in caFingerprint'), null) } diff --git a/test/unit/http-connection.test.ts b/test/unit/http-connection.test.ts index 7fef2f0..c06c43b 100644 --- a/test/unit/http-connection.test.ts +++ b/test/unit/http-connection.test.ts @@ -1133,6 +1133,29 @@ test('Check server fingerprint (success)', async t => { server.stop() }) +test('Check server fingerprint (different formats)', async t => { + t.plan(1) + + function handler (req: http.IncomingMessage, res: http.ServerResponse) { + res.end('ok') + } + + const [{ port, caFingerprint }, server] = await buildServer(handler, { secure: true }) + + let newCaFingerprint = caFingerprint.toLowerCase().replace(/:/g, '') + + const connection = new HttpConnection({ + url: new URL(`https://localhost:${port}`), + caFingerprint: newCaFingerprint, + }) + const res = await connection.request({ + path: '/hello', + method: 'GET' + }, options) + t.equal(res.body, 'ok') + server.stop() +}) + test('Check server fingerprint (failure)', async t => { t.plan(2) diff --git a/test/unit/undici-connection.test.ts b/test/unit/undici-connection.test.ts index 1999f79..c997cdf 100644 --- a/test/unit/undici-connection.test.ts +++ b/test/unit/undici-connection.test.ts @@ -991,6 +991,29 @@ test('Check server fingerprint (success)', async t => { server.stop() }) +test('Check server fingerprint (different formats)', async t => { + t.plan(1) + + function handler (_req: http.IncomingMessage, res: http.ServerResponse) { + res.end('ok') + } + + const [{ port, caFingerprint }, server] = await buildServer(handler, { secure: true }) + + let newCaFingerprint = caFingerprint.toLowerCase().replace(/:/g, '') + + const connection = new UndiciConnection({ + url: new URL(`https://localhost:${port}`), + caFingerprint: newCaFingerprint + }) + const res = await connection.request({ + path: '/hello', + method: 'GET' + }, options) + t.equal(res.body, 'ok') + server.stop() +}) + test('Check server fingerprint (failure)', async t => { t.plan(2)