Certificate Stores for Programming Languages & Common Tools
Certificate Stores for Programming Languages & Common Tools
The previous post covered OS-level trust stores. This one covers what most people actually get bitten by: language runtimes and tools that maintain their own private trust stores, ignoring the OS entirely.
If you’ve ever imported a corporate CA at the OS level and thought “great, done” — only to have pip install, mvn package, npm install, or curl from a Java app still fail — this post is for you.
The Landscape
flowchart TD
A[Runtime/Tool] --> B{Trust source?}
B -->|Own bundled CA list| C[Language runtimes]
B -->|Env variable override| D[CLI tools]
B -->|Config file| E[Package managers]
C --> C1[Java: JKS/PKCS12 cacerts]
C --> C2[Python: certifi]
C --> C3[Node.js: bundled Mozilla]
C --> C4[Go: falls back to OS]
C --> C5[.NET: OS store on Win/macOS]
C --> C6[Ruby: OpenSSL default]
C --> C7[PHP: cafile in php.ini]
D --> D1[curl: --cacert / CURL_CA_BUNDLE]
D --> D2[git: http.sslCAInfo]
D --> D3[wget: --ca-certificate]
E --> E1[npm: cafile / strict-ssl]
E --> E2[pip: cert config]
E --> E3[Maven: -Djavax.net.ssl.trustStore]
style A fill:#e1f5ff
style B fill:#fff3cd
style C fill:#d4edda
style D fill:#ffdac1
style E fill:#e6e6fa
Quick Reference: Where Each Runtime Looks
Runtime
Default trust source
Override env var / config
Java (JDK)
$JAVA_HOME/lib/security/cacerts (JKS/PKCS12)
-Djavax.net.ssl.trustStore=...
Python
certifi module bundle
REQUESTS_CA_BUNDLE, SSL_CERT_FILE, CURL_CA_BUNDLE
Node.js
Bundled Mozilla list
NODE_EXTRA_CA_CERTS=/path/to/ca.pem
Go
OS trust store (Linux/macOS/Windows)
SSL_CERT_FILE, SSL_CERT_DIR
.NET (Windows/macOS)
OS store
N/A (use OS store)
.NET (Linux)
OpenSSL / OS bundle
SSL_CERT_FILE, SSL_CERT_DIR
Ruby
OpenSSL default path
SSL_CERT_FILE
PHP
openssl.cafile in php.ini
curl.cainfo, openssl.cafile
curl
System ca-bundle
CURL_CA_BUNDLE, --cacert
git
Uses OpenSSL/schannel/OS
http.sslCAInfo, GIT_SSL_CAINFO
AWS CLI
Python certifi
AWS_CA_BUNDLE, --ca-bundle
kubectl
OS trust store
--certificate-authority
Terraform
OS trust store
Provider-specific
Docker (daemon)
/etc/docker/certs.d/<registry>/ca.crt
Per-registry
Java: cacerts (JKS / PKCS12)
Java maintains a keystore separate from the OS. It’s typically at $JAVA_HOME/lib/security/cacerts.
# Global
git config --global http.sslCAInfo /etc/ssl/certs/ca-certificates.crt
# Per-repo
git config http.sslCAInfo /etc/ssl/certs/ca-certificates.crt
# Env variableexport GIT_SSL_CAINFO=/etc/ssl/certs/ca-certificates.crt
# Windows: git-for-windows uses its own OpenSSL bundle at# C:\Program Files\Git\mingw64\ssl\certs\ca-bundle.crt# Append corporate CA there, OR switch to schannel:
git config --global http.sslBackend schannel
On Windows, git config --global http.sslBackend schannel makes Git use the Windows Certificate Store — a cleaner corporate-network fix than editing bundled OpenSSL certs.
These flags disable TLS verification entirely — useful only to prove “yes, the issue is trust store configuration” before applying the proper fix. Never leave them on in production or CI.
Decision Tree: Where to Import a CA?
flowchart TD
A[Which apps need to trust the CA?] --> B{All apps on this machine?}
B -->|Yes| C[Import into OS trust store]
B -->|No: specific runtime| D{Which runtime?}
D -->|Java| E[keytool into cacerts or per-app truststore]
D -->|Python| F[Set REQUESTS_CA_BUNDLE / SSL_CERT_FILE]
D -->|Node| G[Set NODE_EXTRA_CA_CERTS]
D -->|.NET/Go| H[Import into OS store]
D -->|Docker registry| I[/etc/docker/certs.d/reg/ca.crt]
C --> J[Also do runtime-specific import if runtime ignores OS]
style A fill:#e1f5ff
style B fill:#fff3cd
style D fill:#fff3cd
style C fill:#d4edda
style J fill:#f8d7da
Anti-Patterns
Disabling TLS verification (-k, NODE_TLS_REJECT_UNAUTHORIZED=0, PYTHONHTTPSVERIFY=0) as a “permanent fix” — this reintroduces the man-in-the-middle risk the CA was supposed to prevent.
Appending CA to certifi/cacert.pem in a shared image — wiped on next pip install --upgrade certifi.
Hard-coding CA paths per-language when the OS already has it — prefer SSL_CERT_FILE/NODE_EXTRA_CA_CERTS pointing to the OS bundle.
Editing bundled files inside a JDK/Node install path without documenting it — the next upgrade removes it.
Using http.sslVerify=false in .gitconfig — trust everything, forever. Instead, configure http.sslCAInfo or switch backend to schannel on Windows.