You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/************************************* * bun run mTLS-demo.ts * Given Output: Server response: Client certificate required * * OR NODE * * bun build mTLS-demo.ts --outfile mtls-node.js --target=node * node mtls-node.js * Expected output: Server response: Hello, mutual TLS world! * * Demonstrates how to: * - Generate a CA (self-signed) * - Generate Server + Client certs * - Include SANs (Subject Alt Names) for localhost and 0.0.0.0 * - Spin up an mTLS Node server * - Make a request using the client cert *************************************/import*asforgefrom"node-forge";import*ashttpsfrom"https";import{IncomingMessage,ServerResponse}from"http";/** * The shape of our returned certs object. */interfaceMTLSCerts{ca: {cert: string;// PEM-encoded CA certkey: string;// PEM-encoded CA private key};server: {cert: string;// PEM-encoded Server certkey: string;// PEM-encoded Server private key};client: {cert: string;// PEM-encoded Client certkey: string;// PEM-encoded Client private key};}/** * Generates an RSA key pair (2048 bits). */functiongenerateKeyPair(): forge.pki.KeyPair{returnforge.pki.rsa.generateKeyPair({bits: 2048,e: 0x10001,});}/** * Certificate options interface for clarity. */interfaceCertificateOptions{commonName: string;serialNumber: string;issuerCert?: forge.pki.Certificate;issuerKey?: forge.pki.PrivateKey;daysValid?: number;isCA?: boolean;/** * Optional Subject Alternative Names to include in the cert. * Defaults to DNS:commonName if none provided. */altNames?: forge.pki.CertificateExtensionSubjectAltName[];}/** * Creates an X.509 certificate. * If no issuer cert/key provided, it becomes self-signed (for CA). */functioncreateCertificate(keyPair: forge.pki.KeyPair,options: CertificateOptions): forge.pki.Certificate{const{
commonName,
serialNumber,
issuerCert,
issuerKey,
daysValid =365,
isCA =false,
altNames,}=options;constcert=forge.pki.createCertificate();cert.publicKey=keyPair.publicKey;cert.serialNumber=serialNumber;// Validityconstnow=newDate();cert.validity.notBefore=newDate(now.getTime()-5*60*1000);// 5 min in the pastcert.validity.notAfter=newDate(now.getTime()+daysValid*24*60*60*1000);// If no issuer, self-signed for CA. Otherwise, use issuer's subject.constissuerAttrs=issuerCert
? issuerCert.subject.attributes
: [{name: "commonName",value: commonName},{name: "countryName",value: "US"},{shortName: "ST",value: "CA"},{name: "organizationName",value: "MyOrg Inc."},];cert.setSubject([{name: "commonName",value: commonName},{name: "countryName",value: "US"},{shortName: "ST",value: "CA"},{name: "organizationName",value: "MyOrg Inc."},]);cert.setIssuer(issuerAttrs);// Default altNames to DNS:commonName if not specifiedconstaltNamesToUse=altNames?.length
? altNames
: [{type: 2,value: commonName}];// Basic constraints, key usage, alt namescert.setExtensions([{name: "basicConstraints",cA: isCA,critical: isCA,},{name: "keyUsage",digitalSignature: true,keyEncipherment: true,dataEncipherment: true,keyCertSign: isCA,cRLSign: isCA,},{name: "subjectAltName",altNames: altNamesToUse,},]);// Sign with issuer key (or self if CA)constsigningKey=issuerKey||keyPair.privateKey;cert.sign(signingKey,forge.md.sha256.create());returncert;}/** * Generates a self-signed CA, plus server/client certificates * in PEM format. Returns them in a single object. */functiongenerateMTLSCerts(): MTLSCerts{// --- 1) CA (self-signed) ---constcaKeyPair=generateKeyPair();constcaCert=createCertificate(caKeyPair,{commonName: "MyRootCA",serialNumber: "01",isCA: true,daysValid: 3650,// 10-year CA});// --- 2) Server certificate (signed by CA) ---// Subject Alt Names include server.example.com, localhost, 127.0.0.1, and 0.0.0.0constserverKeyPair=generateKeyPair();constserverCert=createCertificate(serverKeyPair,{commonName: "server.example.com",serialNumber: "02",issuerCert: caCert,issuerKey: caKeyPair.privateKey,daysValid: 825,// ~2 yearsaltNames: [{type: 2,value: "server.example.com"},// DNS{type: 2,value: "localhost"},// DNS{type: 7,ip: "127.0.0.1"},// IP{type: 7,ip: "0.0.0.0"},// IP],});// --- 3) Client certificate (signed by CA) ---constclientKeyPair=generateKeyPair();constclientCert=createCertificate(clientKeyPair,{commonName: "client.example.com",serialNumber: "03",issuerCert: caCert,issuerKey: caKeyPair.privateKey,daysValid: 825,});return{ca: {cert: forge.pki.certificateToPem(caCert),key: forge.pki.privateKeyToPem(caKeyPair.privateKey),},server: {cert: forge.pki.certificateToPem(serverCert),key: forge.pki.privateKeyToPem(serverKeyPair.privateKey),},client: {cert: forge.pki.certificateToPem(clientCert),key: forge.pki.privateKeyToPem(clientKeyPair.privateKey),},};}// -------------------------------------------------------------------// MAIN: Start an HTTPS server requiring client cert (mTLS).// Then make a request with our "client cert" to show it works.// -------------------------------------------------------------------asyncfunctionmain(){// Generate PEM-encoded CA/server/client certsconst{ ca, server, client }=generateMTLSCerts();constport=8443;// Create the server with mTLSconstserverOptions: https.ServerOptions={key: server.key,cert: server.cert,// The server trusts our CA (so it’ll trust the client cert).ca: ca.cert,// Require client cert, otherwise reject.requestCert: true,rejectUnauthorized: true,};// Create the HTTPS serverconsthttpsServer=https.createServer(serverOptions,(req: IncomingMessage,res: ServerResponse)=>{if(req.socket.authorized){res.writeHead(200);res.end("Hello, mutual TLS world!\n");}else{// If client cert wasn't validatedres.writeHead(401);res.end("Client certificate required.\n");}});httpsServer.listen(port,()=>{console.log(`mTLS server is listening on https://localhost:${port}`);// Once the server is up, make a request using the client cert/key.constclientOptions: https.RequestOptions={hostname: "localhost",
port,method: "GET",path: "/",// Provide the client's key & certkey: client.key,cert: client.cert,// The client trusts the same CAca: ca.cert,rejectUnauthorized: true,};constreq=https.request(clientOptions,(res)=>{letdata="";res.on("data",(chunk)=>(data+=chunk));res.on("end",()=>{console.log("Server response:",data);// Cleanly close the server for this demohttpsServer.close();});});req.on("error",(err)=>{console.error("Client request error:",err);httpsServer.close();});req.end();});}// Run the main functionmain();
On Bun, should be able to start the server and request to it , getting the message "Server response: Hello, mutual TLS world!"
What do you see instead?
Getting "Server response: Client certificate required". It seems like req.socket.authorized is always undefined.
Additional information
Was thinking of doing a server management utility, and mTLS would be great for internal cross-server communication, combined with the single binary option Bun has.
The text was updated successfully, but these errors were encountered:
What version of Bun is running?
1.1.43+76800b049
What platform is your computer?
Darwin 23.6.0 arm64 arm
What steps can reproduce the bug?
mTLS-demo.ts:
package.json:
What is the expected behavior?
On Bun, should be able to start the server and request to it , getting the message "Server response: Hello, mutual TLS world!"
What do you see instead?
Getting "Server response: Client certificate required". It seems like
req.socket.authorized
is always undefined.Additional information
Was thinking of doing a server management utility, and mTLS would be great for internal cross-server communication, combined with the single binary option Bun has.
The text was updated successfully, but these errors were encountered: