适用于 PowerShell 5.1 及以上版本(Windows)
在 Windows 环境中,数字证书无处不在——HTTPS 网站加密、代码签名、文档签名、智能卡登录、VPN 认证等都离不开证书。日常运维中,我们经常需要查看证书有效期、导出证书、批量检查即将过期的证书,甚至通过脚本完成证书的申请和续签。
PowerShell 提供了强大的证书管理能力。通过 Cert: PSDrive,我们可以像操作文件系统一样浏览和管理 Windows 证书存储区。结合 .NET 的 System.Security.Cryptography.X509Certificates 命名空间,PowerShell 能够覆盖几乎所有的证书管理场景。本文将从查看、搜索、导出、续签检查等几个常见场景出发,介绍如何用 PowerShell 高效管理证书。
浏览和查询证书
Windows 证书存储区采用分层结构:按存储位置(CurrentUser / LocalMachine)和存储名称(My / Root / CA 等)组织。PowerShell 的 Cert: 驱动器让我们可以像浏览文件夹一样查看这些证书。
以下代码演示如何列出当前用户个人存储区的所有证书,并筛选出含有私钥的证书:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| $certs = Get-ChildItem -Path Cert:\CurrentUser\My
Write-Host "当前用户共有 $($certs.Count) 张个人证书`n"
foreach ($cert in $certs) { $hasPrivateKey = $cert.HasPrivateKey $expires = $cert.NotAfter.ToString("yyyy-MM-dd") $issuer = $cert.Issuer
Write-Host "主题: $($cert.Subject)" Write-Host "颁发者: $issuer" Write-Host "有效期至: $expires" Write-Host "含私钥: $hasPrivateKey" Write-Host "---" }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 当前用户共有 3 张个人证书
主题: CN=Victor Woo, E=victor@example.com 颁发者: CN=Victor Woo 有效期至: 2026-03-15 含私钥: True --- 主题: CN=blog.vichamp.com 颁发者: CN=Let's Encrypt Authority X3 有效期至: 2025-12-01 含私钥: True --- 主题: CN=code-signing.vichamp.com 颁发者: CN=DigiCert SHA2 Assured ID Code Signing CA 有效期至: 2026-06-20 含私钥: False ---
|
通过 Cert: 驱动器可以直观地浏览整个证书存储区,也可以使用 Get-ChildItem -Recurse 递归遍历所有存储位置。
查找即将过期的证书
证书过期会导致服务中断,这是运维中最头疼的问题之一。手动检查每台机器的证书有效期既耗时又容易遗漏。用 PowerShell 可以快速扫描所有存储区,找出指定天数内即将过期的证书。
下面这段脚本会检查本地计算机所有存储区中 30 天内即将过期的证书,并输出详细信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| $warningDays = 30 $cutoffDate = (Get-Date).AddDays($warningDays)
$allCerts = Get-ChildItem -Path Cert:\LocalMachine -Recurse -CodeSigningCert
$expiringCerts = @()
foreach ($cert in $allCerts) { if ($cert.NotAfter -le $cutoffDate -and $cert.NotAfter -ge (Get-Date)) { $expiringCerts += [PSCustomObject]@{ Subject = $cert.Subject Issuer = $cert.Issuer Thumbprint = $cert.Thumbprint Expires = $cert.NotAfter.ToString("yyyy-MM-dd HH:mm:ss") DaysLeft = ($cert.NotAfter - (Get-Date)).Days Store = $cert.PSParentPath -replace ".*\\([^\\]+)$", '$1' } } }
$expiringCerts = $expiringCerts | Sort-Object -Property DaysLeft
if ($expiringCerts.Count -eq 0) { Write-Host "没有在 $warningDays 天内即将过期的证书。" } else { Write-Host "发现 $($expiringCerts.Count) 张证书将在 $warningDays 天内过期:`n" $expiringCerts | Format-Table -AutoSize }
|
1 2 3 4 5 6
| 发现 2 张证书将在 30 天内过期:
Subject Issuer Thumbprint Expires DaysLeft Store ------- ------ ---------- ------- -------- ----- CN=api.vichamp.com CN=Let's Encrypt Authority X3 A1B2C3D4E5F6... 2025-09-15 08:30:00 5 My CN=legacy.vichamp.com CN=Go Daddy Secure Certificate... F6E5D4C3B2A1... 2025-10-05 12:00:00 25 My
|
将这段脚本集成到定时任务或监控系统中,就可以在证书过期前收到告警,提前完成续签。
导出证书和私钥
有时需要将证书从一台机器迁移到另一台机器,或者备份证书及其私钥。PowerShell 支持将证书导出为各种格式(PFX、CER、PEM 等)。导出含私钥的证书时需要设置密码保护。
下面代码演示如何将指定指纹的证书(含私钥)导出为 PFX 文件,以及如何仅导出公钥为 CER 文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| $thumbprint = "A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2" $cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.Thumbprint -eq $thumbprint }
if (-not $cert) { Write-Host "未找到指纹为 $thumbprint 的证书" exit }
$pfxPassword = Read-Host -AsSecureString -Prompt "请输入 PFX 导出密码" $pfxPath = "C:\Backup\$($cert.Thumbprint).pfx"
$bytes = $cert.Export("PFX", $pfxPassword) [System.IO.File]::WriteAllBytes($pfxPath, $bytes) Write-Host "PFX 已导出: $pfxPath"
$cerPath = "C:\Backup\$($cert.Thumbprint).cer" $cerBytes = $cert.Export("CERT") [System.IO.File]::WriteAllBytes($cerPath, $cerBytes) Write-Host "CER 已导出: $cerPath"
|
1 2
| PFX 已导出: C:\Backup\A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2.pfx CER 已导出: C:\Backup\A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2.cer
|
PFX 文件包含私钥,务必妥善保管;CER 文件仅包含公钥,可以自由分发。
生成自签名证书用于开发测试
开发 HTTPS 服务或测试代码签名时,通常需要自签名证书。PowerShell 5.1 以上版本内置了 New-SelfSignedCertificate cmdlet,一条命令即可生成证书。
下面代码创建一张用于 HTTPS 开发的自签名证书,并将其导出以便在浏览器中信任:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| $devCert = New-SelfSignedCertificate ` -Subject "CN=localhost" ` -FriendlyName "Dev HTTPS Certificate" ` -NotAfter (Get-Date).AddYears(2) ` -CertStoreLocation "Cert:\CurrentUser\My" ` -KeyExportPolicy Exportable ` -KeyUsage DigitalSignature, KeyEncipherment ` -KeyLength 2048 ` -HashAlgorithm SHA256 ` -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.1")
Write-Host "自签名证书已创建" Write-Host " 指纹: $($devCert.Thumbprint)" Write-Host " 主题: $($devCert.Subject)" Write-Host " 有效期: $($devCert.NotBefore.ToString('yyyy-MM-dd')) 至 $($devCert.NotAfter.ToString('yyyy-MM-dd'))"
$rootStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("Root", "CurrentUser") $rootStore.Open("ReadWrite") $rootStore.Add($devCert) $rootStore.Close()
Write-Host "已添加到受信任的根证书存储区"
|
1 2 3 4 5
| 自签名证书已创建 指纹: B7C8D9E0F1A2B3C4D5E6F7A8B9C0D1E2F3A4B5C6 主题: CN=localhost 有效期: 2025-09-10 至 2027-09-10 已添加到受信任的根证书存储区
|
注意:将自签名证书添加到受信任根存储区仅适用于开发环境,生产环境应使用受信任 CA 签发的证书。
批量统计证书存储区信息
在大规模环境中,管理员可能需要统计各证书存储区的使用情况,以便进行容量规划和清理。以下脚本遍历本地计算机所有存储区并生成汇总报告:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| $storeNames = @("My", "Root", "CA", "Trust", "TrustedPeople", "TrustedPublisher") $location = "LocalMachine" $report = @()
foreach ($storeName in $storeNames) { $storePath = "Cert:\$location\$storeName" $certs = Get-ChildItem -Path $storePath -ErrorAction SilentlyContinue
$totalCount = ($certs | Measure-Object).Count $expiredCount = ($certs | Where-Object { $_.NotAfter -lt (Get-Date) } | Measure-Object).Count $expiringCount = ($certs | Where-Object { $_.NotAfter -ge (Get-Date) -and $_.NotAfter -lt (Get-Date).AddDays(90) } | Measure-Object).Count
$report += [PSCustomObject]@{ Store = $storeName Location = $location TotalCerts = $totalCount Expired = $expiredCount ExpiringSoon = $expiringCount ValidRemaining = $totalCount - $expiredCount - $expiringCount } }
Write-Host "`n证书存储区汇总报告:`n" $report | Format-Table -AutoSize
|
1 2 3 4 5 6 7 8 9 10
| 证书存储区汇总报告:
Store Location TotalCerts Expired ExpiringSoon ValidRemaining ----- -------- ---------- ------- ------------ -------------- My LocalMachine 5 1 2 2 Root LocalMachine 120 0 0 120 CA LocalMachine 85 2 1 82 Trust LocalMachine 3 0 0 3 TrustedPeople LocalMachine 1 0 0 1 TrustedPublisher LocalMachine 8 0 1 7
|
通过这种汇总视图,管理员可以快速定位需要关注的存储区,例如上例中 My 存储区有 1 张已过期和 2 张即将过期的证书,需要优先处理。
注意事项
权限要求:访问 LocalMachine 证书存储区需要管理员权限。普通用户只能操作 CurrentUser 存储区。在脚本开头加上 #Requires -RunAsAdministrator 可以避免权限不足导致的运行失败。
私钥安全:导出 PFX 文件时务必使用强密码保护,并妥善保存密码。私钥一旦泄露,攻击者可以冒充证书持有者进行中间人攻击或伪造签名。
Cert: 驱动器仅限 Windows:Cert: PSDrive 是 Windows 特有的功能,PowerShell 7 在 Linux/macOS 上不可用。跨平台场景下需要使用 .NET 的 X509Certificate2 类直接操作。
证书指纹唯一性:每张证书的指纹(Thumbprint)是 SHA-1 哈希值,全局唯一。在脚本中引用证书时应优先使用指纹而非主题名,因为同一主题名可能存在多张证书。
自签名证书仅用于开发:自签名证书不受公共 CA 信任,浏览器和操作系统会发出安全警告。生产环境应使用 Let’s Encrypt、DigiCert 等受信任 CA 签发的证书。
定期巡检:建议将证书过期检查脚本配置为 Windows 计划任务或 CI/CD 流水线,每周自动执行一次。结合邮件或即时通讯告警,确保在证书过期前有充足的处理时间。