Gerenciando certificados no Windows via PowerShell

1. Introdução aos Certificados Digitais no Windows

Certificados digitais são credenciais eletrônicas que vinculam uma identidade a um par de chaves criptográficas (pública e privada). No ecossistema Windows, eles são fundamentais para autenticação, assinatura de código, criptografia de e-mails e comunicações seguras via TLS/SSL. Uma Autoridade Certificadora (CA) é a entidade que emite e valida esses certificados.

O Windows armazena certificados em dois repositórios principais:
- Certificados do Computador Local (Cert:\LocalMachine): Acessíveis a todos os usuários e serviços do sistema.
- Certificados do Usuário Atual (Cert:\CurrentUser): Restritos ao perfil do usuário logado.

Para gerenciar esses certificados via PowerShell, utilizamos os cmdlets dos módulos PKI e Certificate. O módulo PKI oferece funcionalidades avançadas como importação/exportação, enquanto Certificate provê acesso ao provedor Cert:.

2. Navegando e Listando Certificados com PowerShell

O provedor Cert: permite navegar pelos repositórios como se fossem unidades de disco. Para listar todos os certificados no repositório pessoal do computador local:

Get-ChildItem -Path Cert:\LocalMachine\My

Para filtrar por propriedades específicas:

# Certificados com validade superior a 30 dias
Get-ChildItem -Path Cert:\CurrentUser\My | Where-Object { $_.NotAfter -gt (Get-Date).AddDays(30) }

# Certificados expirados
Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.NotAfter -lt (Get-Date) }

# Certificados por assunto (Subject)
Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.Subject -like "*contoso*" }

Localizando certificados prestes a expirar nos próximos 60 dias:

$expiringCerts = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {
    $_.NotAfter -gt (Get-Date) -and $_.NotAfter -lt (Get-Date).AddDays(60)
}
$expiringCerts | Select-Object Subject, NotAfter, Thumbprint

3. Importação e Exportação de Certificados

Importando certificados

Para importar um arquivo PFX (inclui chave privada) com proteção por senha:

$password = ConvertTo-SecureString -String "MinhaSenha123" -AsPlainText -Force
Import-PfxCertificate -FilePath "C:\Certificados\meucert.pfx" `
    -CertStoreLocation Cert:\LocalMachine\My -Password $password

Para importar um certificado sem chave privada (formato CER ou CRT):

Import-Certificate -FilePath "C:\Certificados\certificado.cer" `
    -CertStoreLocation Cert:\CurrentUser\Root

Exportando certificados

Exportando com chave privada para PFX:

$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.Subject -eq "CN=MeuSite" }
$password = ConvertTo-SecureString -String "OutraSenha456" -AsPlainText -Force
Export-PfxCertificate -Cert $cert -FilePath "C:\Backup\meusite.pfx" -Password $password

Exportando apenas o certificado público (CER):

Export-Certificate -Cert $cert -FilePath "C:\Backup\meusite.cer" -Type CERT

4. Criação de Certificados Autoassinados

O cmdlet New-SelfSignedCertificate permite gerar certificados para testes e ambientes de desenvolvimento:

# Certificado SSL para servidor web
New-SelfSignedCertificate -DnsName "www.meusite.local", "meusite.local" `
    -CertStoreLocation Cert:\LocalMachine\My `
    -FriendlyName "Certificado SSL Teste" `
    -NotAfter (Get-Date).AddYears(2) `
    -KeyUsage DigitalSignature, KeyEncipherment `
    -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.1")  # Server Authentication

Criando certificado para assinatura de código:

New-SelfSignedCertificate -Type CodeSigning -Subject "CN=Meu Código" `
    -CertStoreLocation Cert:\CurrentUser\My `
    -NotAfter (Get-Date).AddYears(3)

5. Gerenciamento de Autoridades Certificadoras (CA)

Instalação de CA corporativa

Install-WindowsFeature -Name ADCS-Cert-Authority -IncludeManagementTools
Install-AdcsCertificationAuthority -CAType EnterpriseRootCa `
    -CACommonName "Contoso Root CA" -KeyLength 4096 -ValidityPeriod Years -ValidityPeriodUnits 10

Emissão de certificados via CA

Usando CertReq.exe para solicitar certificado de uma CA corporativa:

# Criar arquivo de solicitação (.inf)
$infContent = @"
[NewRequest]
Subject = "CN=servidor.contoso.com"
KeySpec = 1
KeyLength = 2048
Exportable = TRUE
MachineKeySet = TRUE
SMIME = FALSE
PrivateKeyArchive = FALSE
UserProtected = FALSE
UseExistingKeySet = FALSE
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
ProviderType = 12
RequestType = PKCS10
KeyUsage = 0xa0
[EnhancedKeyUsageExtension]
OID=1.3.6.1.5.5.7.3.1
"@
$infContent | Out-File -FilePath "C:\Temp\request.inf" -Encoding ASCII

# Gerar solicitação
certreq -new "C:\Temp\request.inf" "C:\Temp\request.req"

# Submeter à CA (substitua NomeDaCA)
certreq -submit -config "NomeDaCA\Contoso Root CA" "C:\Temp\request.req" "C:\Temp\cert.cer"

# Instalar o certificado emitido
certreq -accept "C:\Temp\cert.cer"

Revogação de certificados

# Revogar por thumbprint
certutil -revoke <Thumbprint> 1  # 1 = CA compromise

# Verificar CRL
certutil -crl

6. Automação de Tarefas de Manutenção

Backup automático de certificados pessoais

$backupDir = "C:\BackupCertificados\$(Get-Date -Format 'yyyy-MM-dd')"
New-Item -ItemType Directory -Path $backupDir -Force

$certs = Get-ChildItem -Path Cert:\LocalMachine\My
foreach ($cert in $certs) {
    $filename = "$backupDir\$($cert.Subject -replace '[^\w\s]', '_').pfx"
    $password = ConvertTo-SecureString -String "Backup2024!" -AsPlainText -Force
    Export-PfxCertificate -Cert $cert -FilePath $filename -Password $password
}
Write-Output "Backup concluído em $backupDir"

Relatório de expiração com envio por e-mail

$expiring = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {
    $_.NotAfter -lt (Get-Date).AddDays(90) -and $_.NotAfter -gt (Get-Date)
}

$htmlReport = $expiring | Select-Object Subject, NotAfter, Thumbprint |
    ConvertTo-Html -Title "Relatório de Expiração de Certificados"

$htmlReport | Out-File "C:\Temp\expiration_report.html"

Send-MailMessage -To "admin@contoso.com" -From "noreply@contoso.com" `
    -Subject "Relatório de Certificados Próximos ao Vencimento" `
    -Body "Segue relatório em anexo." -Attachments "C:\Temp\expiration_report.html" `
    -SmtpServer "smtp.contoso.com"

Remoção de certificados expirados

$expired = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.NotAfter -lt (Get-Date) }
foreach ($cert in $expired) {
    Remove-Item -Path $cert.PSPath -DeleteKey
    Write-Output "Removido: $($cert.Subject)"
}

7. Integração com Serviços e Aplicações

Associando certificados a sites IIS

$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.Subject -eq "CN=www.meusite.local" }
New-WebBinding -Name "MeuSite" -Protocol https -Port 443 -IPAddress "*"
$binding = Get-IISSiteBinding -Name "MeuSite" -Protocol https
$binding.AddSslCertificate($cert.GetCertHash(), "my")

Configuração de certificado para RDP

$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.Subject -eq "CN=servidor.contoso.com" }
$thumbprint = $cert.Thumbprint
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" `
    -Name "SSLCertificateSHA1Hash" -Value $thumbprint
Restart-Service -Name "TermService"

Uso em autenticação de scripts

$cert = Get-ChildItem -Path Cert:\CurrentUser\My | Where-Object { $_.Subject -eq "CN=script@contoso.com" }
$response = Invoke-RestMethod -Uri "https://api.contoso.com/data" -Certificate $cert

8. Segurança e Boas Práticas

Proteção de chaves privadas

$certPath = "Cert:\LocalMachine\My\$($cert.Thumbprint)"
$acl = Get-Acl -Path $certPath
$permission = "BUILTIN\Administrators", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow"
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $permission
$acl.SetAccessRule($accessRule)
Set-Acl -Path $certPath -AclObject $acl

Auditoria de operações com certificados

Get-WinEvent -LogName "Microsoft-Windows-CertificateServices-Client-Lifecycle-System/Operational" |
    Where-Object { $_.TimeCreated -gt (Get-Date).AddDays(-7) } |
    Select-Object TimeCreated, Id, Message

Estratégias de renovação automatizada

# Verificar certificados com menos de 30 dias de validade
$critical = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {
    $_.NotAfter -lt (Get-Date).AddDays(30) -and $_.NotAfter -gt (Get-Date)
}

if ($critical.Count -gt 0) {
    # Disparar renovação automática via CA
    foreach ($cert in $critical) {
        certreq -enroll -cert $cert.Thumbprint -config "NomeDaCA\Contoso Root CA"
    }
}

Referências