#!/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 # 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