Table of Contents

Overview

LDAPS (LDAP over SSL/TLS) provides secure communication between LDAP clients and Active Directory Domain Controllers. This guide provides comprehensive procedures for validating and confirming LDAPS certificates across your Active Directory infrastructure using multiple methods and tools.

Key Validation Areas:

  • Certificate Installation: Verify certificates are properly installed on Domain Controllers
  • Certificate Chain Validation: Confirm complete certificate trust chain
  • LDAPS Connectivity: Test secure LDAP connections on port 636
  • Certificate Expiration: Monitor certificate validity periods
  • Cross-Platform Validation: Validate certificates from Windows, Linux, and other platforms

Prerequisites

System Requirements

  • Active Directory Environment: Functional AD domain with Domain Controllers
  • LDAPS Configuration: LDAPS enabled on Domain Controllers (port 636)
  • Certificate Infrastructure: Valid SSL certificates installed on DCs
  • Network Access: Connectivity to Domain Controllers on port 636
  • Administrative Privileges: Domain administrator or certificate management rights

Required Tools

  • PowerShell: Version 5.1 or later with AD and PKI modules
  • OpenSSL: For cross-platform certificate validation
  • certutil.exe: Windows certificate utility
  • ldp.exe: LDAP data interchange format directory exchange tool
  • Certificate Manager: certmgr.msc for certificate management

Security Considerations

  • Certificate Validation: Always verify certificate authenticity and trust chain
  • Network Security: Ensure secure transport of certificate validation traffic
  • Credential Protection: Use secure methods for authentication during testing
  • Monitoring: Log certificate validation activities for audit purposes

Method 1: PowerShell Certificate Validation

Comprehensive Domain Controller Certificate Check

Enhanced PowerShell Validation Script:

# LDAPS Certificate Validation Script
[CmdletBinding()]
param(
    [string[]]$DomainControllers,
    [string]$Domain = $env:USERDNSDOMAIN,
    [int]$Port = 636,
    [switch]$ExportCertificates,
    [string]$ExportPath = "C:\CertValidation"
)

function Test-LDAPSCertificate 
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$ComputerName,
        [int]$Port = 636,
        [switch]$ExportCertificate,
        [string]$ExportPath
    )
    
    try 
    {
        Write-Host "Validating LDAPS certificate for: $ComputerName" -ForegroundColor Cyan
        
        # Test LDAPS connectivity
        $TcpClient = New-Object System.Net.Sockets.TcpClient
        $ConnectTask = $TcpClient.ConnectAsync($ComputerName, $Port)
        $ConnectResult = $ConnectTask.Wait(5000)
        
        if (!$ConnectResult -or !$TcpClient.Connected) 
        {
            Write-Warning "Cannot connect to $ComputerName on port $Port"
            return $null
        }
        
        # Get SSL stream and certificate
        $SslStream = New-Object System.Net.Security.SslStream($TcpClient.GetStream())
        $SslStream.AuthenticateAsClient($ComputerName)
        
        $Certificate = $SslStream.RemoteCertificate
        $X509Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($Certificate)
        
        # Validate certificate chain
        $Chain = New-Object System.Security.Cryptography.X509Certificates.X509Chain
        $ChainValid = $Chain.Build($X509Certificate)
        
        # Calculate days until expiration
        $DaysUntilExpiry = ($X509Certificate.NotAfter - (Get-Date)).Days
        
        # Create result object
        $Result = [PSCustomObject]@{
            ComputerName = $ComputerName
            Subject = $X509Certificate.Subject
            Issuer = $X509Certificate.Issuer
            Thumbprint = $X509Certificate.Thumbprint
            NotBefore = $X509Certificate.NotBefore
            NotAfter = $X509Certificate.NotAfter
            DaysUntilExpiry = $DaysUntilExpiry
            IsValid = $ChainValid
            ChainStatus = $Chain.ChainStatus
            SANs = $X509Certificate.Extensions | Where-Object { $_.Oid.FriendlyName -eq "Subject Alternative Name" } | Select-Object -ExpandProperty Format -First 1
            KeySize = $X509Certificate.PublicKey.Key.KeySize
            Algorithm = $X509Certificate.SignatureAlgorithm
        }
        
        # Display results
        Write-Host "Certificate Details:" -ForegroundColor Green
        Write-Host "  Subject: $($X509Certificate.Subject)" -ForegroundColor White
        Write-Host "  Issuer: $($X509Certificate.Issuer)" -ForegroundColor White
        Write-Host "  Valid From: $($X509Certificate.NotBefore)" -ForegroundColor White
        Write-Host "  Valid Until: $($X509Certificate.NotAfter)" -ForegroundColor White
        Write-Host "  Days Until Expiry: $DaysUntilExpiry" -ForegroundColor $(if ($DaysUntilExpiry -lt 30) { "Red" } elseif ($DaysUntilExpiry -lt 90) { "Yellow" } else { "Green" })
        Write-Host "  Chain Valid: $ChainValid" -ForegroundColor $(if ($ChainValid) { "Green" } else { "Red" })
        
        if (!$ChainValid) 
        {
            Write-Warning "Certificate chain validation issues:"
            foreach ($Status in $Chain.ChainStatus) 
            {
                Write-Warning "  $($Status.Status): $($Status.StatusInformation)"
            }
        }
        
        # Export certificate if requested
        if ($ExportCertificate -and $ExportPath) 
        {
            if (!(Test-Path $ExportPath)) 
            {
                New-Item -Path $ExportPath -ItemType Directory -Force | Out-Null
            }
            
            $CertFile = Join-Path $ExportPath "$ComputerName.cer"
            [System.IO.File]::WriteAllBytes($CertFile, $X509Certificate.RawData)
            Write-Host "  Certificate exported to: $CertFile" -ForegroundColor Green
        }
        
        return $Result
    }
    catch 
    {
        Write-Error "Failed to validate certificate for $ComputerName : $($_.Exception.Message)"
        return $null
    }
    finally 
    {
        if ($SslStream) { $SslStream.Dispose() }
        if ($TcpClient) { $TcpClient.Dispose() }
        if ($Chain) { $Chain.Dispose() }
    }
}

function Get-DomainControllerCertificates 
{
    [CmdletBinding()]
    param(
        [string[]]$DomainControllers,
        [string]$Domain,
        [int]$Port = 636,
        [switch]$ExportCertificates,
        [string]$ExportPath
    )
    
    # Get Domain Controllers if not specified
    if (!$DomainControllers) 
    {
        try 
        {
            Write-Host "Discovering Domain Controllers..." -ForegroundColor Yellow
            $DomainControllers = Get-ADDomainController -Filter * | Select-Object -ExpandProperty HostName
            Write-Host "Found $($DomainControllers.Count) Domain Controllers" -ForegroundColor Green
        }
        catch 
        {
            Write-Error "Failed to discover Domain Controllers: $($_.Exception.Message)"
            return
        }
    }
    
    $Results = @()
    
    foreach ($DC in $DomainControllers) 
    {
        Write-Host "`n" + "="*50 -ForegroundColor Cyan
        $Result = Test-LDAPSCertificate -ComputerName $DC -Port $Port -ExportCertificate:$ExportCertificates -ExportPath $ExportPath
        if ($Result) 
        {
            $Results += $Result
        }
    }
    
    return $Results
}

# Main execution
Write-Host "LDAPS Certificate Validation Tool" -ForegroundColor Green
Write-Host "=================================" -ForegroundColor Green

# Create export directory if needed
if ($ExportCertificates -and !(Test-Path $ExportPath)) 
{
    New-Item -Path $ExportPath -ItemType Directory -Force | Out-Null
    Write-Host "Created export directory: $ExportPath" -ForegroundColor Green
}

# Run certificate validation
$ValidationResults = Get-DomainControllerCertificates -DomainControllers $DomainControllers -Domain $Domain -Port $Port -ExportCertificates:$ExportCertificates -ExportPath $ExportPath

if ($ValidationResults) 
{
    Write-Host "`n" + "="*50 -ForegroundColor Green
    Write-Host "VALIDATION SUMMARY" -ForegroundColor Green
    Write-Host "="*50 -ForegroundColor Green
    
    $ValidationResults | Format-Table ComputerName, Subject, DaysUntilExpiry, IsValid -AutoSize
    
    # Check for issues
    $ExpiringCerts = $ValidationResults | Where-Object { $_.DaysUntilExpiry -lt 90 }
    $InvalidCerts = $ValidationResults | Where-Object { !$_.IsValid }
    
    if ($ExpiringCerts) 
    {
        Write-Warning "Certificates expiring within 90 days:"
        $ExpiringCerts | Format-Table ComputerName, NotAfter, DaysUntilExpiry -AutoSize
    }
    
    if ($InvalidCerts) 
    {
        Write-Warning "Certificates with validation issues:"
        $InvalidCerts | Format-Table ComputerName, Subject, ChainStatus -AutoSize
    }
    
    if (!$ExpiringCerts -and !$InvalidCerts) 
    {
        Write-Host "✓ All certificates are valid and not expiring soon" -ForegroundColor Green
    }
}

# Example usage:
# .\Test-LDAPSCertificates.ps1 -ExportCertificates -ExportPath "C:\CertValidation"
# .\Test-LDAPSCertificates.ps1 -DomainControllers @("DC01.contoso.com", "DC02.contoso.com")

Method 2: OpenSSL Cross-Platform Validation

Basic OpenSSL Certificate Retrieval

Simple Certificate Download and Validation:

#!/bin/bash

# Basic LDAPS Certificate Validation Script
# Usage: ./validate-ldaps.sh <domain-controller> [port]

DC_HOST="${1:-dc01.contoso.com}"
LDAPS_PORT="${2:-636}"
OUTPUT_DIR="./certificates"

echo "Validating LDAPS certificate for: $DC_HOST:$LDAPS_PORT"

# Create output directory
mkdir -p "$OUTPUT_DIR"

# Test connectivity
if ! timeout 10 bash -c "</dev/tcp/$DC_HOST/$LDAPS_PORT" 2>/dev/null; then
    echo "ERROR: Cannot connect to $DC_HOST:$LDAPS_PORT"
    exit 1
fi

# Retrieve certificate chain
echo "Retrieving certificate chain..."
openssl s_client -showcerts -verify 5 -connect "$DC_HOST:$LDAPS_PORT" < /dev/null 2>/dev/null > "$OUTPUT_DIR/${DC_HOST}_chain.pem"

# Extract individual certificates
awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/{ if(/BEGIN/) {a++}; out="'$OUTPUT_DIR'/'$DC_HOST'_cert"a".pem"; print >out}' "$OUTPUT_DIR/${DC_HOST}_chain.pem"

# Verify each certificate
echo "Verifying certificates..."
for cert in "$OUTPUT_DIR/${DC_HOST}_cert"*.pem; do
    if [ -f "$cert" ]; then
        echo "Verifying: $(basename "$cert")"
        openssl verify -show_chain "$cert"
        echo "Certificate details:"
        openssl x509 -in "$cert" -noout -subject -issuer -dates -fingerprint
        echo "---"
    fi
done

Advanced OpenSSL Certificate Analysis

Comprehensive Certificate Validation Script:

#!/bin/bash

# Advanced LDAPS Certificate Validation Script
# Validates SSL certificates on Domain Controllers using OpenSSL

# Configuration
DOMAIN_CONTROLLERS=()
LDAPS_PORT=636
OUTPUT_DIR="./certificates"
VERBOSE=false

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color

# Function to display usage
show_usage() 
{
    cat <<EOF
Usage: $0 [OPTIONS]
Options:
  -h, --help              Show this help message
  -d, --domain-controller Add domain controller (can be used multiple times)
  -p, --port             LDAPS port (default: 636)
  -o, --output-dir       Directory to save certificates (default: ./certificates)
  -v, --verbose          Enable verbose output
  --discover             Auto-discover domain controllers

Examples:
  $0 -d dc01.contoso.com -d dc02.contoso.com
  $0 --discover -v
  $0 -d 192.168.1.10 -p 636 -o /tmp/certs
EOF
}

# Function to log messages
log() 
{
    local level=$1
    local message=$2
    case $level in
        "INFO")
            echo -e "${GREEN}[INFO]${NC} $message"
            ;;
        "WARN")
            echo -e "${YELLOW}[WARN]${NC} $message"
            ;;
        "ERROR")
            echo -e "${RED}[ERROR]${NC} $message"
            ;;
        "DEBUG")
            if [ "$VERBOSE" = true ]; then
                echo -e "${CYAN}[DEBUG]${NC} $message"
            fi
            ;;
    esac
}

# Function to validate single certificate
validate_certificate() 
{
    local dc=$1
    local port=${2:-$LDAPS_PORT}
    
    log "INFO" "Validating certificate for: $dc:$port"
    
    # Create output directory
    mkdir -p "$OUTPUT_DIR"
    
    # Test connectivity first
    if ! timeout 10 bash -c "</dev/tcp/$dc/$port" 2>/dev/null; then
        log "ERROR" "Cannot connect to $dc:$port"
        return 1
    fi
    
    log "DEBUG" "Connection successful to $dc:$port"
    
    # Get certificate chain
    local cert_file="$OUTPUT_DIR/${dc//[^a-zA-Z0-9]/_}.pem"
    local chain_file="$OUTPUT_DIR/${dc//[^a-zA-Z0-9]/_}_chain.pem"
    
    log "DEBUG" "Retrieving certificate chain..."
    
    # Retrieve certificate and chain
    echo | openssl s_client -showcerts -servername "$dc" -connect "$dc:$port" 2>/dev/null > "$chain_file"
    
    if [ $? -ne 0 ]; then
        log "ERROR" "Failed to retrieve certificate from $dc"
        return 1
    fi
    
    # Extract server certificate
    openssl x509 -in "$chain_file" 2>/dev/null > "$cert_file"
    
    if [ $? -ne 0 ]; then
        log "ERROR" "Failed to extract certificate from $dc"
        return 1
    fi
    
    # Get certificate details
    local subject=$(openssl x509 -in "$cert_file" -noout -subject 2>/dev/null | sed 's/subject=//')
    local issuer=$(openssl x509 -in "$cert_file" -noout -issuer 2>/dev/null | sed 's/issuer=//')
    local not_before=$(openssl x509 -in "$cert_file" -noout -startdate 2>/dev/null | sed 's/notBefore=//')
    local not_after=$(openssl x509 -in "$cert_file" -noout -enddate 2>/dev/null | sed 's/notAfter=//')
    local fingerprint=$(openssl x509 -in "$cert_file" -noout -fingerprint -sha256 2>/dev/null | sed 's/SHA256 Fingerprint=//')
    
    # Calculate days until expiration
    local exp_date=$(date -d "$not_after" +%s 2>/dev/null || echo "0")
    local current_date=$(date +%s)
    local days_until_exp=$(( (exp_date - current_date) / 86400 ))
    
    # Display results
    echo -e "\n${CYAN}Certificate Details for $dc:${NC}"
    echo "----------------------------------------"
    echo "Subject: $subject"
    echo "Issuer: $issuer"
    echo "Valid From: $not_before"
    echo "Valid Until: $not_after"
    echo "SHA256 Fingerprint: $fingerprint"
    
    if [ $days_until_exp -lt 0 ]; then
        echo -e "Status: ${RED}EXPIRED${NC} ($((-days_until_exp)) days ago)"
    elif [ $days_until_exp -lt 30 ]; then
        echo -e "Status: ${RED}EXPIRING SOON${NC} ($days_until_exp days remaining)"
    elif [ $days_until_exp -lt 90 ]; then
        echo -e "Status: ${YELLOW}WARNING${NC} ($days_until_exp days remaining)"
    else
        echo -e "Status: ${GREEN}VALID${NC} ($days_until_exp days remaining)"
    fi
    
    # Verify certificate chain
    log "DEBUG" "Verifying certificate chain..."
    local verify_result=$(openssl verify -show_chain "$cert_file" 2>&1)
    
    if echo "$verify_result" | grep -q "OK"; then
        echo -e "Chain Validation: ${GREEN}VALID${NC}"
    else
        echo -e "Chain Validation: ${RED}INVALID${NC}"
        if [ "$VERBOSE" = true ]; then
            echo "Verification Details:"
            echo "$verify_result" | sed 's/^/  /'
        fi
    fi
    
    # Check SAN extensions
    local sans=$(openssl x509 -in "$cert_file" -noout -text 2>/dev/null | grep -A1 "Subject Alternative Name" | tail -1 | sed 's/^ *//')
    if [ -n "$sans" ]; then
        echo "Subject Alternative Names: $sans"
    fi
    
    log "INFO" "Certificate saved to: $cert_file"
    echo ""
    
    return 0
}

# Example usage demonstration
if [ "$#" -eq 0 ]; then
    echo "LDAPS Certificate Validation Script"
    echo "Example usage:"
    echo "  $0 -d dc01.contoso.com -d dc02.contoso.com -v"
    echo "  $0 --help"
    exit 0
fi

Method 3: Windows Certificate Utilities

certutil.exe Validation

Remote Certificate Validation using certutil:

function Invoke-CertutilValidation 
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string[]]$DomainControllers,
        [PSCredential]$Credential
    )
    
    $ScriptBlock = {
        # Get hostname for certificate matching
        $HostName = "{0}.{1}" -f $env:COMPUTERNAME, $env:USERDNSDOMAIN
        
        # Find certificate matching hostname
        $Certificate = Get-ChildItem -Path "Cert:\LocalMachine\My" | 
            Where-Object { $_.Subject -match $HostName }
        
        if ($Certificate) 
        {
            # Export certificate for validation
            $CertPath = "C:\Windows\Temp\$HostName.cer"
            [System.IO.File]::WriteAllBytes($CertPath, $Certificate.RawData)
            
            # Validate certificate using certutil
            Write-Host "Validating certificate for $HostName" -ForegroundColor Green
            certutil.exe -v -urlfetch -verify $CertPath
            
            # Clean up
            Remove-Item $CertPath -Force -ErrorAction SilentlyContinue
        }
        else 
        {
            Write-Warning "No matching certificate found for $HostName"
        }
    }
    
    foreach ($DC in $DomainControllers) 
    {
        Write-Host "`nValidating certificates on: $DC" -ForegroundColor Cyan
        Write-Host "=" * 50 -ForegroundColor Cyan
        
        try 
        {
            if ($Credential) 
            {
                Invoke-Command -ComputerName $DC -ScriptBlock $ScriptBlock -Credential $Credential
            }
            else 
            {
                Invoke-Command -ComputerName $DC -ScriptBlock $ScriptBlock
            }
        }
        catch 
        {
            Write-Error "Failed to validate certificate on $DC : $($_.Exception.Message)"
        }
    }
}

# Usage example
$DomainControllers = (Get-ADDomainController -Filter *).HostName
$Credentials = Get-Credential -Message "Enter credentials for Domain Controller access"
Invoke-CertutilValidation -DomainControllers $DomainControllers -Credential $Credentials

Troubleshooting Common Issues

Certificate Chain Problems

Common certificate chain validation errors and solutions:

Error Description Solution
unable to get local issuer certificate Root CA not in trusted store Install root CA certificate in trusted store
EE certificate key too weak (num=66) The DC certificate's key is below the accepted strength (e.g. RSA < 2048-bit) Reissue the DC certificate with an RSA key ≥ 2048-bit (4096-bit recommended) or an ECC key ≥ 256-bit. This is a key-strength failure, not a trust/self-signed issue.
certificate signature failure Certificate tampered or corrupted Re-issue certificate from CA
certificate has expired Certificate past validity period Renew certificate before expiration
certificate not yet valid System time incorrect or certificate future-dated Verify system time and certificate dates
self signed certificate Certificate is self-signed Install self-signed cert in trusted store or use proper CA

LDAPS Connection Failures

Troubleshooting LDAPS connectivity issues:

function Diagnose-LDAPSConnectivity 
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$DomainController,
        [int]$Port = 636
    )
    
    Write-Host "Diagnosing LDAPS connectivity to $DomainController`:$Port" -ForegroundColor Yellow
    
    # Test 1: Network connectivity
    Write-Host "`n1. Testing network connectivity..." -ForegroundColor Cyan
    $TcpTest = Test-NetConnection -ComputerName $DomainController -Port $Port -WarningAction SilentlyContinue
    
    if ($TcpTest.TcpTestSucceeded) 
    {
        Write-Host "   ✓ Network connection successful" -ForegroundColor Green
    }
    else 
    {
        Write-Host "   ✗ Network connection failed" -ForegroundColor Red
        Write-Host "     - Check firewall rules" -ForegroundColor Yellow
        Write-Host "     - Verify LDAPS is enabled" -ForegroundColor Yellow
        Write-Host "     - Check network routing" -ForegroundColor Yellow
        return
    }
    
    # Test 2: Certificate validation
    Write-Host "`n2. Testing certificate validation..." -ForegroundColor Cyan
    try 
    {
        $TcpClient = New-Object System.Net.Sockets.TcpClient
        $TcpClient.Connect($DomainController, $Port)
        $SslStream = New-Object System.Net.Security.SslStream($TcpClient.GetStream())
        $SslStream.AuthenticateAsClient($DomainController)
        
        Write-Host "   ✓ SSL/TLS handshake successful" -ForegroundColor Green
        
        $Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($SslStream.RemoteCertificate)
        Write-Host "   Certificate Subject: $($Certificate.Subject)" -ForegroundColor White
        Write-Host "   Certificate Issuer: $($Certificate.Issuer)" -ForegroundColor White
        Write-Host "   Valid Until: $($Certificate.NotAfter)" -ForegroundColor White
        
        # Check certificate chain
        $Chain = New-Object System.Security.Cryptography.X509Certificates.X509Chain
        $ChainValid = $Chain.Build($Certificate)
        
        if ($ChainValid) 
        {
            Write-Host "   ✓ Certificate chain validation successful" -ForegroundColor Green
        }
        else 
        {
            Write-Host "   ✗ Certificate chain validation failed" -ForegroundColor Red
            foreach ($Status in $Chain.ChainStatus) 
            {
                Write-Host "     - $($Status.Status): $($Status.StatusInformation)" -ForegroundColor Yellow
            }
        }
    }
    catch 
    {
        Write-Host "   ✗ SSL/TLS handshake failed: $($_.Exception.Message)" -ForegroundColor Red
    }
    finally 
    {
        if ($SslStream) { $SslStream.Dispose() }
        if ($TcpClient) { $TcpClient.Dispose() }
        if ($Chain) { $Chain.Dispose() }
    }
    
    # Test 3: LDAP bind test
    Write-Host "`n3. Testing LDAP bind..." -ForegroundColor Cyan
    try 
    {
        $LDAP = "LDAP://$DomainController`:$Port"
        $DirectoryEntry = New-Object System.DirectoryServices.DirectoryEntry($LDAP)
        $SchemaEntry = $DirectoryEntry.SchemaEntry
        
        if ($SchemaEntry) 
        {
            Write-Host "   ✓ LDAP bind successful" -ForegroundColor Green
        }
        else 
        {
            Write-Host "   ✗ LDAP bind failed" -ForegroundColor Red
        }
    }
    catch 
    {
        Write-Host "   ✗ LDAP bind failed: $($_.Exception.Message)" -ForegroundColor Red
        Write-Host "     - Check authentication credentials" -ForegroundColor Yellow
        Write-Host "     - Verify LDAP service is running" -ForegroundColor Yellow
    }
    finally 
    {
        if ($DirectoryEntry) { $DirectoryEntry.Dispose() }
    }
}

# Usage example
$DomainControllers = (Get-ADDomainController -Filter *).HostName
foreach ($DC in $DomainControllers) 
{
    Diagnose-LDAPSConnectivity -DomainController $DC
    Write-Host "`n" + "="*60 + "`n"
}

Monitoring and Automation

Automated Certificate Monitoring

PowerShell script for scheduled certificate monitoring:

# LDAPS Certificate Monitoring Script
# Schedule this script to run daily via Task Scheduler

[CmdletBinding()]
param(
    [string]$LogPath = "C:\Logs\LDAPS-Monitoring.log",
    [string]$EmailRecipients = "admin@contoso.com",
    [string]$SMTPServer = "mail.contoso.com",
    [int]$WarningDays = 90,
    [int]$CriticalDays = 30
)

function Write-Log 
{
    param([string]$Message, [string]$Level = "INFO")
    $Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $LogEntry = "[$Timestamp] [$Level] $Message"
    Write-Host $LogEntry
    Add-Content -Path $LogPath -Value $LogEntry
}

try 
{
    Write-Log "Starting LDAPS certificate monitoring"
    
    # Get all Domain Controllers
    $DomainControllers = (Get-ADDomainController -Filter *).HostName
    Write-Log "Found $($DomainControllers.Count) Domain Controllers"
    
    $Issues = @()
    
    foreach ($DC in $DomainControllers) 
    {
        try 
        {
            $TcpClient = New-Object System.Net.Sockets.TcpClient
            $TcpClient.Connect($DC, 636)
            $SslStream = New-Object System.Net.Security.SslStream($TcpClient.GetStream())
            $SslStream.AuthenticateAsClient($DC)
            
            $Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($SslStream.RemoteCertificate)
            $DaysUntilExpiry = ($Certificate.NotAfter - (Get-Date)).Days
            
            if ($DaysUntilExpiry -le $CriticalDays) 
            {
                $Issue = [PSCustomObject]@{
                    DomainController = $DC
                    Subject = $Certificate.Subject
                    NotAfter = $Certificate.NotAfter
                    DaysUntilExpiry = $DaysUntilExpiry
                    Severity = "CRITICAL"
                }
                $Issues += $Issue
                Write-Log "CRITICAL: Certificate on $DC expires in $DaysUntilExpiry days" "ERROR"
            }
            elseif ($DaysUntilExpiry -le $WarningDays) 
            {
                $Issue = [PSCustomObject]@{
                    DomainController = $DC
                    Subject = $Certificate.Subject
                    NotAfter = $Certificate.NotAfter
                    DaysUntilExpiry = $DaysUntilExpiry
                    Severity = "WARNING"
                }
                $Issues += $Issue
                Write-Log "WARNING: Certificate on $DC expires in $DaysUntilExpiry days" "WARN"
            }
            else 
            {
                Write-Log "OK: Certificate on $DC is valid for $DaysUntilExpiry days"
            }
        }
        catch 
        {
            $Issue = [PSCustomObject]@{
                DomainController = $DC
                Subject = "CONNECTION FAILED"
                NotAfter = "N/A"
                DaysUntilExpiry = "N/A"
                Severity = "ERROR"
            }
            $Issues += $Issue
            Write-Log "ERROR: Failed to validate certificate on $DC : $($_.Exception.Message)" "ERROR"
        }
        finally 
        {
            if ($SslStream) { $SslStream.Dispose() }
            if ($TcpClient) { $TcpClient.Dispose() }
        }
    }
    
    # Send email notification if issues found
    if ($Issues -and $EmailRecipients -and $SMTPServer) 
    {
        $Subject = "LDAPS Certificate Alert - $($Issues.Count) Issues Found"
        $Body = @"
LDAPS Certificate Monitoring Report
Generated: $(Get-Date)

Issues Found: $($Issues.Count)

$($Issues | Format-Table -AutoSize | Out-String)

Please review and take appropriate action.
"@
        
        Send-MailMessage -To $EmailRecipients -Subject $Subject -Body $Body -SmtpServer $SMTPServer -From "ldaps-monitor@contoso.com"
        Write-Log "Email notification sent to $EmailRecipients"
    }
    
    Write-Log "LDAPS certificate monitoring completed successfully"
}
catch 
{
    Write-Log "LDAPS certificate monitoring failed: $($_.Exception.Message)" "ERROR"
    exit 1
}

Best Practices

Security Best Practices

  • Certificate Validation: Always validate the complete certificate chain
  • Private Key Protection: Ensure private keys are properly secured
  • Certificate Renewal: Plan certificate renewals well in advance
  • Monitoring: Implement automated monitoring for certificate expiration
  • Documentation: Maintain detailed records of certificate deployments

Performance Optimization

  • Connection Pooling: Use connection pooling for frequent LDAPS connections
  • Caching: Cache certificate validation results where appropriate
  • Parallel Processing: Validate multiple Domain Controllers in parallel
  • Resource Management: Properly dispose of SSL streams and TCP connections

Compliance Requirements

  • Audit Logging: Log all certificate validation activities
  • Regular Reviews: Conduct regular certificate security reviews
  • Backup Procedures: Maintain secure backups of certificates and private keys
  • Change Management: Follow proper change management for certificate updates

See Also

External References