The error verify error:num=20:unable to get local issuer certificate
typically occurs when OpenSSL cannot find the root certificate that signed the server's SSL certificate. This is common when connecting to LDAPS (LDAP over SSL) servers, especially in enterprise environments with internal Certificate Authorities (CAs).
When you tried:
openssl s_client -CAfile mycert.pem -connect the.server.edu:3269
The issue is that you likely saved the server's certificate (not the CA certificate) in mycert.pem
. For verification to work, you need the CA's root certificate that actually signed the server's certificate.
Here's how to properly handle this:
# First, get the server's certificate chain
openssl s_client -showcerts -connect the.server.edu:3269 < /dev/null > chain.pem
# Then extract the CA certificate (usually the last cert in the chain)
# You might need to manually identify which one is the CA cert
openssl x509 -in chain.pem -text | less
For testing purposes only, you can disable certificate verification:
openssl s_client -connect the.server.edu:3269 -verify 0
Warning: This bypasses security checks and should never be used in production.
For production systems, you should:
- Obtain the proper CA certificate from your IT department
- Add it to your system's trusted store or specify it explicitly:
openssl s_client -connect the.server.edu:3269 -CAfile /path/to/company-ca.crt
To better understand the certificate chain:
openssl s_client -showcerts -connect the.server.edu:3269 < /dev/null
This will show you all certificates presented by the server, helping you identify which CA certificate is missing.
For scripting purposes, you might want to handle this programmatically. Here's a Python example using the ssl
module:
import ssl
import socket
context = ssl.create_default_context()
context.load_verify_locations('/path/to/ca-certificates.crt')
with socket.create_connection(('the.server.edu', 3269)) as sock:
with context.wrap_socket(sock, server_hostname='the.server.edu') as ssock:
print(ssock.version())
When establishing an LDAPS connection to Active Directory, the error verify error:num=20:unable to get local issuer certificate
typically indicates that OpenSSL cannot find the root certificate needed to verify the server's certificate chain. This is fundamentally a trust chain validation problem.
The common misconception is that providing the server's certificate directly via -CAfile
should suffice:
openssl s_client -CAfile mycert.pem -connect the.server.edu:3269
This fails because:
- The server might be presenting intermediate certificates
- The certificate might be self-signed
- The complete chain might not be properly configured on the server
First, capture the full certificate chain:
openssl s_client -showcerts -connect the.server.edu:3269 /dev/null | \
awk '/BEGIN CERT/,/END CERT/{ if(/BEGIN CERT/){a++}; out="cert"a".pem"; print >out}'
Then verify each certificate level:
for cert in *.pem; do
openssl x509 -in $cert -noout -issuer -subject
done
Option 1: Trust the specific certificate (bypass verification)
openssl s_client -connect the.server.edu:3269 -verify_return_error -verify 0
Option 2: Build complete CA chain file
cat rootCA.pem intermediateCA.pem > fullchain.pem
openssl s_client -CAfile fullchain.pem -connect the.server.edu:3269
Option 3: Add certificate to system trust store
sudo cp mycert.pem /usr/local/share/ca-certificates/
sudo update-ca-certificates
For Active Directory environments:
- Check if Schannel is enforcing specific protocols
- Verify the certificate has proper SAN entries for LDAP service
- Consider using
ldapsearch
with explicit trust options:
ldapsearch -H ldaps://the.server.edu:3269 -x -ZZ \
-o tls_certfile=/path/to/cacert.pem
Here's a robust verification script:
#!/bin/bash
SERVER="the.server.edu"
PORT=3269
# Get certificate chain
openssl s_client -connect ${SERVER}:${PORT} -showcerts /dev/null > chain.pem
# Extract individual certificates
csplit -f cert- chain.pem '/-----BEGIN CERTIFICATE-----/' '{*}'
# Verify chain
for cert in cert-*; do
echo "Verifying $cert:"
openssl verify -CAfile <(cat cert-* | grep -v "$(cat $cert)") $cert
done
For Windows environments using Schannel:
# PowerShell equivalent for testing
Test-NetConnection -ComputerName the.server.edu -Port 3269
$request = [System.Net.WebRequest]::Create("https://the.server.edu:3269")
$request.GetResponse()