Skip to content

Instantly share code, notes, and snippets.

@samloh84
Last active May 20, 2025 03:36
Show Gist options
  • Select an option

  • Save samloh84/e8eb15c6195d661ae57bf20a526360f3 to your computer and use it in GitHub Desktop.

Select an option

Save samloh84/e8eb15c6195d661ae57bf20a526360f3 to your computer and use it in GitHub Desktop.
Export all certificates in MacOS keychain stores for use with scripts that need a PEM file

πŸ” Trusting Corporate Root Certificates in Development Environments

If you're behind a corporate proxy or tunneling solution like ZScaler or Cloudflare WARP, you may encounter SSL issues like SELF_SIGNED_CERT_IN_CHAIN when running development tools, such as when you run commands like npm install or when your Node.js script attempts to make HTTPS calls.

Corporate proxy/tunneling solutions often perform HTTPS interception using a self-signed or internally-signed certificate. Typically, the IT or security team managing your device through MDM software will install this certificate in the operating system trust store. If the development tool does not follow the convention of trusting the root certificates in the operating system trust store, it needs to be explicitly configured to trust the corporate proxy/tunnelling solution self-signed or internally-signed certificate to avoid SSL verification errors.

If escaping the corporate proxy/tunnelling solution is not an option, you can extract these trusted certificates and explicitly configure your development tools to trust them.


πŸ“¦ Step 1: Export System Trusted Certificates to PEM

General

If you have the PEM file that is used by your corporate proxy/tunnelling solution, you can use that PEM file in Step 2.

Windows

Use the Export-Windows-Certificates.ps1 PowerShell script to ump your system’s root certificates into a PEM file at %USERPROFILE%\.certs\certs.pem

macOS

Use the export_macos_certificates.sh shell script to dump your system’s root certificates into a PEM file at ${HOME}/.certs/certs.pem


βš™οΈ Step 2: Configure Development Tools to Trust the Exported PEM File


βœ… Node.js

Node.js uses a root certificate trust store bundled with the Node.js runtime. You can configure an environment variable NODE_EXTRA_CA_CERTS to trust self-signed SSL certificates in a PEM file.

πŸ”— Reference: nodejs/node#4175

export NODE_EXTRA_CA_CERTS="${HOME}/.certs/certs.pem"

βœ… Use this when running npm install or Node-based scripts that make HTTPS requests.


βœ… Linux Shell Tools (curl, wget)

For curl:

export CURL_CA_BUNDLE="${HOME}/.certs/certs.pem"

For wget:

wget --ca-certificate="${HOME}/.certs/certs.pem" https://example.com

βœ… Useful when using CLI tools to pull packages or APIs behind a corporate proxy.


βœ… Python (requests, urllib3, pip)

If you are using the requests library, you can configure an environment variable REQUESTS_CA_BUNDLE to trust self-signed SSL certificates in a PEM file. You may also need to install the pip-system-certs library.

Using environment variable:

export REQUESTS_CA_BUNDLE="${HOME}/.certs/certs.pem"

In code:

import requests
import os

response = requests.get(
    "https://example.com",
    verify=f"{os.environ['HOME']}/.certs/certs.pem"
)

βœ… Also affects pip if REQUESTS_CA_BUNDLE is set.


βœ… Go (Golang)

Programmatically load certificates:

package main

import (
    "crypto/tls"
    "crypto/x509"
    "io/ioutil"
    "net/http"
    "os"
)

func main() {
    caCert, _ := ioutil.ReadFile(os.Getenv("HOME") + "/.certs/certs.pem")
    roots := x509.NewCertPool()
    roots.AppendCertsFromPEM(caCert)

    client := &http.Client{
        Transport: &http.Transport{
            TLSClientConfig: &tls.Config{RootCAs: roots},
        },
    }

    resp, _ := client.Get("https://example.com")
    defer resp.Body.Close()
}

βœ… Required only if you override the default http.Client. Go usually respects system CA roots unless running in Alpine or custom containers.


βœ… Java (JVM: Maven, Gradle, Spring, etc.)

Option 1: Import PEM into a Java keystore:

keytool -import -trustcacerts -file ~/.certs/certs.pem \
  -keystore ${JAVA_HOME}/lib/security/cacerts \
  -storepass changeit -alias corp-root -noprompt

Option 2: Use JAVA_OPTS:

export JAVA_OPTS="-Djavax.net.ssl.trustStore=${HOME}/.certs/cacerts -Djavax.net.ssl.trustStorePassword=changeit"

⚠️ Java tools like Maven and Gradle use their own trust stores. You must either import the certs or configure the trust store explicitly.


βœ… Ruby (net/http, open-uri, rest-client)

Environment variable:

export SSL_CERT_FILE="${HOME}/.certs/certs.pem"

In code:

require 'net/http'
require 'openssl'

uri = URI('https://example.com')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.ca_file = "#{ENV['HOME']}/.certs/certs.pem"

response = http.request(Net::HTTP::Get.new(uri))
puts response.body

βœ… Works with most Ruby HTTP clients.


βœ… Docker

Build-time (Dockerfile):

COPY certs.pem /usr/local/share/ca-certificates/certs.pem
RUN update-ca-certificates

Runtime (docker-compose):

services:
  myapp:
    build: .
    environment:
      NODE_EXTRA_CA_CERTS: /usr/local/share/ca-certificates/certs.pem

βœ… Ensure you update certificates inside containers too. Some base images (like Alpine) do not come with ca-certificates by default.


βœ… Git

git config --global http.sslCAInfo "${HOME}/.certs/certs.pem"

βœ… Ensures that git clone, git fetch, and other HTTPS actions work over corporate proxies.


πŸ“Œ Final Notes

  • πŸ” Regenerate certs.pem if your system trust store is updated (e.g., after certificate renewal).
  • πŸ” Do not share your PEM file publicly β€” it may contain internal CA certificates.
  • 🧰 Consider adding the environment variable exports to your shell init file (~/.bashrc, ~/.zshrc, etc.).
  • πŸ› οΈ You can bundle this into a dev bootstrap script for teams to avoid local SSL errors.
# Set up paths
$homeCertsDir = Join-Path $env:USERPROFILE ".certs"
$outputFile = Join-Path $homeCertsDir "certs.pem"
# Ensure ~/.certs directory exists
if (-Not (Test-Path $homeCertsDir)) {
New-Item -ItemType Directory -Path $homeCertsDir | Out-Null
}
Write-Host "Exporting all certificates from Windows trusted roots to: $outputFile"
# Clear or create the output file
if (Test-Path $outputFile) {
Remove-Item $outputFile
}
New-Item -ItemType File -Path $outputFile -Force | Out-Null
# Define certificate stores to extract from
$certStores = @("Cert:\LocalMachine\Root", "Cert:\CurrentUser\Root")
foreach ($store in $certStores) {
Write-Host "Processing store: $store"
Get-ChildItem -Path $store | ForEach-Object {
$cert = $_
$pem = @(
"-----BEGIN CERTIFICATE-----"
[System.Convert]::ToBase64String($cert.RawData, 'InsertLineBreaks')
"-----END CERTIFICATE-----"
""
) -join "`n"
Add-Content -Path $outputFile -Value $pem
}
}
Write-Host "`nDone. Certificates saved to: $outputFile"
#!/bin/bash
# Output PEM file
OUTPUT_FILE="~/.certs/macos-system-certificates.pem"
mkdir -p ~/.certs
echo "Exporting all certificates from macOS keychains to: $OUTPUT_FILE"
> "$OUTPUT_FILE"
# List of keychains to extract certs from
KEYCHAINS=(
"/System/Library/Keychains/SystemRootCertificates.keychain"
"/Library/Keychains/System.keychain"
"$HOME/Library/Keychains/login.keychain-db"
)
for KC in "${KEYCHAINS[@]}"; do
echo "Processing keychain: $KC"
security find-certificate -a -p "$KC" >> "$OUTPUT_FILE"
done
echo "Done. Certificates saved to: $OUTPUT_FILE"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment