Corporate Proxy SSL Fix

Corporate networks use SSL inspection (MITM proxies) that break certificate verification in WSL, git, curl, and other tools. This guide covers identification and resolution.

Understanding SSL Inspection

What Happens

  1. Your HTTPS request goes to corporate proxy (Cisco Umbrella, Zscaler, etc.)

  2. Proxy decrypts your traffic, inspects it, re-encrypts with its own certificate

  3. Windows trusts the proxy CA (pushed via GPO)

  4. WSL/Linux tools don’t have the proxy CA → SSL verification fails

Identifying SSL Inspection

# From WSL - check who issued the certificate
curl -vI https://github.com 2>&1 | grep -E "(issuer|subject)"

# If you see corporate names instead of real CA, you're being inspected:
# BAD:  issuer: O=Cisco; CN=Cisco Umbrella Secondary SubCA
# GOOD: issuer: C=US, O=DigiCert Inc, CN=DigiCert
# From PowerShell - check certificate chain
$url = "https://github.com"
$request = [System.Net.WebRequest]::Create($url)
$request.GetResponse() | Out-Null
$cert = $request.ServicePoint.Certificate
$cert.Issuer

Common Corporate Proxies

Vendor Subject Pattern Notes

Cisco Umbrella

CN=Cisco Umbrella

Cloud-based DNS/proxy

Zscaler

CN=Zscaler

Cloud security gateway

Palo Alto

CN=Palo Alto

NGFW SSL decryption

Fortinet/FortiGate

CN=Fortinet

UTM SSL inspection

Blue Coat / Symantec

CN=Blue Coat or CN=Symantec

Legacy proxy appliances

McAfee Web Gateway

CN=McAfee

Web gateway proxy

Fix: Export and Import CA

Step 1: Find the Proxy CA in Windows

# Search for common proxy CA patterns
$proxyPatterns = "Umbrella|Zscaler|Palo Alto|Fortinet|BlueCoat|McAfee|Corporate|Proxy"
Get-ChildItem Cert:\LocalMachine\Root | Where-Object \{
    $_.Subject -match $proxyPatterns -or $_.Issuer -match $proxyPatterns
} | Format-Table Subject, Issuer, NotAfter

# If nothing found, list all and look manually
Get-ChildItem Cert:\LocalMachine\Root | Sort-Object Subject | Format-Table Subject

Step 2: Export to PEM

# Replace "Umbrella" with your proxy CA name
$caName = "Umbrella"
$outputPath = "C:\temp\corporate-ca.crt"

$cert = Get-ChildItem Cert:\LocalMachine\Root | Where-Object \{ $_.Subject -match $caName }

if ($cert) \{
    $pem = "-----BEGIN CERTIFICATE-----`n" +
           [Convert]::ToBase64String($cert.RawData, 'InsertLineBreaks') +
           "`n-----END CERTIFICATE-----"
    [System.IO.File]::WriteAllText($outputPath, $pem)
    Write-Host "Exported to $outputPath"
    Get-Content $outputPath | Select-Object -First 3
} else \{
    Write-Host "Certificate not found for pattern: $caName" -ForegroundColor Red
}

Step 3: Import to WSL

Arch Linux

sudo cp /mnt/c/temp/corporate-ca.crt /etc/ca-certificates/trust-source/anchors/
sudo update-ca-trust

Ubuntu/Debian

sudo cp /mnt/c/temp/corporate-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

RHEL/Fedora

sudo cp /mnt/c/temp/corporate-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust

Step 4: Verify

# Test SSL verification
curl -vI https://github.com 2>&1 | grep -E "(SSL|verify)"

# Should show: SSL certificate verify ok
# NOT: unable to get local issuer certificate

One-Liner Scripts

PowerShell: Export Cisco Umbrella CA

$cert = Get-ChildItem Cert:\LocalMachine\Root | Where-Object \{ $_.Subject -match "Umbrella" }; [System.IO.File]::WriteAllText("C:\temp\corp-ca.crt", "-----BEGIN CERTIFICATE-----`n" + [Convert]::ToBase64String($cert.RawData, 'InsertLineBreaks') + "`n-----END CERTIFICATE-----")

Bash (Arch): Import and Update

sudo cp /mnt/c/temp/corp-ca.crt /etc/ca-certificates/trust-source/anchors/ && sudo update-ca-trust

Combined (Copy-Paste Ready)

Run in PowerShell first:

$cert = Get-ChildItem Cert:\LocalMachine\Root | ? \{ $_.Subject -match "Umbrella" }
[IO.File]::WriteAllText("C:\temp\corp-ca.crt", "-----BEGIN CERTIFICATE-----`n$([Convert]::ToBase64String($cert.RawData, 'InsertLineBreaks'))`n-----END CERTIFICATE-----")

Then in WSL (Arch):

sudo cp /mnt/c/temp/corp-ca.crt /etc/ca-certificates/trust-source/anchors/ && sudo update-ca-trust && curl -sI https://github.com | head -1

Application-Specific Fixes

Git

# Check if git uses custom CA bundle
git config --global --get http.sslCAInfo

# Option 1: Unset to use system trust store
git config --global --unset http.sslCAInfo

# Option 2: Append corporate CA to git's bundle
cat /mnt/c/temp/corp-ca.crt >> $(git config --global --get http.sslCAInfo)

# Option 3: Point git to system bundle
git config --global http.sslCAInfo /etc/ssl/certs/ca-certificates.crt

Node.js / npm

# Set system CA bundle
export NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt

# Or add to .bashrc/.zshrc
echo 'export NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt' >> ~/.zshrc

Python / pip

# Use system certificates
export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt

# Or configure pip
pip config set global.cert /etc/ssl/certs/ca-certificates.crt

curl

# Should work after system trust store update
# If not, specify CA bundle explicitly
curl --cacert /etc/ssl/certs/ca-certificates.crt https://example.com

Troubleshooting

Certificate Not Found in Windows Store

# Check intermediate CA store too
Get-ChildItem Cert:\LocalMachine\CA | Where-Object \{ $_.Subject -match "Umbrella|Zscaler|Proxy" }

# Check user store
Get-ChildItem Cert:\CurrentUser\Root | Where-Object \{ $_.Subject -match "Umbrella|Zscaler|Proxy" }

# List ALL certificates to find it
Get-ChildItem Cert:\LocalMachine\Root | Select-Object Subject | Sort-Object Subject | Out-GridView

Still Failing After Import

# Verify certificate was added
ls -la /etc/ca-certificates/trust-source/anchors/

# Check if trust store was updated
trust list | grep -i umbrella

# Force rebuild of CA bundle
sudo update-ca-trust force-enable
sudo update-ca-trust extract

Multiple Proxy CAs

Some environments have multiple proxy CAs (root + intermediate). Export all of them:

# Find all related certs
$certs = Get-ChildItem Cert:\LocalMachine\Root, Cert:\LocalMachine\CA |
    Where-Object \{ $_.Subject -match "Umbrella" -or $_.Issuer -match "Umbrella" }

# Export all to single bundle
$bundle = ""
foreach ($cert in $certs) \{
    $bundle += "-----BEGIN CERTIFICATE-----`n"
    $bundle += [Convert]::ToBase64String($cert.RawData, 'InsertLineBreaks')
    $bundle += "`n-----END CERTIFICATE-----`n`n"
}
[System.IO.File]::WriteAllText("C:\temp\corp-ca-bundle.crt", $bundle)
Write-Host "Exported $($certs.Count) certificates"

Emergency Workaround

Only use as temporary measure while proper fix is implemented.
# Disable SSL verification for specific host (INSECURE)
git config --global http.https://git.sr.ht.sslVerify false

# Disable for all (VERY INSECURE - DO NOT USE IN PRODUCTION)
# git config --global http.sslVerify false  # DON'T DO THIS

Automation Script

Save this as fix-corporate-ssl.ps1:

#Requires -RunAsAdministrator
param(
    [string]$CAPattern = "Umbrella|Zscaler",
    [string]$OutputPath = "C:\temp\corporate-ca.crt"
)

$cert = Get-ChildItem Cert:\LocalMachine\Root | Where-Object \{ $_.Subject -match $CAPattern } | Select-Object -First 1

if (-not $cert) \{
    Write-Error "No certificate found matching: $CAPattern"
    Write-Host "Available certificates:"
    Get-ChildItem Cert:\LocalMachine\Root | Select-Object Subject | Sort-Object Subject
    exit 1
}

$pem = "-----BEGIN CERTIFICATE-----`n" +
       [Convert]::ToBase64String($cert.RawData, 'InsertLineBreaks') +
       "`n-----END CERTIFICATE-----"

New-Item -ItemType Directory -Path (Split-Path $OutputPath) -Force | Out-Null
[System.IO.File]::WriteAllText($OutputPath, $pem)

Write-Host "SUCCESS: Exported '$($cert.Subject)' to $OutputPath" -ForegroundColor Green
Write-Host ""
Write-Host "Next steps (run in WSL):" -ForegroundColor Yellow
Write-Host "  sudo cp /mnt/c/temp/corporate-ca.crt /etc/ca-certificates/trust-source/anchors/"
Write-Host "  sudo update-ca-trust"