diff --git a/build-root-cert.sh b/build-root-cert.sh index e72d279..f75d308 100644 --- a/build-root-cert.sh +++ b/build-root-cert.sh @@ -11,33 +11,40 @@ else exit 1 fi +# Флаг, управляющий запуском генерации OpenSSL +NEED_GENERATION=true + # ПРОВЕРКА НАЛИЧИЯ УЖЕ ГОТОВОГО КОРНЕВОГО СЕРТИФИКАТА if [ -f "root_ca.key" ] || [ -f "root_ca.crt" ]; then echo "Внимание: Файлы Корневого сертификата (root_ca.key/crt) УЖЕ СУЩЕСТВУЮТ!" echo "⚠️ ПЕРЕЗАПИСЬ КОРНЯ СЛОМАЕТ ДОВЕРИЕ КО ВСЕМ ВЫПУЩЕННЫМ РАНЕЕ ПРОМЕЖУТОЧНЫМ И ДОМЕННЫМ СЕРТИФИКАТАМ!" echo "" - read -p "Вы абсолютно уверены, что хотите перевыпустить Корневой ЦС? [y/N]: " overwrite_response + read -p "Вы хотите ПЕРЕВЫПУСТИТЬ и перезаписать Корневой ЦС? [y/N]: " overwrite_response overwrite_response=$(echo "$overwrite_response" | tr '[:upper:]' '[:lower:]') - # По умолчанию (Enter) или при любом ответе, кроме y/yes — выходим - if [[ ! "$overwrite_response" =~ ^(y|yes)$ ]]; then - echo "Генерация отменена. Текущий корневой сертификат сохранен." - exit 0 - fi + # Если пользователь ввел y или yes — генерируем заново + if [[ "$overwrite_response" =~ ^(y|yes)$ ]]; then echo "Продолжаем генерацию и перезапись Корневого ЦС..." + NEED_GENERATION=true + else + # Во всех остальных случаях (включая Enter) — ПРОПУСКАЕМ генерацию, но не выходим из скрипта + echo "Генерация пропущена. Используются существующие файлы сертификата." + NEED_GENERATION=false + fi fi # Root certificate (Root CA) -openssl req -x509 -nodes -new -sha512 -days "$CERT_DAYS" \ - -newkey rsa:4096 -keyout root_ca.key -out root_ca.crt \ - -extfile root_ca.ini -extensions "$ROOT_EXT_SECTION" \ - -subj "/C=${CERT_COUNTRY}/L=${CERT_LOCALITY}/O=${CERT_ORG}/CN=${ROOT_CN}" - +if [ "$NEED_GENERATION" = true ]; then + echo "Запуск OpenSSL для создания Корневого ЦС..." + openssl req -x509 -nodes -new -sha512 -days "$CERT_DAYS" \ + -newkey rsa:4096 -keyout root_ca.key -out root_ca.crt \ + -extfile root_ca.ini -extensions "$ROOT_EXT_SECTION" \ + -subj "/C=${CERT_COUNTRY}/L=${CERT_LOCALITY}/O=${CERT_ORG}/CN=${ROOT_CN}" +fi echo "" -# Запрашиваем подтверждение у пользователя (Y - да, n - нет) read -p "Хотите установить сертификат в систему как доверенный? [Y/n]: " response # Переводим ответ в нижний регистр для надежности @@ -47,15 +54,26 @@ response=$(echo "$response" | tr '[:upper:]' '[:lower:]') if [[ -z "$response" || "$response" =~ ^(y|yes)$ ]]; then echo "Установка сертификата..." + # Проверяем, есть ли вообще утилита update-ca-certificates в вашей Linux-системе + if ! command -v update-ca-certificates &> /dev/null; then + echo "❌ Ошибка: Утилита update-ca-certificates не найдена в этой системе!" + exit 1 + fi + + TARGET_DIR="/usr/local/share/ca-certificates" + + # Проверяем, запущен ли скрипт от root, чтобы использовать sudo при создании папки и копировании if [ "$EUID" -ne 0 ]; then - sudo cp root_ca.crt /usr/local/share/ca-certificates/root_ca.crt + sudo mkdir -p "$TARGET_DIR" + sudo cp root_ca.crt "$TARGET_DIR/root_ca.crt" sudo update-ca-certificates else - cp root_ca.crt /usr/local/share/ca-certificates/root_ca.crt + mkdir -p "$TARGET_DIR" + cp root_ca.crt "$TARGET_DIR/root_ca.crt" update-ca-certificates fi - echo "Сертификат успешно установлен!" + echo "✅ Сертификат успешно установлен в локальное Linux-окружение!" else echo "Установка отменена." fi diff --git a/check-all-certs.sh b/check-all-certs.sh new file mode 100644 index 0000000..ee55b00 --- /dev/null +++ b/check-all-certs.sh @@ -0,0 +1,110 @@ +#!/bin/bash + +# Цветовая разметка для терминала +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0;60m' # Сброс цвета + +# Массив со строго заданными именами ваших файлов +TARGET_CERTS=("root_ca.crt" "intermediate_ca.crt" "issuing.crt") + +echo -e "${BLUE}==================================================${NC}" +echo -e "${BLUE} АНАЛИЗ И ПРОВЕРКА СЕРТИФИКАТОВ ${NC}" +echo -e "${BLUE}==================================================${NC}" + +for cert in "${TARGET_CERTS[@]}"; do + # Если конкретного файла нет — просто идём к следующему + if [ ! -f "$cert" ]; then + continue + fi + + echo "" + echo -e "${YELLOW}--------------------------------------------------${NC}" + echo -e "Файл: ${GREEN}$cert${NC}" + echo -e "${YELLOW}--------------------------------------------------${NC}" + + # Извлекаем основные метаданные + SUBJECT=$(openssl x509 -in "$cert" -noout -subject | sed 's/subject=//') + ISSUER=$(openssl x509 -in "$cert" -noout -issuer | sed 's/issuer=//') + END_DATE=$(openssl x509 -in "$cert" -noout -enddate | sed 's/notAfter=//') + + echo -e "${BLUE}Имя (Subject):${NC} $SUBJECT" + echo -e "${BLUE}Издатель (Issuer):${NC} $ISSUER" + echo -e "${BLUE}Действует до:${NC} $END_DATE" + + # Проверяем тип сертификата (CA или конечный) + IS_CA=$(openssl x509 -in "$cert" -text -noout | grep -E "CA:TRUE|CA:FALSE") + if [[ "$IS_CA" == *"CA:TRUE"* ]]; then + # Вытаскиваем строку Basic Constraints и очищаем от пробелов + PATHLEN_LINE=$(openssl x509 -in "$cert" -text -noout | grep -A1 "Basic Constraints" | tail -n 1 | sed 's/^[ \t]*//') + + # Переводим технический текст на понятный русский язык + if [[ "$PATHLEN_LINE" == *"pathlen:0"* ]]; then + RU_PATHLEN=" [Выпуск CA недоступен]" + elif [[ "$PATHLEN_LINE" == *"pathlen:"* ]]; then + # Вырезаем цифру лимита (например, pathlen:1) + NUM=$(echo "$PATHLEN_LINE" | grep -o -E '[0-9]+') + RU_PATHLEN=" [Допустима цепочка СА из $NUM сертификатов]" + else + # Если параметр pathlen вообще отсутствует в Basic Constraints + RU_PATHLEN=" [Не имеет ограничений по выпуску CA]" + fi + + echo -e "${BLUE}Тип:${NC} Удостоверяющий центр (CA)$RU_PATHLEN" + + + # Вывод Name Constraints для промежуточных CA + CONSTRAINTS=$(openssl x509 -in "$cert" -text -noout | awk '/Permitted:/ {flag=1; next} /X509v3|Signature Algorithm:/ {flag=0} flag {print}' | sed 's/^[ \t]*//' | sed 's/^/ /') + if [ ! -z "$CONSTRAINTS" ]; then + echo -e "${BLUE}Ограничения имён (Name Constraints):${NC}" + echo "$CONSTRAINTS" + fi + else + echo -e "${BLUE}Тип:${NC} Конечный SSL-сертификат (End-Entity)" + + # Вывод SAN для конечного сертификата + SAN_DOMAINS=$(openssl x509 -in "$cert" -text -noout | awk '/Subject Alternative Name:/ {flag=1; next} /X509v3|Signature Algorithm:/ {flag=0} flag {print}' | sed 's/^[ \t]*//' | sed 's/, /\n/g' | sed 's/^/ /') + if [ ! -z "$SAN_DOMAINS" ]; then + echo -e "${BLUE}Защищаемые домены и IP (SAN):${NC}" + echo "$SAN_DOMAINS" + fi + fi +done + +echo "" +echo -e "${BLUE}==================================================${NC}" +echo -e "${BLUE} ПРОВЕРКА ЦЕПОЧКИ ВЕРИФИКАЦИИ ${NC}" +echo -e "${BLUE}==================================================${NC}" + +ROOT_FILE="root_ca.crt" +INTERMEDIATE_FILE="intermediate_ca.crt" +ISSUING_FILE="issuing.crt" + +# 1. Проверка Intermediate CA через Root CA +if [ -f "$ROOT_FILE" ] && [ -f "$INTERMEDIATE_FILE" ]; then + echo -n "Проверка Intermediate CA корневым (Root): " + VERIFY_INT=$(openssl verify -CAfile "$ROOT_FILE" "$INTERMEDIATE_FILE" 2>&1) + if [[ "$VERIFY_INT" == *"OK"* ]]; then + echo -e "${GREEN}УСПЕШНО (OK)${NC}" + else + echo -e "${RED}ОШИБКА\n$VERIFY_INT${NC}" + fi +fi + +# 2. Проверка конечного SSL (issuing.crt) БЕЗ БАНДЛА (через -untrusted) +if [ -f "$ROOT_FILE" ] && [ -f "$INTERMEDIATE_FILE" ] && [ -f "$ISSUING_FILE" ]; then + echo -n "Проверка конечного SSL полной цепочкой (Root+Int): " + VERIFY_ISS=$(openssl verify -CAfile "$ROOT_FILE" -untrusted "$INTERMEDIATE_FILE" "$ISSUING_FILE" 2>&1) + + if [[ "$VERIFY_ISS" == *"OK"* ]]; then + echo -e "${GREEN}УСПЕШНО (OK)${NC}" + else + echo -e "${RED}ОШИБКА${NC}" + echo "$VERIFY_ISS" + fi +fi + +echo -e "${BLUE}==================================================${NC}" + diff --git a/install-root-ca.ps1 b/install-root-ca.ps1 new file mode 100644 index 0000000..61bf3ef --- /dev/null +++ b/install-root-ca.ps1 @@ -0,0 +1,52 @@ +# install-root-ca.ps1 + +$IsAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) + +if (-not $IsAdmin) { + Write-Host "ERROR: SCRIPT MUST BE RUN AS ADMINISTRATOR!" -ForegroundColor Red + Write-Host "Please restart PowerShell as Administrator." -ForegroundColor Yellow + Exit +} + +$CertFile = "root_ca.crt" + +if (-not (Test-Path $CertFile)) { + Write-Host "Error: File $CertFile not found in current directory!" -ForegroundColor Red + Exit +} + +try { + # Получаем полный абсолютный путь Рє файлу сертификата + $FullCertPath = (Get-Item $CertFile).FullName + # Читаем сертификат РїРѕ полному пути + $Cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($FullCertPath) + $Thumbprint = $Cert.Thumbprint + $Subject = $Cert.Subject +} catch { + Write-Host "Error: Could not read certificate file $CertFile." -ForegroundColor Red + Write-Host $_.Exception.Message -ForegroundColor Yellow + Exit +} + +Write-Host "Checking root certificate..." -ForegroundColor Cyan +Write-Host "Certificate Name: $Subject" -ForegroundColor Gray +Write-Host "SHA1 Thumbprint: $Thumbprint" -ForegroundColor Gray + +$StorePath = "Cert:\LocalMachine\Root" +$TargetCert = Get-ChildItem -Path $StorePath | Where-Object { $_.Thumbprint -eq $Thumbprint } + +if ($TargetCert) { + Write-Host "[ OK ] Certificate is ALREADY installed in Windows Trusted Root Storage." -ForegroundColor Green +} else { + Write-Host "[ INFO ] Certificate not found. Installing..." -ForegroundColor Yellow + try { + $Store = New-Object System.Security.Cryptography.X509Certificates.X509Store("Root", "LocalMachine") + $Store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite) + $Store.Add($Cert) + $Store.Close() + Write-Host "[ SUCCESS ] Certificate successfully added to Trusted Roots!" -ForegroundColor Green + } catch { + Write-Host "[ ERROR ] Failed to install certificate." -ForegroundColor Red + Write-Host $_.Exception.Message -ForegroundColor Red + } +} \ No newline at end of file diff --git a/intermediate_ca.ini.example b/intermediate_ca.ini.example index ad4b7cf..22529e2 100644 --- a/intermediate_ca.ini.example +++ b/intermediate_ca.ini.example @@ -4,6 +4,12 @@ authorityKeyIdentifier = keyid:always,issuer basicConstraints = critical, CA:true, pathlen:0 keyUsage = critical, digitalSignature, cRLSign, keyCertSign -nameConstraints = critical, permit;DNS:ixrs.ru, permit;DNS:.ixrs.ru -# Разрешает домен ixrs.ru, localhost и локальные IP-адреса -nameConstraints = critical, permit;DNS:ixrs.ru, permit;DNS:.ixrs.ru, permit;DNS:localhost, permit;IP:127.0.0.1/255.255.255.255, permit;IP:0:0:0:0:0:0:0:1/128 +nameConstraints = critical, @name_constraints + +[ name_constraints ] +permitted;DNS.1 = ixrs.ru +permitted;DNS.2 = .ixrs.ru +permitted;DNS.3 = localhost + +permitted;IP.1 = 127.0.0.1/255.255.255.255 +permitted;IP.2 = ::1/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff