DEV Community

Cover image for Never Type openssl x509 -text -noout Again: I Built a VS Code Extension for SSL/TLS Certificates
Juan Torchia
Juan Torchia

Posted on • Originally published at juanchi.dev

Never Type openssl x509 -text -noout Again: I Built a VS Code Extension for SSL/TLS Certificates

It was 11 PM, there was a production incident, and I had three terminals open trying to remember whether it was openssl x509 -text -noout -in cert.pem or openssl pkcs12 -info -in keystore.p12 -noout. The server was throwing TLS handshake failed and I needed to confirm in two seconds whether the certificate we'd deployed was the right one or if someone had accidentally pushed the staging cert.

That moment of silent rage where you want to scream but it's 11 PM and your family is asleep — you know the feeling, right?

That's when I decided I was going to build something so I'd never live through that again.

Twenty-something years staring at certificates in a black terminal

When I started managing servers at a hosting company back around 2007, SSL certificates were an expensive rarity that only big companies could afford. I saw them as mystical objects: weird binary files with extensions nobody really understood — .pem, .der, .p12, .pfx, .crt, .cer. The same thing with six different names depending on who generated them.

I learned to read them with openssl in the terminal the same way I learned everything back then: by breaking things in production and praying. Over time it became second nature. But it never stopped being a mess.

Then came Let's Encrypt in 2015, certificates got democratized, and suddenly every project has SSL. Automatic renewals, certificate chains, SANs with 40 domains, Java keystores for those enterprise Spring Boot projects... The volume of certificate files flowing through a modern workspace is brutal.

And every single time you need to inspect one, it's the same story: you leave VS Code, open a terminal, try to remember the command, Google it if you can't, run it, read a wall of text that takes up half your screen, and close the terminal. Flow destroyed.

The real problem isn't the command — it's the context switching

Think about it. When you're working on an application that consumes external services, validating certificates is a normal part of the development cycle. You're configuring mutual TLS to connect to a banking API, or debugging why the Java client doesn't trust the server's certificate, or you just want to verify that the .p12 the security team sent you has the right CN before throwing it into Kubernetes as a Secret.

The problem isn't that openssl is hard. The problem is that it pulls you out of context. You have the file right there in the VS Code explorer, and to see its contents you have to make a round trip to the terminal. It's like having to walk outside to check what's in the fridge.

Image files have built-in previews. PDFs too, with extensions. SVGs render inline. So why don't X.509 certificates?

How gmm.certview was born

The idea was simple: double-click a .pem and VS Code shows you everything. No terminal. No remembering flags. No context switching.

The stack was almost obvious to me: TypeScript because I'm in the VS Code ecosystem, and node-forge for the cryptographic parsing because it's the most complete and mature library in the Node.js ecosystem for handling PKI. I didn't want to depend on external binaries or system calls — I needed everything to work 100% offline, in airplane mode if necessary, and especially in corporate environments where installing anything requires three forms and two approvals.

The most technically interesting piece was VS Code's Custom Editor Provider. Instead of registering a command you trigger manually, the extension registers itself as the native editor for certain file types. VS Code asks it "hey, the user wants to open this .pem, do you handle it?" and the extension says yes and takes control.

// Registering a Custom Editor Provider in VS Code
// The 'viewType' must match what you declare in package.json
vscode.window.registerCustomEditorProvider(
  'gmm.certview.editor', // unique identifier for your editor
  new CertificateEditorProvider(context),
  {
    // Keeps the webview in memory even when it's not the active tab
    // Important so we don't re-parse the cert every time you switch tabs
    webviewOptions: { retainContextWhenHidden: true },
    // Allows multiple tabs to open the same file
    supportsMultipleEditorsPerDocument: false,
  }
);
Enter fullscreen mode Exit fullscreen mode

The provider receives the file contents as a Uint8Array and hands it to node-forge for parsing. Then it renders everything in a Webview — basically an HTML page running inside VS Code with restricted system access.

What you see when you open a certificate

Open a .pem, .crt, .cer or .der and instead of base64 text or incomprehensible binary, you get a panel with:

Subject and Issuer — who it is and who signed it, with the Distinguished Name fields cleanly separated (CN, O, OU, C, etc.) instead of one giant string like CN=api.company.com, O=Company Inc., C=US.

Validity dates with visual alerts — this was the first thing I implemented because it's what matters most during an incident. Green if it's valid, yellow if it expires in less than 30 days, red if it's already expired. No mental math required.

// Expiry alert logic
// node-forge returns dates as Date objects in cert.validity
function getCertificateStatus(notAfter: Date): 'valid' | 'expiring' | 'expired' {
  const now = new Date();
  const daysRemaining = Math.floor(
    (notAfter.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)
  );

  if (daysRemaining < 0) return 'expired';      // Red — already expired
  if (daysRemaining <= 30) return 'expiring';   // Yellow — watch out
  return 'valid';                               // Green — all good
}
Enter fullscreen mode Exit fullscreen mode

SHA-1 and SHA-256 fingerprints with a copy-to-clipboard button. How many times have I compared fingerprints character by character in a terminal to verify two certificates were the same. With the copy button, you paste it directly where you need it.

Public key — algorithm (RSA, ECDSA, EdDSA) and bit size. If someone sends you a 1024-bit RSA certificate in 2024, you see it immediately and send the request straight back.

X.509 extensions and SANs — Subject Alternative Names listed cleanly. When a certificate says it's valid for *.company.com and company.com and four internal services, you see it at a glance.

Supported formats: not just your everyday .pem

This is where it gets good for people working in enterprise environments.

PKCS#7 / .p7b — certificate chains. Each cert in the bundle appears in its own tab within the panel. Super useful for validating that the chain is complete before configuring Nginx or a load balancer.

PKCS#12 / .p12 / .pfx — password-protected keystores. The extension shows you a prompt, you enter the password, and it opens the contents: certificate, private key (shows type and size, not the key material — we're not animals), and the chain certificates. That's three separate commands with different flags in a terminal.

PKCS#10 CSRs — Certificate Signing Requests. You can verify that the CN and SANs in the CSR you're about to send to the CA are actually what you want before pulling the trigger.

CRLs — Certificate Revocation Lists. Less common, but when you need it, you need it.

Workspace sidebar panel — I added this later when I realized the problem wasn't just opening individual certs. Sometimes you want to see all the certificates in a project at once: which one expires first, if any are already expired. The sidebar scans the workspace and lists everything with visual status indicators.

No telemetry. For real.

I say this explicitly because I know it matters. In corporate environments — finance, healthcare, government — you can't use extensions that send data anywhere. Certificates are sensitive cryptographic material. They could be production certificates, internal service certificates, critical infrastructure certificates.

gmm.certview sends zero data to any server. All processing happens locally on your machine with node-forge. No analytics, no usage telemetry, nothing. The code is available to audit if your security team requires it.

It works in airplane mode. It works on corporate networks with restrictive proxies. It works in air-gapped virtual machines. If VS Code runs, the extension works.

Two-click installation

You can install X509 Certificate Utility directly from the marketplace:

https://marketplace.visualstudio.com/items?itemName=gmm.certview

Or from inside VS Code: Ctrl+Pext install gmm.certview → Enter. Done.

After installing, double-click any .pem, .crt, .cer, .p12, .pfx, .p7b or .csr file in the VS Code explorer. The viewer should open automatically. If for some reason VS Code opens it as text, right-click → "Reopen with" → "X509 Certificate Viewer".

The mistakes I made along the way

Mistake 1: underestimating encodings. I thought all .pem files were the same. They're not. There's PEM with PKCS#1, PKCS#8, with specific headers, with and without bag attributes when they come from an exported PKCS#12. I had to handle each variant separately and add fallbacks. If you find a file that doesn't parse correctly, send me the error (without the cert, obviously) and I'll add it.

Mistake 2: the Webview and Content Security Policy. VS Code has pretty strict restrictions on what you can do inside a Webview. The first version was throwing CSP errors in the developer console. I had to review all inline styles and scripts and move everything to local resources with the correct URIs using webview.asWebviewUri().

Mistake 3: assuming node-forge handles everything. For some edge cases with PKCS#12 files using more exotic algorithms, node-forge throws cryptic exceptions. I had to add more granular error handling and descriptive messages so the user understands what happened instead of seeing "Error: invalid asn1 encoding".

Why the X.509 standard exists and why it's this complicated

X.509 dates back to 1988. Yes, 1988 — the year it was published as part of the ITU-T X.500 standard for distributed directories. The internet we use today runs on an evolved version (v3, from 1996) with extensions that were added to support use cases nobody imagined in 1988: Subject Alternative Names for multiple domains, Extended Key Usage to distinguish server certs from code signing certs, OCSP for real-time revocation.

The zoo of file formats exists because every ecosystem did its own thing: OpenSSL popularized PEM (Privacy Enhanced Mail — yes, it was originally for encrypted emails). Java uses JKS and PKCS#12. Windows uses PFX. Each with its own conventions.

Understanding this helps explain why the tool had to support all those formats. It's not arbitrary — it's the reality of modern projects where completely different stacks coexist.

What's coming next

There are a few things on the roadmap that I'm genuinely excited about:

  • Trust chain validation: given a leaf certificate and a CA bundle, verify the chain is valid without leaving VS Code.
  • Certificate comparison: select two certs and see the differences highlighted. Super useful for verifying rotations.
  • Decoding unknown OID fields: there are proprietary X.509 extensions from some CAs that node-forge doesn't know about. I want to add a lookup table.
  • Java JKS support: technically I'd need to re-implement the JKS format in TypeScript or use a Java library via WASM. It's the most interesting challenge I have ahead of me.

If you use the extension and have feedback, open an issue in the repo or reach out directly. The edge cases I care most about are the ones from enterprise environments with unusual configurations — those are the ones that make a tool genuinely robust.

The next time you're in a production incident at 11 PM trying to remember the openssl command, I hope you can just double-click and keep going.

Install it: marketplace.visualstudio.com/items?itemName=gmm.certview

Top comments (0)