SSL/TLS Certificate Operations

The Path to Mastery: Progression Framework

Understanding Your Journey

Mastering TLS/SSL operations is essential for every security engineer. This framework guides you from basic certificate inspection to advanced PKI architecture and troubleshooting.


Level 1: Padawan (Beginner) - "What’s This Certificate Thing?"

Time investment: 2-4 hours

Goal: Understand certificates and basic inspection

Core competencies:

# Basic TLS connection test
openssl s_client -connect host:443
# Read certificate details
openssl x509 -in cert.pem -text -noout
# Check expiry dates
openssl x509 -in cert.pem -dates -noout
# Quick HTTPS check
curl -vI https://host
# Quick cert grab
echo | openssl s_client -connect host:443 2>&1 | head -20

Conceptual understanding:

  • What a certificate is (identity + public key)

  • Certificate vs private key

  • Why HTTPS matters

  • Self-signed vs CA-signed certificates

  • Basic trust model

Red flags you’re still at Padawan level:

  • You don’t know the difference between .pem, .crt, .cer, .key

  • "Certificate expired" errors confuse you

  • You don’t understand certificate chains

  • Self-signed certificate warnings scare you

Graduation criteria:

  • Can inspect any certificate and understand the output

  • Know how to check expiration dates

  • Understand why browsers warn about self-signed certs

  • Can troubleshoot basic "certificate invalid" errors


Level 2: Knight (Intermediate) - "I Can Debug TLS Issues"

Time investment: 4-8 hours beyond Padawan

You’ve mastered: Everything in Level 1

Core competencies:

# TLS connection debugging - your daily bread
echo | openssl s_client -connect 10.50.1.21:2484 2>&1 | head -30
# Full certificate chain inspection
echo | openssl s_client -connect host:443 -showcerts 2>&1
# Test specific TLS version
openssl s_client -connect host:443 -tls1_2
openssl s_client -connect host:443 -tls1_3
# Check certificate chain
openssl s_client -connect host:443 2>&1 | grep -E "depth|verify"
# Extract certificate from server
echo | openssl s_client -connect host:443 2>&1 | \
  sed -n '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' > cert.pem
# Verify certificate against CA
openssl verify -CAfile ca-bundle.crt server.crt
# Check if cert matches private key
openssl x509 -noout -modulus -in cert.pem | md5sum
openssl rsa -noout -modulus -in key.pem | md5sum
# If hashes match, they're a pair

Conceptual understanding:

  • TLS handshake process (ClientHello, ServerHello, etc.)

  • Certificate chains (Root CA → Intermediate → Server)

  • SNI (Server Name Indication) and why it matters

  • Certificate validation process

  • Common TLS errors and what they mean

Key error codes you understand:

# Self-signed certificate
verify error:num=18:self-signed certificate

# Expired certificate
verify error:num=10:certificate has expired

# Hostname mismatch
verify error:num=62:Hostname mismatch

# Unknown CA
verify error:num=19:self-signed certificate in certificate chain

# Chain incomplete
verify error:num=21:unable to verify the first certificate

Graduation criteria:

  • Can debug any TLS connection failure

  • Understand certificate chain validation

  • Know how to bypass validation for testing (and why not to in production)

  • Can extract and inspect certificates from live servers


Level 3: Master (Advanced) - "I Architect TLS Solutions"

Time investment: 8-15 hours beyond Knight

You’ve mastered: Everything in Levels 1-2

Core competencies:

# Generate self-signed certificate with SANs
openssl req -x509 -newkey rsa:4096 -sha256 -days 365 \
  -nodes -keyout server.key -out server.crt \
  -subj "/CN=myserver.local" \
  -addext "subjectAltName=DNS:myserver.local,DNS:*.myserver.local,IP:10.0.0.1"
# Create CSR for CA signing
openssl req -new -newkey rsa:4096 -nodes \
  -keyout server.key -out server.csr \
  -subj "/C=US/ST=CA/L=LA/O=MyOrg/CN=server.example.com"
# Sign CSR with your own CA
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
  -CAcreateserial -out server.crt -days 365 -sha256
# Create PKCS12 bundle (for importing to Windows/browsers)
openssl pkcs12 -export -out server.p12 \
  -inkey server.key -in server.crt -certfile ca.crt
# Convert between formats
openssl x509 -in cert.der -inform DER -out cert.pem -outform PEM
openssl x509 -in cert.pem -inform PEM -out cert.der -outform DER
# Inspect PKCS12 bundle
openssl pkcs12 -in bundle.p12 -info -noout
# Test with client certificate
openssl s_client -connect host:443 \
  -cert client.crt -key client.key -CAfile ca.crt
# Full TLS debug with all details
openssl s_client -connect host:443 -state -debug -msg

Conceptual understanding:

  • PKI architecture and trust hierarchies

  • Certificate extensions (SAN, Key Usage, Extended Key Usage)

  • OCSP and CRL (certificate revocation)

  • mTLS (mutual TLS / client certificates)

  • Certificate pinning

  • HSM integration concepts

You can troubleshoot:

  • ISE EAP-TLS authentication failures

  • Oracle DataConnect certificate validation

  • LDAPS certificate issues

  • Internal CA trust problems

  • Certificate chain ordering issues

Graduation criteria:

  • Can design and implement PKI for an organization

  • Understand certificate lifecycle management

  • Can troubleshoot complex multi-tier TLS issues

  • Know security implications of each decision


Quick Reference Card

# ============================================================
#                SSL/TLS QUICK REFERENCE
# ============================================================

# CONNECTION TESTING
echo | openssl s_client -connect host:port 2>&1 | head -30
echo | openssl s_client -connect host:443 -servername hostname  # SNI
openssl s_client -connect host:443 -tls1_2                      # Force TLS 1.2
openssl s_client -connect host:443 -tls1_3                      # Force TLS 1.3
# CERTIFICATE INSPECTION
openssl x509 -in cert.pem -text -noout           # Full details
openssl x509 -in cert.pem -dates -noout          # Expiry dates
openssl x509 -in cert.pem -subject -noout        # Subject only
openssl x509 -in cert.pem -issuer -noout         # Issuer only
openssl x509 -in cert.pem -fingerprint -noout    # SHA1 fingerprint
# EXTRACT FROM SERVER
echo | openssl s_client -connect host:443 2>&1 | \
  sed -n '/BEGIN CERT/,/END CERT/p' > cert.pem
# VERIFY CHAIN
openssl verify -CAfile ca.crt server.crt
openssl s_client -connect host:443 2>&1 | grep "Verify return"
# KEY/CERT MATCHING
openssl x509 -noout -modulus -in cert.pem | md5sum
openssl rsa -noout -modulus -in key.pem | md5sum
# GENERATE SELF-SIGNED
openssl req -x509 -newkey rsa:4096 -sha256 -days 365 \
  -nodes -keyout key.pem -out cert.pem -subj "/CN=hostname"
# FORMAT CONVERSION
openssl x509 -in cert.der -inform DER -out cert.pem  # DER to PEM
openssl x509 -in cert.pem -outform DER -out cert.der # PEM to DER
openssl pkcs12 -export -out cert.p12 -inkey key.pem -in cert.pem
# COMMON PORTS
443   - HTTPS
636   - LDAPS
993   - IMAPS
995   - POP3S
8443  - Alt HTTPS
2484  - Oracle TCPS (ISE DataConnect)
9060  - ISE ERS API

1. TLS Connection Debugging

The Essential Debug Command

This is your go-to command for any TLS issue:

echo | openssl s_client -connect host:port 2>&1 | head -30

Real example - ISE DataConnect (Oracle):

❯ echo | timeout 5 openssl s_client -connect 10.50.1.21:2484 2>&1 | head -30
Connecting to 10.50.1.21
Can't use SSL_get_servername
depth=0 O=Cisco Systems, CN=ISE_ORACLE_ise-02.inside.domusdigitalis.dev
verify error:num=18:self-signed certificate
verify return:1
depth=0 O=Cisco Systems, CN=ISE_ORACLE_ise-02.inside.domusdigitalis.dev
verify return:1
CONNECTED(00000003)
---
Certificate chain
 0 s:O=Cisco Systems, CN=ISE_ORACLE_ise-02.inside.domusdigitalis.dev
   i:O=Cisco Systems, CN=ISE_ORACLE_ise-02.inside.domusdigitalis.dev
   a:PKEY: RSA, 2048 (bit); sigalg: sha256WithRSAEncryption
   v:NotBefore: Jan 13 02:01:10 2026 GMT; NotAfter: Jan 13 02:01:10 2027 GMT

What this tells you:

  • CONNECTED = TLS handshake succeeded

  • verify error:num=18 = Self-signed certificate (expected for internal services)

  • depth=0 = Server certificate (not CA)

  • Certificate details (CN, validity dates, key type)

Debug by TLS Version

# Test TLS 1.2 (most common)
openssl s_client -connect host:443 -tls1_2
# Test TLS 1.3 (modern)
openssl s_client -connect host:443 -tls1_3
# Force specific cipher
openssl s_client -connect host:443 -cipher 'AES256-SHA'
# List available ciphers
openssl ciphers -v

Debug with SNI (Server Name Indication)

# Required for servers hosting multiple certificates
openssl s_client -connect host:443 -servername www.example.com
# Without SNI, you might get wrong certificate
openssl s_client -connect shared-host:443                    # Wrong cert
openssl s_client -connect shared-host:443 -servername mysite.com  # Correct

Full Debug Mode

# Everything: states, messages, hex dumps
openssl s_client -connect host:443 -state -debug -msg 2>&1 | tee tls_debug.log
# Just the handshake states
openssl s_client -connect host:443 -state 2>&1 | grep "SSL_connect"

2. Certificate Inspection

Extract Certificate from Server

# One-liner to grab and save certificate
echo | openssl s_client -connect host:443 2>&1 | \
  sed -n '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' > server.crt
# With timeout for unresponsive servers
echo | timeout 5 openssl s_client -connect host:443 2>&1 | \
  sed -n '/BEGIN CERT/,/END CERT/p' > server.crt
# Get full chain (all certificates)
echo | openssl s_client -connect host:443 -showcerts 2>&1 | \
  sed -n '/BEGIN CERT/,/END CERT/p' > chain.pem

Inspect Certificate Details

# Human-readable full output
openssl x509 -in cert.pem -text -noout
# Key fields only
openssl x509 -in cert.pem -noout \
  -subject -issuer -dates -fingerprint
# Subject Alternative Names (SANs)
openssl x509 -in cert.pem -noout -text | grep -A1 "Subject Alternative Name"
# Check if wildcard
openssl x509 -in cert.pem -noout -subject | grep '\*'

Check Expiration

# Show expiry dates
openssl x509 -in cert.pem -dates -noout
# Check if expired (returns 0 if valid)
openssl x509 -in cert.pem -checkend 0
# Check if expiring in 30 days
openssl x509 -in cert.pem -checkend 2592000
# Script: Check multiple certificates
for cert in *.pem; do
  echo -n "$cert: "
  if openssl x509 -in "$cert" -checkend 2592000 -noout 2>/dev/null; then
    echo "OK (valid > 30 days)"
  else
    openssl x509 -in "$cert" -enddate -noout
  fi
done

Verify Certificate/Key Match

# These hashes MUST match for cert+key to work together
openssl x509 -noout -modulus -in cert.pem | openssl md5
openssl rsa -noout -modulus -in key.pem | openssl md5
# One-liner check
cert_hash=$(openssl x509 -noout -modulus -in cert.pem | md5sum | cut -d' ' -f1)
key_hash=$(openssl rsa -noout -modulus -in key.pem 2>/dev/null | md5sum | cut -d' ' -f1)
[[ "$cert_hash" == "$key_hash" ]] && echo "MATCH" || echo "MISMATCH"

3. Certificate Generation

Self-Signed Certificate (Quick)

# One-liner for testing
openssl req -x509 -newkey rsa:4096 -sha256 -days 365 \
  -nodes -keyout server.key -out server.crt \
  -subj "/CN=myserver.local"

Self-Signed with SANs (Production)

# Modern browsers require SANs, not just CN
openssl req -x509 -newkey rsa:4096 -sha256 -days 365 \
  -nodes -keyout server.key -out server.crt \
  -subj "/C=US/ST=CA/L=LA/O=MyOrg/CN=server.example.com" \
  -addext "subjectAltName=DNS:server.example.com,DNS:*.server.example.com,IP:10.0.0.1" \
  -addext "keyUsage=digitalSignature,keyEncipherment" \
  -addext "extendedKeyUsage=serverAuth"

Create CSR for CA Signing

# Generate key and CSR
openssl req -new -newkey rsa:4096 -nodes \
  -keyout server.key -out server.csr \
  -subj "/C=US/ST=California/L=Los Angeles/O=MyOrganization/OU=IT/CN=server.example.com"
# With SANs (requires config file or -addext)
openssl req -new -newkey rsa:4096 -nodes \
  -keyout server.key -out server.csr \
  -subj "/CN=server.example.com" \
  -addext "subjectAltName=DNS:server.example.com,DNS:www.server.example.com"
# Verify CSR contents
openssl req -in server.csr -text -noout

Create Your Own CA

# Step 1: Generate CA private key
openssl genrsa -aes256 -out ca.key 4096
# Step 2: Generate CA certificate (10 years)
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
  -out ca.crt \
  -subj "/C=US/ST=CA/L=LA/O=MyOrg/OU=PKI/CN=MyOrg Root CA"
# Step 3: Sign server CSR with CA
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
  -CAcreateserial -out server.crt -days 365 -sha256
# Step 4: Verify the signature
openssl verify -CAfile ca.crt server.crt

4. Format Conversion

Common Conversions

# PEM to DER
openssl x509 -in cert.pem -outform DER -out cert.der
# DER to PEM
openssl x509 -in cert.der -inform DER -outform PEM -out cert.pem
# PEM to PKCS12 (for Windows/browsers)
openssl pkcs12 -export -out cert.p12 \
  -inkey key.pem -in cert.pem -certfile ca.crt
# PKCS12 to PEM
openssl pkcs12 -in cert.p12 -out cert.pem -nodes
# PKCS7 to PEM
openssl pkcs7 -in cert.p7b -print_certs -out cert.pem
# Extract key from PKCS12
openssl pkcs12 -in cert.p12 -nocerts -nodes -out key.pem
# Extract cert from PKCS12
openssl pkcs12 -in cert.p12 -clcerts -nokeys -out cert.pem

File Format Reference

Extension Format Contains Use Case

.pem

Base64

Cert and/or key

Linux/Unix default

.crt

Base64

Certificate only

Common convention

.cer

Binary (DER) or Base64

Certificate only

Windows default

.key

Base64

Private key

Key files

.der

Binary

Certificate

Java/Windows

.p12/.pfx

Binary PKCS12

Cert + key + chain

Windows import

.p7b

PKCS7

Cert chain (no key)

CA bundles

.csr

Base64

Certificate request

CA signing


5. Troubleshooting Common Errors

Error: Self-Signed Certificate

verify error:num=18:self-signed certificate

Cause: Server presents self-signed certificate (no CA chain)

Solution for testing:

# Accept any certificate (TESTING ONLY)
curl -k https://host
openssl s_client -connect host:443  # Still connects, just warns
# Python
import ssl
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

Production solution: Import CA certificate to trust store

Error: Certificate Has Expired

verify error:num=10:certificate has expired

Check expiry:

echo | openssl s_client -connect host:443 2>&1 | openssl x509 -noout -dates

Solution: Renew the certificate

Error: Unable to Verify First Certificate

verify error:num=21:unable to verify the first certificate

Cause: Incomplete certificate chain

Debug:

echo | openssl s_client -connect host:443 -showcerts 2>&1 | grep -E "depth|verify"

Solution: Configure server to send intermediate certificates

Error: Hostname Mismatch

verify error:num=62:Hostname mismatch

Check certificate hostnames:

echo | openssl s_client -connect host:443 2>&1 | \
  openssl x509 -noout -text | grep -A1 "Subject Alternative Name"

Solution: Use correct hostname or get certificate with correct SANs

Error: Connection Reset by Peer

Connection reset by peer

Possible causes:

  1. Wrong port

  2. Service not running

  3. Firewall blocking

  4. TLS version mismatch

  5. Client certificate required

Debug steps:

# Check if port is open
nc -zv host 443
# Try different TLS versions
openssl s_client -connect host:443 -tls1_2
openssl s_client -connect host:443 -tls1_3
# Check for client cert requirement
openssl s_client -connect host:443 2>&1 | grep -i "client"

6. Enterprise Integrations

Cisco ISE Certificate Inspection

# Admin GUI certificate (443)
echo | openssl s_client -connect ise.example.com:443 2>&1 | head -30
# ERS API certificate (9060)
echo | openssl s_client -connect ise.example.com:9060 2>&1 | head -30
# DataConnect (Oracle) certificate (2484)
echo | openssl s_client -connect ise.example.com:2484 2>&1 | head -30
# pxGrid certificate (8905)
echo | openssl s_client -connect ise.example.com:8905 2>&1 | head -30

LDAPS Certificate

# Test LDAPS connection
echo | openssl s_client -connect dc.example.com:636 2>&1 | head -30
# Extract AD certificate for trust store
echo | openssl s_client -connect dc.example.com:636 2>&1 | \
  sed -n '/BEGIN CERT/,/END CERT/p' > ad_ldaps.crt

pfSense/OPNsense

# Web GUI
echo | openssl s_client -connect firewall.local:443 2>&1 | head -30
# Check certificate expiry
echo | openssl s_client -connect firewall.local:443 2>&1 | \
  openssl x509 -noout -dates

7. Automation Scripts

Certificate Expiry Monitor

#!/bin/bash
# cert-monitor.sh - Check certificate expiry across hosts

HOSTS=(
  "ise.example.com:443"
  "ise.example.com:9060"
  "pfsense.local:443"
  "web.example.com:443"
)

WARN_DAYS=30
WARN_SECONDS=$((WARN_DAYS * 86400))

for hostport in "${HOSTS[@]}"; do
  cert=$(echo | timeout 5 openssl s_client -connect "$hostport" 2>/dev/null | \
    sed -n '/BEGIN CERT/,/END CERT/p')

  if [[ -z "$cert" ]]; then
    echo "[$hostport] ERROR: Could not retrieve certificate"
    continue
  fi

  expiry=$(echo "$cert" | openssl x509 -noout -enddate | cut -d= -f2)

  if echo "$cert" | openssl x509 -checkend $WARN_SECONDS -noout 2>/dev/null; then
    echo "[$hostport] OK - Expires: $expiry"
  else
    echo "[$hostport] WARNING - Expires: $expiry (< $WARN_DAYS days)"
  fi
done

Bulk Certificate Extraction

#!/bin/bash
# extract-certs.sh - Extract certificates from multiple hosts

HOSTS="$1"  # File with host:port per line
OUTDIR="${2:-./certs}"

mkdir -p "$OUTDIR"

while read -r hostport; do
  [[ -z "$hostport" || "$hostport" =~ ^# ]] && continue

  name=$(echo "$hostport" | tr ':' '_')

  echo "Extracting: $hostport"
  echo | timeout 5 openssl s_client -connect "$hostport" 2>/dev/null | \
    sed -n '/BEGIN CERT/,/END CERT/p' > "$OUTDIR/$name.pem"

  if [[ -s "$OUTDIR/$name.pem" ]]; then
    echo "  -> $OUTDIR/$name.pem"
    openssl x509 -in "$OUTDIR/$name.pem" -noout -subject -dates
  else
    echo "  -> FAILED"
    rm -f "$OUTDIR/$name.pem"
  fi
done < "$HOSTS"

8. Security Best Practices

Certificate Validation

Always validate in production:

# Verify chain against system CA store
openssl s_client -connect host:443 -CApath /etc/ssl/certs
# Verify against specific CA
openssl verify -CAfile /path/to/ca.crt server.crt

Key Security

# Generate key with proper permissions
(umask 077 && openssl genrsa -out key.pem 4096)
# Verify permissions
ls -la key.pem  # Should be -rw------- (600)
# Encrypt private key
openssl rsa -aes256 -in key.pem -out key.enc.pem

TLS Configuration Testing

# Test for weak ciphers
nmap --script ssl-enum-ciphers -p 443 host
# Test with testssl.sh
./testssl.sh https://host
# Check for SSLv3 (should fail)
openssl s_client -connect host:443 -ssl3 2>&1 | grep -i "handshake failure"

9. ISE Certificate Extraction & Installation

Extract Certificates from ISE

See for the recommended approach. Quick extraction:

DOMAIN="<domain>"
ISE_HOST="<ise-ip-or-fqdn>"
CERT_DIR="$HOME/.secrets/certs/$DOMAIN/ise"
mkdir -p "$CERT_DIR"
# ROOT-CA cert (port 443) - Admin/ERS/MnT/OpenAPI
# NOTE: Use -showcerts and extract the LAST cert (root CA), not the first (server cert)
echo | openssl s_client -connect "$ISE_HOST:443" -showcerts 2>&1 | \
  awk '/-----BEGIN CERTIFICATE-----/{buf=""} {buf=buf $0 ORS} /-----END CERTIFICATE-----/{last=buf} END{printf "%s", last}' > "$CERT_DIR/ROOT-CA.crt"
# DataConnect cert (port 2484 - Oracle TCPS)
echo | openssl s_client -connect "$ISE_HOST:2484" 2>&1 | \
  sed -n '/BEGIN CERT/,/END CERT/p' > "$CERT_DIR/dataconnect.crt"
# pxGrid cert (port 8910)
echo | openssl s_client -connect "$ISE_HOST:8910" 2>&1 | \
  sed -n '/BEGIN CERT/,/END CERT/p' > "$CERT_DIR/pxgrid.crt"
chmod 600 "$CERT_DIR"/*.crt

Verify Certificate Before Installing

# Check issuer and validity
openssl x509 -in ~/.secrets/certs/<domain>/ise/ROOT-CA.crt -noout -issuer -subject -dates
openssl x509 -in ~/.secrets/certs/<domain>/ise/dataconnect.crt -noout -issuer -subject -dates
# Check fingerprint
openssl x509 -in ~/.secrets/certs/<domain>/ise/dataconnect.crt -noout -fingerprint -sha256

Install Certificate

Option 1: System trust store (Arch Linux)

sudo cp ~/.secrets/certs/<domain>/ise/dataconnect.crt /etc/ca-certificates/trust-source/anchors/
sudo update-ca-trust

Option 2: System trust store (Debian/Ubuntu)

sudo cp ~/.secrets/certs/<domain>/ise/dataconnect.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

Option 3: netapi only (env var) - Recommended

Add to your network.env:

ISE_CA_CERT=~/.secrets/certs/<domain>/ise/ROOT-CA.crt
ISE_DATACONNECT_CA=~/.secrets/certs/<domain>/ise/dataconnect.crt
ISE_PXGRID_CA=~/.secrets/certs/<domain>/ise/pxgrid.crt

10. Certificate Storage for Programmatic Access

Directory Structure

Store CA certificates for programmatic access under ~/.secrets/certs/<domain>/<service>/:

~/.secrets/certs/
├── d000/                           # Home enterprise domain
│   ├── ise/                        # Cisco ISE service
│   │   ├── ROOT-CA.crt             # Admin/ERS/MnT/OpenAPI (port 443, 9060)
│   │   ├── dataconnect.crt         # Oracle TCPS (port 2484)
│   │   └── pxgrid.crt              # pxGrid controller (port 8910)
│   ├── pfsense/
│   │   └── api.crt                 # pfSense API
│   └── ldap/
│       └── dc01.crt                # Domain controller LDAPS
├── d001/                           # Work domain
│   ├── ise/
│   │   ├── ROOT-CA.crt
│   │   ├── dataconnect.crt
│   │   └── pxgrid.crt
│   └── ldap/
│       ├── dc01.crt
│       └── dc02.crt
└── README.md                       # Documentation

Naming convention: ~/.secrets/certs/<domain>/<service>/<name>.crt

  • domain - Environment identifier (d000=home, d001=work, etc.)

  • service - Service category (ise, pfsense, ldap, etc.)

  • name - Specific certificate (dataconnect, ers, admin, etc.)

Step-by-Step Setup (Copy-Paste)

Copy-paste each command one at a time.

Step 1: Set variables

DOMAIN="<your-domain>"      # e.g., d000, d001
ISE_HOST="<ise-ip-or-fqdn>" # e.g., 10.50.1.21, ise.example.com
CERT_DIR="$HOME/.secrets/certs/$DOMAIN/ise"

Step 2: Create directory

mkdir -p "$CERT_DIR"
chmod 700 "$CERT_DIR"

Step 3: Extract ROOT-CA cert (port 443)

Used by Admin GUI, ERS, MnT, OpenAPI. NOTE: The first cert is ISE’s server cert. We need the LAST cert (root CA) from the chain.

echo | timeout 5 openssl s_client -connect "$ISE_HOST:443" -showcerts 2>/dev/null | \
  awk '/-----BEGIN CERTIFICATE-----/{buf=""} {buf=buf $0 ORS} /-----END CERTIFICATE-----/{last=buf} END{printf "%s", last}' > "$CERT_DIR/ROOT-CA.crt"

Step 4: Verify ROOT-CA cert

openssl x509 -in "$CERT_DIR/ROOT-CA.crt" -noout -subject -dates

Step 5: Extract DataConnect cert (port 2484)

Self-signed Oracle cert for DataConnect.

echo | timeout 5 openssl s_client -connect "$ISE_HOST:2484" 2>/dev/null | \
  sed -n '/BEGIN CERT/,/END CERT/p' > "$CERT_DIR/dataconnect.crt"

Step 6: Verify DataConnect cert

openssl x509 -in "$CERT_DIR/dataconnect.crt" -noout -subject -dates

Step 7: Extract pxGrid cert (port 8910)

pxGrid controller certificate.

echo | timeout 5 openssl s_client -connect "$ISE_HOST:8910" 2>/dev/null | \
  sed -n '/BEGIN CERT/,/END CERT/p' > "$CERT_DIR/pxgrid.crt"

Step 8: Verify pxGrid cert

openssl x509 -in "$CERT_DIR/pxgrid.crt" -noout -subject -dates

Step 9: Set permissions

chmod 600 "$CERT_DIR"/*.crt
ls -la "$CERT_DIR"

Step 10: Update network.env

Add these lines to your domain’s network.env (replace <domain>):

ISE_CA_CERT=~/.secrets/certs/<domain>/ise/ROOT-CA.crt
ISE_DATACONNECT_CA=~/.secrets/certs/<domain>/ise/dataconnect.crt
ISE_PXGRID_CA=~/.secrets/certs/<domain>/ise/pxgrid.crt

Automated Setup Script

#!/bin/bash
# setup-ise-certs.sh - Extract and store ISE certificates for a domain
#
# Usage:
#   setup-ise-certs.sh <domain> <ise_host>        # Run all steps automatically
#   setup-ise-certs.sh -i <domain> <ise_host>     # Interactive mode (step-by-step)

# Parse flags
INTERACTIVE=false
if [[ "$1" == "-i" ]]; then
  INTERACTIVE=true
  shift
fi

DOMAIN="${1:?Usage: setup-ise-certs.sh [-i] <domain> <ise_host>}"
ISE_HOST="${2:?Usage: setup-ise-certs.sh [-i] <domain> <ise_host>}"
CERT_DIR="$HOME/.secrets/certs/$DOMAIN/ise"

# Helper for interactive mode
confirm() {
  if $INTERACTIVE; then
    echo
    read -p "$1 [Y/n] " -n 1 -r
    echo
    [[ $REPLY =~ ^[Yy]$ ]] || [[ -z $REPLY ]]
  else
    return 0
  fi
}

echo "=============================================="
echo "  ISE Certificate Setup - $DOMAIN"
echo "  Host: $ISE_HOST"
echo "  Mode: $($INTERACTIVE && echo 'Interactive' || echo 'Automatic')"
echo "=============================================="
echo

# Step 1: Create directory
echo "[Step 1] Create certificate directory"
echo "  Path: $CERT_DIR"
if confirm "Create directory?"; then
  mkdir -p "$CERT_DIR"
  chmod 700 "$CERT_DIR"
  echo "  Done"
else
  echo "  Skipped"
fi

# Step 2: Extract ROOT-CA cert
echo
echo "[Step 2] Extract ROOT-CA certificate (port 443)"
echo "  Used by: Admin GUI, ERS API (9060), MnT API, OpenAPI"
echo "  Note: Extracts the LAST cert in chain (root CA), not the server cert"
if confirm "Extract ROOT-CA.crt?"; then
  echo -n "  Connecting to $ISE_HOST:443... "
  if echo | timeout 5 openssl s_client -connect "$ISE_HOST:443" -showcerts 2>/dev/null | \
     awk '/-----BEGIN CERTIFICATE-----/{buf=""} {buf=buf $0 ORS} /-----END CERTIFICATE-----/{last=buf} END{printf "%s", last}' > "$CERT_DIR/ROOT-CA.crt" && \
     [ -s "$CERT_DIR/ROOT-CA.crt" ]; then
    echo "OK"
    echo "  Certificate details:"
    openssl x509 -in "$CERT_DIR/ROOT-CA.crt" -noout -subject -issuer -dates 2>/dev/null | sed 's/^/    /'
    echo "  Fingerprint:"
    openssl x509 -in "$CERT_DIR/ROOT-CA.crt" -noout -fingerprint -sha256 2>/dev/null | sed 's/^/    /'
  else
    echo "FAILED"
    rm -f "$CERT_DIR/ROOT-CA.crt"
  fi
else
  echo "  Skipped"
fi

# Step 3: Extract DataConnect cert
echo
echo "[Step 3] Extract DataConnect certificate (port 2484)"
echo "  Used by: ISE DataConnect (Oracle TCPS)"
echo "  Note: This is a separate self-signed Oracle certificate"
if confirm "Extract dataconnect.crt?"; then
  echo -n "  Connecting to $ISE_HOST:2484... "
  if echo | timeout 5 openssl s_client -connect "$ISE_HOST:2484" 2>/dev/null | \
     sed -n '/BEGIN CERT/,/END CERT/p' > "$CERT_DIR/dataconnect.crt" && \
     [ -s "$CERT_DIR/dataconnect.crt" ]; then
    echo "OK"
    echo "  Certificate details:"
    openssl x509 -in "$CERT_DIR/dataconnect.crt" -noout -subject -issuer -dates 2>/dev/null | sed 's/^/    /'
    echo "  Fingerprint:"
    openssl x509 -in "$CERT_DIR/dataconnect.crt" -noout -fingerprint -sha256 2>/dev/null | sed 's/^/    /'
  else
    echo "FAILED (DataConnect may not be enabled)"
    rm -f "$CERT_DIR/dataconnect.crt"
  fi
else
  echo "  Skipped"
fi

# Step 4: Extract pxGrid cert
echo
echo "[Step 4] Extract pxGrid certificate (port 8910)"
echo "  Used by: pxGrid controller"
if confirm "Extract pxgrid.crt?"; then
  echo -n "  Connecting to $ISE_HOST:8910... "
  if echo | timeout 5 openssl s_client -connect "$ISE_HOST:8910" 2>/dev/null | \
     sed -n '/BEGIN CERT/,/END CERT/p' > "$CERT_DIR/pxgrid.crt" && \
     [ -s "$CERT_DIR/pxgrid.crt" ]; then
    echo "OK"
    echo "  Certificate details:"
    openssl x509 -in "$CERT_DIR/pxgrid.crt" -noout -subject -issuer -dates 2>/dev/null | sed 's/^/    /'
    echo "  Fingerprint:"
    openssl x509 -in "$CERT_DIR/pxgrid.crt" -noout -fingerprint -sha256 2>/dev/null | sed 's/^/    /'
  else
    echo "FAILED (pxGrid may not be enabled)"
    rm -f "$CERT_DIR/pxgrid.crt"
  fi
else
  echo "  Skipped"
fi

# Step 5: Set permissions
echo
echo "[Step 5] Set file permissions"
if confirm "Set permissions (chmod 600)?"; then
  chmod 600 "$CERT_DIR"/*.crt 2>/dev/null
  echo "  Done"
else
  echo "  Skipped"
fi

# Summary
echo
echo "=============================================="
echo "  Summary"
echo "=============================================="
echo "Certificates in $CERT_DIR:"
ls -la "$CERT_DIR"/*.crt 2>/dev/null || echo "  (none)"
echo
echo "Add to your network.env:"
echo "  ISE_CA_CERT=~/.secrets/certs/$DOMAIN/ise/ROOT-CA.crt"
echo "  ISE_DATACONNECT_CA=~/.secrets/certs/$DOMAIN/ise/dataconnect.crt"
echo "  ISE_PXGRID_CA=~/.secrets/certs/$DOMAIN/ise/pxgrid.crt"

Integration with netapi

Add certificate paths to your domain’s network.env:

# ~/.secrets/environments/domains/<domain>/dev/network.env

# ISE Connection Settings
ISE_PAN_IP=10.50.1.20
ISE_MNT_IP=10.50.1.21
ISE_API_USER=ersadmin
ISE_API_PASS=<encrypted>

# Certificate Paths (enables proper SSL verification)
# ISE_CA_CERT: For Admin/ERS/MnT APIs (ROOT-CA cert)
# ISE_DATACONNECT_CA: For DataConnect (separate Oracle self-signed cert)
# ISE_PXGRID_CA: For pxGrid controller
ISE_CA_CERT=~/.secrets/certs/<domain>/ise/ROOT-CA.crt
ISE_DATACONNECT_CA=~/.secrets/certs/<domain>/ise/dataconnect.crt
ISE_PXGRID_CA=~/.secrets/certs/<domain>/ise/pxgrid.crt

How netapi Uses Certificates

For ERS/MnT/OpenAPI (netapi ise get-endpoints, netapi ise mnt sessions, etc.):

  • ISE_CA_CERT → CA or admin cert for server verification

  • Falls back to ISE_MTLS_CA if set (for mutual TLS environments)

For DataConnect (netapi ise dc test, netapi ise dc sessions, etc.):

  • ISE_DATACONNECT_CA → DataConnect’s self-signed Oracle cert

  • Does NOT fall back to ISE_CA_CERT (different certificate)

When CA certificate is configured:

  1. Loads certificate from specified path

  2. Enables proper SSL verification (CERT_REQUIRED)

  3. Connection validates against the CA cert

When CA certificate is NOT set:

  • INSECURE=true → Skips all SSL verification silently

  • Otherwise → Interactive prompt with options (y/n/s/c)

Interactive SSL Fallback

When SSL verification fails, netapi provides an interactive prompt:

$ netapi ise dc test

SSL verification failed for 10.50.1.21:2484
  DPY-6005: cannot connect to database
  Subject: O=Cisco Systems, CN=ISE_ORACLE_ise-02.inside.domusdigitalis.dev

Options:
  y - Proceed insecurely (this time only)
  n - Abort (default)
  s - Save INSECURE=true preference
  c - Extract certificate and save to ~/.secrets/certs/

Choice [y/n/s/c]: c

Certificate saved: /home/user/.secrets/certs/d000/ise/dataconnect.crt
  Subject: O=Cisco Systems, CN=ISE_ORACLE_ise-02.inside.domusdigitalis.dev
  Expires: Jan 13 02:01:10 2027 GMT

Add to your network.env:
  ISE_DATACONNECT_CA=/home/user/.secrets/certs/d000/ise/dataconnect.crt

Retrying with certificate verification...
✓ Connected to ISE DataConnect (10.50.1.21:2484)

Verify Certificate Before Trusting

Always inspect certificates before adding to trust:

# View certificate details
openssl x509 -in ~/.secrets/certs/d000/ise/dataconnect.crt -text -noout | head -30
# Check fingerprint matches what you expect
openssl x509 -in ~/.secrets/certs/d000/ise/dataconnect.crt -noout -fingerprint -sha256
# Compare with live server
echo | openssl s_client -connect 10.50.1.21:2484 2>&1 | \
  openssl x509 -noout -fingerprint -sha256

Certificate Renewal Workflow

When ISE certificates are renewed:

#!/bin/bash
# renew-ise-certs.sh - Update stored certificates after ISE renewal

DOMAIN="${1:-d000}"
ISE_HOST="${2:-10.50.1.21}"
CERT_DIR="$HOME/.secrets/certs/$DOMAIN/ise"

echo "=== Renewing ISE certificates for $DOMAIN ==="

# Backup existing certs
mkdir -p "$CERT_DIR/backup"
cp "$CERT_DIR"/*.crt "$CERT_DIR/backup/" 2>/dev/null

# Extract new certs (admin cert covers ERS/MnT/OpenAPI, dataconnect is separate)
for port_name in "443:admin" "2484:dataconnect"; do
  port="${port_name%%:*}"
  name="${port_name##*:}"

  echo -n "Extracting $name (port $port)... "
  if echo | timeout 5 openssl s_client -connect "$ISE_HOST:$port" 2>/dev/null | \
     sed -n '/BEGIN CERT/,/END CERT/p' > "$CERT_DIR/$name.crt.new" && \
     [ -s "$CERT_DIR/$name.crt.new" ]; then

    # Compare old and new
    if [ -f "$CERT_DIR/$name.crt" ]; then
      old_fp=$(openssl x509 -in "$CERT_DIR/$name.crt" -noout -fingerprint -sha256 2>/dev/null)
      new_fp=$(openssl x509 -in "$CERT_DIR/$name.crt.new" -noout -fingerprint -sha256)

      if [ "$old_fp" = "$new_fp" ]; then
        echo "unchanged"
        rm "$CERT_DIR/$name.crt.new"
      else
        echo "UPDATED"
        mv "$CERT_DIR/$name.crt.new" "$CERT_DIR/$name.crt"
        echo "  Old: $old_fp"
        echo "  New: $new_fp"
      fi
    else
      mv "$CERT_DIR/$name.crt.new" "$CERT_DIR/$name.crt"
      echo "created"
    fi
  else
    echo "FAILED"
    rm -f "$CERT_DIR/$name.crt.new"
  fi
done

echo
echo "Certificates in $CERT_DIR:"
ls -la "$CERT_DIR"/*.crt 2>/dev/null


Certificate mastery is essential for modern security operations. Practice these commands until they’re muscle memory.