Files
fn_registry/bash/functions/cybersecurity/inspect_ssl_cert.sh
T
egutierrez 7c3f01c9eb feat: add bash cybersecurity audit and hardening functions
12 funciones Bash del dominio cybersecurity: auditoria de red y servicios
(analyze_dns, audit_http_headers, inspect_ssl_cert, list_active_connections,
enumerate_subdomains, geolocate_ip), auditoria de sistema (audit_ssh_config,
check_firewall, detect_suspicious_users), y utilidades crypto (encrypt_file,
generate_password, verify_file_hash). Dominio nuevo en bash/functions/.
2026-04-12 13:54:25 +02:00

170 lines
6.0 KiB
Bash

#!/usr/bin/env bash
# inspect_ssl_cert
# ----------------
# Inspecciona el certificado SSL/TLS de un host: sujeto, emisor, fechas de validez,
# SANs, cadena de confianza y versiones de TLS aceptadas (detecta TLS 1.0/1.1 inseguros).
#
# USO (directo):
# inspect_ssl_cert <host[:puerto]>
# inspect_ssl_cert example.com
# inspect_ssl_cert example.com:8443
#
# Depende de: openssl, timeout
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../shell/bash_colors.sh"
source "$SCRIPT_DIR/../shell/bash_log.sh"
bash_colors
bash_log_init
# ─── Constantes ───────────────────────────────────────────────────────────────
_SSL_WARN_DAYS=30
# ─── Funciones puras ──────────────────────────────────────────────────────────
_ssl_parse_host_port() {
local input="$1"
if [[ "$input" =~ ^(.+):([0-9]+)$ ]]; then
echo "${BASH_REMATCH[1]} ${BASH_REMATCH[2]}"
else
echo "${input} 443"
fi
}
_ssl_days_until_expiry() {
local expiry_str="$1"
local expiry_epoch
expiry_epoch="$(date -d "$expiry_str" +%s 2>/dev/null || echo 0)"
local now_epoch
now_epoch="$(date +%s)"
echo $(( (expiry_epoch - now_epoch) / 86400 ))
}
# ─── Funciones de efecto ──────────────────────────────────────────────────────
_ssl_fetch_subject() {
local host="$1"
local port="$2"
echo | timeout 10 openssl s_client -connect "${host}:${port}" -servername "$host" 2>/dev/null \
| openssl x509 -noout -subject -issuer 2>/dev/null
}
_ssl_fetch_dates() {
local host="$1"
local port="$2"
echo | timeout 10 openssl s_client -connect "${host}:${port}" -servername "$host" 2>/dev/null \
| openssl x509 -noout -dates 2>/dev/null
}
_ssl_fetch_san() {
local host="$1"
local port="$2"
echo | timeout 10 openssl s_client -connect "${host}:${port}" -servername "$host" 2>/dev/null \
| openssl x509 -noout -ext subjectAltName 2>/dev/null \
| grep -oE 'DNS:[^,]+' | sed 's/DNS://g' | tr '\n' ' '
}
_ssl_fetch_chain() {
local host="$1"
local port="$2"
echo | timeout 10 openssl s_client -connect "${host}:${port}" -servername "$host" \
-showcerts 2>/dev/null \
| grep -E "^(subject|issuer)=" | sed 's/^/ /'
}
_ssl_check_tls_version() {
local host="$1"
local port="$2"
local proto="$3"
local label="$4"
if echo | timeout 5 openssl s_client -connect "${host}:${port}" \
-servername "$host" "${proto}" 2>/dev/null | grep -q "Cipher"; then
echo -e " ${RED}[x]${NC} ${label} -- soportado (inseguro)"
else
echo -e " ${GREEN}[ok]${NC} ${label} no soportado"
fi
}
# ─── Punto de entrada ─────────────────────────────────────────────────────────
inspect_ssl_cert() {
local input="$1"
if [[ -z "$input" ]]; then
error "inspect_ssl_cert: se requiere un host como argumento (ej: example.com o example.com:8443)" >&2
return 1
fi
if ! command -v openssl &>/dev/null; then
error "inspect_ssl_cert: 'openssl' no está instalado (sudo apt install openssl)" >&2
return 1
fi
local host port
read -r host port <<< "$(_ssl_parse_host_port "$input")"
info "Conectando a ${host}:${port}..."
echo ""
local subj_issuer
subj_issuer="$(_ssl_fetch_subject "$host" "$port")"
if [[ -z "$subj_issuer" ]]; then
error "inspect_ssl_cert: no se pudo obtener el certificado. ¿El host está disponible?" >&2
return 1
fi
local subject issuer
subject="$(echo "$subj_issuer" | grep ^subject | cut -d= -f2- | xargs)"
issuer="$(echo "$subj_issuer" | grep ^issuer | cut -d= -f2- | xargs)"
local dates
dates="$(_ssl_fetch_dates "$host" "$port")"
local not_before not_after
not_before="$(echo "$dates" | grep notBefore | cut -d= -f2)"
not_after="$(echo "$dates" | grep notAfter | cut -d= -f2)"
local days_left
days_left="$(_ssl_days_until_expiry "$not_after")"
local sans
sans="$(_ssl_fetch_san "$host" "$port")"
echo -e "${PURPLE}════════ Certificado SSL/TLS ════════════════════${NC}"
echo -e " ${CYAN}Sujeto:${NC} ${subject}"
echo -e " ${CYAN}Emisor:${NC} ${issuer}"
echo -e " ${CYAN}Válido desde:${NC} ${not_before}"
echo -e " ${CYAN}Válido hasta:${NC} ${not_after}"
echo ""
if [[ $days_left -le 0 ]]; then
echo -e " ${RED}[x] CERTIFICADO EXPIRADO${NC}"
elif [[ $days_left -le $_SSL_WARN_DAYS ]]; then
echo -e " ${YELLOW}[!] Expira en ${days_left} días -- renovar pronto${NC}"
else
echo -e " ${GREEN}[ok] Válido -- expira en ${days_left} días${NC}"
fi
echo ""
echo -e " ${CYAN}SANs:${NC}"
echo "$sans" | tr ' ' '\n' | grep -v '^$' | while IFS= read -r san; do
echo -e " ${GREEN}*${NC} ${san}"
done
echo ""
echo -e "${PURPLE}════════ Cadena de confianza ════════════════════${NC}"
_ssl_fetch_chain "$host" "$port"
echo ""
echo -e "${PURPLE}════════ Versiones TLS aceptadas ════════════════${NC}"
_ssl_check_tls_version "$host" "$port" "-tls1" "TLS 1.0"
_ssl_check_tls_version "$host" "$port" "-tls1_1" "TLS 1.1"
echo -e " ${GREEN}[ok]${NC} TLS 1.2 / 1.3 (estándar)"
echo -e "${PURPLE}═════════════════════════════════════════════════${NC}"
}
# Ejecutar si se llama directamente
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
inspect_ssl_cert "$@"
fi