Windows Server 2019: accumulo anomalo di file in Microsoft Crypto\RSA (LocalService) — cause, analisi CAPI2 e pulizia sicura

Se su Windows Server 2019 ti ritrovi con “milioni di file” da pochi KB nella cartella C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\Microsoft\Crypto\RSA, questa guida spiega cosa sono, perché continuano a crescere e come rimuoverli in sicurezza senza interrompere TLS, backup o altri servizi crittografici.

Indice

Contesto e impatto

Quella cartella contiene i “contenitori di chiave” usati dalla CryptoAPI (CAPI) di Windows. Quando un servizio in esecuzione come LocalService genera una chiave RSA persistente, il relativo contenitore viene salvato qui. In condizioni normali il numero di file è ridotto; se esplode fino a centinaia di migliaia o milioni, significa che un processo sta creando chiavi “usa‑e‑getta” a ogni operazione senza riutilizzarle o distruggerle. Oltre a occupare spazio, il filesystem diventa lento (enumerazioni NTFS, antivirus, backup) e la cartella può risultare non eliminabile per ACL restrittive o handle aperti.

Cosa sono esattamente quei file

  • Contenitori di chiave RSA (CryptoAPI Key Container): ciascun file rappresenta un contenitore CAPI che custodisce una coppia di chiavi RSA o i metadati per accedervi tramite il provider crittografico (es. “Microsoft Enhanced RSA and AES Cryptographic Provider”).
  • Ambito di profilo: la posizione indica che i file appartengono al profilo LocalService; spesso sono dentro una sottocartella con il SID corrispondente. Altri account (NetworkService, LocalSystem o utenti reali) hanno cartelle analoghe sotto i rispettivi profili.
  • Origine tipica: componenti che usano CAPI: Schannel/TLS, servizi .NET che impiegano RSACryptoServiceProvider, agent di backup, endpoint security, gestori certificati, servizi di discovery o inventario.

Perché il numero di file continua a crescere

Le cause più comuni sono:

  • Bug o uso improprio della CryptoAPI: un’app crea una nuova chiave RSA a ogni chiamata (per connessione, per job, per transazione) con persistenza attiva, senza mai eliminarla.
  • Configurazione errata: servizi o job pianificati che generano chiavi temporanee pensando di non persisterle (ma le impostazioni di default le rendono persistenti nel profilo).
  • Mancata pulizia automatica: in alcune build o combinazioni software, la routine di “garbage collection” delle chiavi non si attiva; gli aggiornamenti successivi tendono a correggere, ma i file accumulati restano.
  • Crash o interruzioni improvvise: chiavi parzialmente create o contenitori non chiusi restano a terra.

Perché non riesci a cancellarli

  • Permessi: la cartella è di proprietà di LocalService e le ACL sono restrittive; anche un amministratore può non avere diritto di eliminazione finché non prende possesso e aggiorna le ACL.
  • Handle in uso: il processo che sta generando i file tiene aperti i contenitori; rimuoverli mentre sono in uso può fallire o causare errori a caldo.
  • Quantità enorme: eliminare milioni di file con strumenti non adatti può richiedere ore e apparire come “bloccato”. Serve una strategia a lotti e un orario di manutenzione.

Come identificare il processo che genera i contenitori

Prima di cancellare, trova il colpevole. Eliminare alla cieca può interrompere un servizio cruciale e il problema si ripresenterà.

Log CAPI2

Abilita il canale operativo CAPI2 per tracciare chi crea i contenitori:

wevtutil sl Microsoft-Windows-CAPI2/Operational /e:true

Successivamente, nel Visualizzatore eventi (Applicazioni e servizi → Microsoft → Windows → CAPI2 → Operational), cerca eventi che mostrano creazione/accesso a contenitori in Crypto\RSA. Gli eventi includono call‑stack e processo chiamante: è il modo più affidabile per collegare una pioggia di file a un servizio specifico.

Monitoraggio file‑system

Con strumenti di monitoraggio I/O (ad es. Process Monitor), applica questi filtri:

  • Path contiene: \Microsoft\Crypto\RSA
  • Operation è: CreateFile, WriteFile, SetDispositionInformation

Osserva quali processi creano nuovi file con alta frequenza. Annota nome del processo, percorso eseguibile e comando di avvio (servizio Windows, attività pianificata, ecc.).

Mappare contenitori e certificati

Per capire se alcuni contenitori sono effettivamente legati a certificati in uso (da non toccare), puoi elencare i contenitori dal punto di vista di CAPI eseguendo la shell nel contesto corretto e usando certutil:

certutil -key

Nel dettaglio dei certificati (certutil -store My) troverai la riga “Key Container = …” che indica quale contenitore serve quel certificato. Quei contenitori vanno preservati.

Piano di bonifica in sicurezza

La regola d’oro: prima ferma la causa, poi pulisci. Esegui la bonifica in una finestra di manutenzione.

Passo 1 — Aggiorna e stabilizza

  • Applica gli aggiornamenti cumulativi di Windows Server 2019.
  • Aggiorna le applicazioni/agent coinvolti (backup, security, middleware, runtime .NET/Java, ecc.).

Passo 2 — Ferma temporaneamente il processo colpevole

Interrompi il servizio o la pianificazione che genera i contenitori (identificato via CAPI2/monitoraggio). Se non è possibile, riduci il carico (es. scollega il listener o fermane le code).

Passo 3 — Esegui un backup prudenziale

Considerando l’enorme numero di file, comprimere tutto può essere impraticabile. Due opzioni pratiche:

  • Snapshot/VM checkpoint del volume o della VM, se la tua policy lo consente.
  • Quarantena selettiva: sposta solo i file più vecchi di X giorni su un’altra unità. Esempio con Robocopy:
robocopy "C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\Microsoft\Crypto\RSA" `
         "D:\Quarantena_RSA" /E /MOV /MINAGE:30 /R:1 /W:1 /NFL /NDL /NP

Lo spostamento sullo stesso volume è veloce (aggiornamento metadati), su volume diverso può richiedere più tempo ma mantiene una copia di sicurezza.

Passo 4 — Prendi possesso e garantisci i permessi (se necessario)

Se ricevi “Accesso negato”, esegui come amministratore:

takeown /F "C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\Microsoft\Crypto\RSA" /A /R /D Y
icacls  "C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\Microsoft\Crypto\RSA" /grant Administrators:F /T

Nota: dopo la pulizia, ripristina ACL appropriate per non indebolire la sicurezza.

Passo 5 — Rimozione selettiva e graduale

Inizia con i file più vecchi. Un approccio conservativo in PowerShell:

Get-ChildItem -Path "C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\Microsoft\Crypto\RSA" `
  -Recurse -File -Force -ErrorAction SilentlyContinue |
  Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)} |
  Remove-Item -Force -ErrorAction SilentlyContinue

Se i file sono milioni, elimina a lotti per evitare lunghi “stop the world” e ridurre la pressione su antivirus:

$path = "C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\Microsoft\Crypto\RSA"
$cutoff = (Get-Date).AddDays(-30)
$batch  = 200000        # numero per lotto
$more   = $true

while (\$more) {
\$targets = Get-ChildItem -Path \$path -Recurse -File -Force -ErrorAction SilentlyContinue |
Where-Object {\$*.LastWriteTime -lt \$cutoff} |
Select-Object -First \$batch
if (\$targets.Count -eq 0) { \$more = \$false; break }
\$targets | ForEach-Object {
try { Remove-Item -LiteralPath \$*.FullName -Force -ErrorAction Stop } catch {}
}
} 

Best practice: disabilita temporaneamente la scansione in tempo reale dell’antivirus solo per questa cartella durante la bonifica (se consentito dalla policy) e riattivala a fine lavori.

Passo 6 — Pulizia mirata con “certutil” (opzionale)

Per contenitori noti e mappati a certificati non più in uso è più elegante chiedere al provider di eliminarli:

certutil -delkey <NomeKeyContainer>

È più lento su grandi numeri ma lascia il sistema coerente.

Passo 7 — Ripristina permessi

Dopo la bonifica, ripristina le ACL ereditate oppure assegna i permessi minimi necessari a LocalService e ai gruppi di sistema:

icacls "C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\Microsoft\Crypto\RSA" /reset /T

Prevenzione a lungo termine

AzioneScopo
Identificare il processoAbilita CAPI2 Operational e/o usa un monitor I/O per individuare l’eseguibile che crea i contenitori.
Aggiornare il sistemaInstalla le ultime cumulative di Windows Server 2019 e gli update dei prodotti coinvolti.
Correggere configurazioniEvita la creazione di chiavi persistenti ad ogni operazione; abilita riuso/pooling delle chiavi dove possibile.
Pulizia ricorrentePianifica una manutenzione mensile che rimuove i file più vecchi di X giorni dopo un backup.

Script di manutenzione mensile pronto all’uso

Questo script crea un log, elimina i file più vecchi di 45 giorni a lotti, e restituisce un codice di uscita utile per il monitoraggio:

# Esegui come amministratore
$Path   = "C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\Microsoft\Crypto\RSA"
$Cutoff = (Get-Date).AddDays(-45)
$Batch  = 150000
$Log    = "C:\Windows\Temp\RSACleanup$(Get-Date -Format yyyyMMdd_HHmmss).log"

"\[\$(Get-Date)] Start cleanup in \$Path (older than \$Cutoff)" | Out-File -FilePath \$Log -Encoding UTF8
\$deleted = 0
\$errors  = 0

try {
do {
\$chunk = Get-ChildItem -Path \$Path -Recurse -File -Force -ErrorAction SilentlyContinue |
Where-Object {$\_.LastWriteTime -lt \$Cutoff} |
Select-Object -First \$Batch
```
if (-not $chunk) { break }

foreach ($f in $chunk) {
  try {
    Remove-Item -LiteralPath $f.FullName -Force -ErrorAction Stop
    $deleted++
    if (($deleted % 10000) -eq 0) { "[$(Get-Date)] Deleted: $deleted" | Add-Content $Log }
  } catch {
    $errors++
    "[$(Get-Date)] ERROR deleting $($f.FullName): $($_.Exception.Message)" | Add-Content $Log
  }
}
```
} while (\$true)
} finally {
"\[\$(Get-Date)] Completed. Deleted=\$deleted, Errors=\$errors" | Add-Content \$Log
}

if (\$errors -gt 0) { exit 1 } else { exit 0 } 

Segnali tipici del colpevole

  • Servizi .NET legacy: uso di RSACryptoServiceProvider senza impostare PersistKeyInCsp = false genera un contenitore persistente nuovo per ogni istanza. Sintomo: esplosione della cartella durante picchi di richieste.
  • Gateway/proxy TLS, WCF/IIS, agent di sicurezza: generazione di chiavi per handshake o funzioni di ispezione con persistenza involontaria nel profilo LocalService.
  • Job di inventario/telemetria: processo breve e frequente che all’avvio crea e abbandona una nuova chiave.

Linee guida per sviluppatori e integratori

Se controlli il codice o la configurazione dell’app, queste modifiche riducono drasticamente l’accumulo:

Disabilitare la persistenza non necessaria delle chiavi (C#)

using (var rsa = new System.Security.Cryptography.RSACryptoServiceProvider(2048)) {
    rsa.PersistKeyInCsp = false; // evita la creazione del contenitore persistente
    // ... usa la chiave e poi lascia che venga eliminata
}

Usare il key store macchina (se serve persistenza condivisa)

var csp = new CspParameters {
    Flags = CspProviderFlags.UseMachineKeyStore | CspProviderFlags.NoPrompt
};
using (var rsa = new RSACryptoServiceProvider(2048, csp)) {
    rsa.PersistKeyInCsp = true;  // ma in archivio macchina, non nel profilo del servizio
}

Questo sposta i contenitori in C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys, più adatto ad app server multi‑istanza e meno incline a “spazzatura” per‑profilo.

Preferire CNG ai provider CAPI legacy

Quando possibile, usa le API CNG (RSACng, ECDsaCng) e l’archivio Microsoft\Crypto\Keys, più moderno e con semantica di persistenza più chiara.

Eliminazione manuale: rischi e accortezze

  • Rischio di interruzione TLS o backup: se un contenitore in uso viene eliminato, i servizi che ne dipendono falliranno autenticazioni o decrittazioni.
  • ACL e auditing: modificare proprietario e permessi è spesso necessario per la bonifica, ma ripristina il modello di sicurezza al termine.
  • Avvio graduale della pulizia: inizia con file più vecchi di 60/90 giorni, testa i servizi, scendi a 45/30 giorni.
  • Gestione degli handle: se ottieni “file in uso”, prova a fermare il servizio colpevole o pianifica un riavvio di manutenzione.

Check rapido: tabella decisionale

SituazioneAzione consigliataNote
Cartella con milioni di file, spazio in caloFerma il servizio colpevole, elimina per età a lottiPriorità a > 60 giorni; verifica dopo ogni passaggio
“Accesso negato” in eliminazionetakeown + icaclsRipristina ACL a fine attività
Impossibile identificare il colpevoleAbilita CAPI2, usa monitor I/OFiltra su \Microsoft\Crypto\RSA
Contenitori legati a certificati attiviNon eliminare; valuta rotazione certificatiMappa con certutil -store / “Key Container = …”
Ripresentazione del problemaPatch + fix configurazione + job ricorrenteScript di manutenzione mensile

Domande frequenti

Posso cancellare tutta la cartella?
È sconsigliato: potresti eliminare contenitori in uso. Pulisci per età e dopo aver identificato chi li genera.

Perché i file hanno nomi illeggibili?
I nomi sono identificatori di contenitore generati dal provider e non corrispondono direttamente ai nomi dei certificati.

Perché l’eliminazione è lentissima?
Il numero enorme di voci in directory rallenta NTFS e l’antivirus. Procedi a lotti, filtra per età, sospendi temporaneamente la scansione su quella cartella.

Serve riavviare?
Non sempre. Ma se un servizio mantiene handle aperti, un riavvio in finestra di manutenzione può velocizzare bonifica e ripristino.

Sintesi operativa

  1. Non eliminare alla cieca: i file sono contenitori di chiave; capisci prima chi li usa.
  2. Isola il processo colpevole: abilita CAPI2 e usa un monitor file‑system per vedere chi crea i file.
  3. Aggiorna e correggi configurazioni: la crescita infinita in genere deriva da bug o impostazioni che persistono chiavi per errore.
  4. Pulisci in sicurezza: backup/quarantena, permessi corretti, rimozione selettiva per età, a lotti.
  5. Prevenzione: mantieni patch aggiornate e pianifica una manutenzione periodica.

Esempi di comandi pronti

Prendere proprietà e concedere diritti agli amministratori

takeown /F "C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\Microsoft\Crypto\RSA" /A /R /D Y
icacls  "C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\Microsoft\Crypto\RSA" /grant Administrators:F /T

Eliminare solo i file più vecchi di 30 giorni

Get-ChildItem -Path "C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\Microsoft\Crypto\RSA" -Recurse |
  Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-30)} |
  Remove-Item -Force

Abilitare il log CAPI2

wevtutil sl Microsoft-Windows-CAPI2/Operational /e:true

Conclusione

I file che affollano …\Microsoft\Crypto\RSA non sono “spazzatura” generica ma contenitori di chiave crittografica. La loro moltiplicazione segnala un problema a monte: un servizio o un’app che crea chiavi persistenti a raffica. Con logging mirato (CAPI2), aggiornamenti, fix di configurazione e una bonifica selettiva e cadenzata, puoi riportare la cartella a dimensioni sane senza interrompere TLS, backup o altri componenti mission‑critical. Completa il lavoro con uno script di manutenzione ricorrente: impedirai che la situazione degeneri nuovamente e manterrai sotto controllo sicurezza e performance del server.


Appendice: differenza tra CAPI e CNG (utile per orientarsi)
CAPI (CryptoAPI) è lo stack “storico” che salva i contenitori sotto \Microsoft\Crypto\RSA, suddivisi per profilo utente/servizio. CNG (Cryptography Next Generation) è lo stack moderno (chiavi in \Microsoft\Crypto\Keys) e supporta algoritmi e provider più nuovi. Molti prodotti legacy usano ancora CAPI; sapere quale stack impiega la tua applicazione è fondamentale per agire correttamente.

Appendice: ripristino ACL di base
Se hai modificato i permessi per bonificare, valuta il reset ereditando dal profilo LocalService:

icacls "C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\Microsoft\Crypto\RSA" /reset /T

Eventuali permessi speciali richiesti da specifici servizi andranno reapplicati secondo le loro linee guida.


Con questi passaggi si ristabilisce l’ordine nella cartella senza compromettere funzioni crittografiche o servizi di sistema.

Indice