本文目录导读:

我来帮你编写一个自动清理无用证书的脚本,以下是针对不同系统的实现方案:
Linux 系统证书清理脚本
#!/bin/bash
# 证书清理脚本
# 功能:自动清理过期、无效或未使用的证书
# 配置参数
CERT_PATHS=(
"/etc/ssl/certs"
"/etc/pki/tls/certs"
"/usr/local/share/ca-certificates"
"/usr/share/ca-certificates"
)
LOG_FILE="/var/log/cert-cleanup.log"
DRY_RUN=false # 设为 true 进行预检查,不实际删除
# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# 检查证书是否过期
check_expired() {
local cert_file="$1"
local expire_date=$(openssl x509 -enddate -noout -in "$cert_file" 2>/dev/null | cut -d= -f2)
local expire_epoch=$(date -d "$expire_date" +%s 2>/dev/null)
local current_epoch=$(date +%s)
if [ -n "$expire_epoch" ] && [ "$expire_epoch" -lt "$current_epoch" ]; then
return 0 # 已过期
fi
return 1 # 未过期
}
# 检查证书是否有效
check_valid() {
local cert_file="$1"
# 使用 openssl 验证证书
openssl x509 -noout -in "$cert_file" 2>/dev/null
if [ $? -ne 0 ]; then
return 0 # 无效
fi
# 检查证书链
openssl verify -CApath /etc/ssl/certs "$cert_file" 2>/dev/null
if [ $? -ne 0 ]; then
return 1 # 证书链问题
fi
return 2 # 完全有效
}
# 查找并清理证书
cleanup_certs() {
local cleaned=0
local expired=0
local invalid=0
log "开始清理证书..."
for cert_dir in "${CERT_PATHS[@]}"; do
if [ ! -d "$cert_dir" ]; then
log "目录不存在: $cert_dir"
continue
fi
log "检查目录: $cert_dir"
# 查找所有证书文件
find "$cert_dir" -type f \( -name "*.crt" -o -name "*.pem" -o -name "*.cert" \) 2>/dev/null | while read cert; do
# 跳过符号链接
if [ -L "$cert" ]; then
continue
fi
# 检查过期
if check_expired "$cert"; then
expired=$((expired + 1))
log "过期证书: $cert"
if [ "$DRY_RUN" = false ]; then
rm -f "$cert"
cleaned=$((cleaned + 1))
fi
continue
fi
# 检查有效性
check_valid "$cert"
case $? in
0)
invalid=$((invalid + 1))
log "无效证书: $cert"
if [ "$DRY_RUN" = false ]; then
rm -f "$cert"
cleaned=$((cleaned + 1))
fi
;;
esac
done
done
log "清理完成: 清理 $cleaned 个证书"
log " - 过期证书: $expired"
log " - 无效证书: $invalid"
}
# 更新证书存储
update_cert_store() {
log "更新证书存储..."
# Debian/Ubuntu
if command -v update-ca-certificates &> /dev/null; then
update-ca-certificates --fresh 2>&1 | tee -a "$LOG_FILE"
fi
# CentOS/RHEL/Fedora
if command -v update-ca-trust &> /dev/null; then
update-ca-trust extract 2>&1 | tee -a "$LOG_FILE"
fi
}
# 清理重复证书
cleanup_duplicates() {
log "检查重复证书..."
local hash_map=()
for cert_dir in "${CERT_PATHS[@]}"; do
if [ ! -d "$cert_dir" ]; then
continue
fi
find "$cert_dir" -type f \( -name "*.crt" -o -name "*.pem" \) 2>/dev/null | while read cert; do
if [ -f "$cert" ]; then
local hash=$(openssl x509 -noout -hash -in "$cert" 2>/dev/null)
if [ -n "$hash" ]; then
if [[ " ${hash_map[@]} " =~ " ${hash} " ]]; then
log "发现重复证书: $cert"
if [ "$DRY_RUN" = false ]; then
rm -f "$cert"
fi
else
hash_map+=("$hash")
fi
fi
fi
done
done
}
# 主函数
main() {
log "=== 证书清理脚本开始 ==="
# 检查 root 权限
if [ "$EUID" -ne 0 ]; then
log "错误: 需要 root 权限运行此脚本"
exit 1
fi
# 处理命令行参数
while [[ $# -gt 0 ]]; do
case $1 in
--dry-run)
DRY_RUN=true
log "运行在预览模式(不实际删除)"
;;
--help)
echo "用法: $0 [选项]"
echo "选项:"
echo " --dry-run 预览模式,不实际删除"
echo " --help 显示此帮助信息"
exit 0
;;
*)
echo "未知选项: $1"
exit 1
;;
esac
shift
done
# 执行清理
cleanup_certs
cleanup_duplicates
update_cert_store
log "=== 证书清理脚本结束 ==="
}
# 运行主函数
main "$@"
Windows 系统证书清理脚本 (PowerShell)
# Windows 证书清理脚本
param(
[switch]$DryRun = $false
)
# 配置
$LogFile = "C:\Windows\Temp\cert-cleanup.log"
$CurrentDate = Get-Date
# 日志函数
function Write-Log {
param([string]$Message)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
"$timestamp - $Message" | Out-File -FilePath $LogFile -Append
Write-Host "$timestamp - $Message"
}
# 检查证书是否过期
function Test-CertExpired {
param(
[System.Security.Cryptography.X509Certificates.X509Certificate2]$Cert
)
if ($Cert.NotAfter -lt $CurrentDate) {
return $true
}
return $false
}
# 清理指定存储中的证书
function Cleanup-CertStore {
param(
[string]$StoreName,
[string]$StoreLocation
)
Write-Log "检查证书存储: $StoreName ($StoreLocation)"
try {
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store(
$StoreName,
[System.Security.Cryptography.X509Certificates.StoreLocation]::$StoreLocation
)
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
$cleanedCount = 0
foreach ($cert in $store.Certificates) {
$removeCert = $false
$reason = ""
# 检查过期
if (Test-CertExpired -Cert $cert) {
$removeCert = $true
$reason = "已过期 (过期日: $($cert.NotAfter))"
}
# 检查自签名证书(非根证书)
if ($cert.Subject -eq $cert.Issuer -and $StoreName -ne "Root") {
# 可以自定义规则
}
if ($removeCert) {
Write-Log " 清理证书: $($cert.Subject) - $reason"
if (-not $DryRun) {
try {
$store.Remove($cert)
$cleanedCount++
}
catch {
Write-Log " 错误: 无法删除证书 - $($_.Exception.Message)"
}
}
else {
$cleanedCount++
}
}
}
$store.Close()
Write-Log " 完成: 清理 $cleanedCount 个证书"
}
catch {
Write-Log " 错误: $($_.Exception.Message)"
}
}
# 清理用户和计算机证书存储
function Cleanup-AllStores {
Write-Log "开始清理 Windows 证书存储..."
$stores = @(
@{Name="My"; Location="CurrentUser"},
@{Name="My"; Location="LocalMachine"},
@{Name="Root"; Location="CurrentUser"},
@{Name="Root"; Location="LocalMachine"},
@{Name="CA"; Location="CurrentUser"},
@{Name="CA"; Location="LocalMachine"},
@{Name="TrustedPublisher"; Location="CurrentUser"},
@{Name="TrustedPublisher"; Location="LocalMachine"},
@{Name="Disallowed"; Location="CurrentUser"},
@{Name="Disallowed"; Location="LocalMachine"}
)
foreach ($store in $stores) {
Cleanup-CertStore -StoreName $store.Name -StoreLocation $store.Location
}
}
# 清理过期且未使用的根证书
function Cleanup-UnusedRootCerts {
Write-Log "检查未使用的根证书..."
try {
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store(
"Root",
[System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine
)
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
foreach ($cert in $store.Certificates) {
# 检查是否为微软根证书计划中的证书
$isMicrosoftCert = $cert.Issuer -match "Microsoft"
# 如果证书已过期且不是微软证书
if ((Test-CertExpired -Cert $cert) -and -not $isMicrosoftCert) {
if (-not $DryRun) {
Write-Log " 删除过期非微软根证书: $($cert.Subject)"
$store.Remove($cert)
}
else {
Write-Log " [预览] 将删除过期非微软根证书: $($cert.Subject)"
}
}
}
$store.Close()
}
catch {
Write-Log " 错误: $($_.Exception.Message)"
}
}
# 主函数
function Main {
Write-Log "========================================"
Write-Log "Windows 证书清理脚本开始"
Write-Log "运行模式: $(if ($DryRun) { "预览模式" } else { "实际执行" })"
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
[Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Log "错误: 需要管理员权限运行此脚本"
exit 1
}
Cleanup-AllStores
Cleanup-UnusedRootCerts
Write-Log "证书清理完成"
Write-Log "========================================"
}
# 执行主函数
Main
使用方法
Linux 版本:
# 赋予执行权限 chmod +x cleanup-certs.sh # 预览模式(不实际删除) sudo ./cleanup-certs.sh --dry-run # 实际执行 sudo ./cleanup-certs.sh # 查看日志 cat /var/log/cert-cleanup.log
Windows 版本:
# 以管理员身份运行 PowerShell Set-ExecutionPolicy Bypass -Scope Process # 预览模式 .\cleanup-certs.ps1 -DryRun # 实际执行 .\cleanup-certs.ps1 # 查看日志 Get-Content C:\Windows\Temp\cert-cleanup.log
定时任务设置
Linux (cron):
# 每月1号凌晨3点执行 0 3 1 * * /root/scripts/cleanup-certs.sh >> /var/log/cert-cleanup-cron.log 2>&1
Windows (Task Scheduler):
# 创建定时任务 $action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-File C:\scripts\cleanup-certs.ps1" $trigger = New-ScheduledTaskTrigger -Monthly -DaysOfMonth 1 -At 3am $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest Register-ScheduledTask -TaskName "Certificate Cleanup" -Action $action -Trigger $trigger -Principal $principal
这个脚本会自动:
- 检查并清理过期的证书
- 清理无效或损坏的证书
- 清理重复的证书
- 提供预览模式用于安全检查
- 生成详细的日志记录
使用时请先在预览模式测试,确认不会误删重要证书后再实际执行。