Come posso enumerare tutte le chiavi RSA salvate nel Microsoft CSP?

14

Ho un'applicazione che sta creando diverse chiavi e memorizzandole in vari negozi (in questo caso l'archivio delle macchine).

Come posso enumerare tutte le chiavi su un determinato sistema Windows?

      CspParameters cspParams = new CspParameters();
        cspParams.KeyContainerName = containerName + " " + g.ToString() ;
        cspParams.Flags = CspProviderFlags.UseMachineKeyStore;

        // Create a new RSA key and save it in the container.  This key will encrypt
        // a symmetric key, which will then be encryped in the XML document.
        RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
        rsaKey.PersistKeyInCsp = true;
        Console.WriteLine(rsaKey.ToXmlString(false));
        string PublicKeyTest = rsaKey.ToXmlString(false);
    
posta random65537 20.01.2011 - 20:19
fonte

5 risposte

15

Queste chiavi sono memorizzate nelle posizioni elencate in fondo a questo post. Molti amministratori di rete non sono a conoscenza dello scopo di questi file e alcuni post sul forum in modo errato consigliano alle persone di eliminare questi file. Naturalmente, l'impatto di tale azione è l'implementazione / l'applicazione specifica. Non ero in grado di leggere i file usando il seguente codice (forse sono necessarie alcune modifiche)

  var files = System.IO.Directory.GetFiles(@"C:\ProgramData\Application Data\Microsoft\Crypto\RSA\MachineKeys\");

        foreach (var f in files)
        {           
            RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider();
            var readFile = File.OpenRead(  f.ToString());
            byte[] FileOut = new byte[readFile.Length];
            readFile.Read( FileOut, 0, (int)readFile.Length-1);
            rsaKey.ImportCspBlob(FileOut);

        }

Sembra che lo strumento "User State Migration tool" sia necessario per spostare questi dati da un computer a un altro. Inoltre, alcuni strumenti dovranno esporre le chiavi da CryptoAPI al CNG dopo tale spostamento.

Non sono a conoscenza di alcun modo per visualizzare i file correlati containerName a cui si fa riferimento nel CSP.

The Microsoft legacy CryptoAPI CSPs store private keys in the following directories.

Utente privato

% APPDATA% \ Microsoft \ Crypto \ RSA \ SID utente \ % APPDATA% \ Microsoft \ Crypto \ DSS \ SID utente \

Sistema privato locale

% ALLUSERSPROFILE% \ Dati applicazioni \ Microsoft \ Crypto \ RSA \ S-1-5-18 \ % ALLUSERSPROFILE% \ Dati applicazioni \ Microsoft \ Crypto \ DSS \ S-1-5-18 \

Servizio locale privato

% ALLUSERSPROFILE% \ Dati applicazioni \ Microsoft \ Crypto \ RSA \ S-1-5-19 \ % ALLUSERSPROFILE% \ Dati applicazioni \ Microsoft \ Crypto \ DSS \ S-1-5-19 \

Servizio di rete privato

% ALLUSERSPROFILE% \ Dati applicazioni \ Microsoft \ Crypto \ RSA \ S-1-5-20 \ % ALLUSERSPROFILE% \ Dati applicazioni \ Microsoft \ Crypto \ DSS \ S-1-5-20 \

Privato condiviso

% ALLUSERSPROFILE% \ Dati applicazioni \ Microsoft \ Crypto \ RSA \ MachineKeys % ALLUSERSPROFILE% \ Dati applicazioni \ Microsoft \ Crypto \ DSS \ MachineKeys

CNG stores private keys in the following directories.

Utente privato % Appdata% \ Microsoft \ Crypto \ Keys

Sistema privato locale % ALLUSERSPROFILE% \ Dati applicazioni \ Microsoft \ Crypto \ SystemKeys

Servizio locale privato % WINDIR% \ ServiceProfiles \ LocalService

Servizio di rete privato % WINDIR% \ ServiceProfiles \ NetworkService

Privato condiviso % ALLUSERSPROFILE% \ Dati applicazioni \ Microsoft \ Crypto \ Keys

Riferimento:

link

LDAP

Queste chiavi sono anche memorizzate in LDAP se il roaming delle credenziali è abilitato

ldifde.exe -s %LOGONSERVER% -f cscverify.ldf -r "(cn=USERNAME)" -l msPKIAccountCredentials,msPKIRoamingTimeStamp,msPKIDPAPIMasterKeys

Sostituisci la parola USERNAME in questo comando con il nome utente dove il roaming delle credenziali non funziona. Per garantire che la replica di Active Directory fosse già stata eseguita, utilizzare l'opzione -s nel comando e sostituire% LOGONSERVER% con il server a cui l'utente ha effettivamente effettuato l'accesso. Assicurarsi che il file cscverify.ldf mostri i valori per gli attributi esportati.

La dimensione delle voci LDAP è controllata dalle chiavi di registro DIMSRoarmingMaxNumTokens e DIMSRoamingMaxTokenSize ( origine )

    
risposta data 20.01.2011 - 20:39
fonte
5

Puoi enumerare i contenitori di chiavi usando solo C #, ma devi fare leva su P / Invoke per farlo. In realtà, questo è l'approccio utilizzato dall'infame utilità KeyPal . Ecco una piccola applicazione C # per elencare i nomi dei contenitori delle chiavi della macchina. Una volta ottenuti i nomi, è possibile utilizzare Classe CspParameters per creare un'istanza di Keyset RSA corrispondente al contenitore di chiavi.

Grazie a Pinvoke.net per le firme P / Invoke di CryptAcquireContext , CryptGetProvParam , CryptReleaseContext per sfruttare ciò che è richiesto da CryptoAPI di Windows.

class Program
{
    static long CRYPT_MACHINE_KEYSET = 0x20;
    static long CRYPT_VERIFYCONTEXT = 0xF0000000;
    static uint CRYPT_FIRST = 1;
    static uint CRYPT_NEXT = 2;

    static uint PROV_RSA_FULL = 1;
    static uint PP_ENUMCONTAINERS = 2;

    [DllImport("advapi32.dll", SetLastError = true)]
    static extern bool CryptGetProvParam(
       IntPtr hProv,
       uint dwParam,
       [MarshalAs(UnmanagedType.LPStr)] StringBuilder pbData,
       ref uint dwDataLen,
       uint dwFlags);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool CryptAcquireContext(
        ref IntPtr hProv,
        string pszContainer,
        string pszProvider,
        uint dwProvType,
        uint dwFlags);

    [DllImport("advapi32.dll", EntryPoint = "CryptReleaseContext", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern bool CryptReleaseContext(
       IntPtr hProv,
       Int32 dwFlags);

    static void Main(string[] args)
    {
        Console.WriteLine("Key Container Names:");
        IEnumerable<string> keyContainerNames = GetKeyContainerNames();
        foreach (string name in keyContainerNames)
        {
            Console.WriteLine(name);
        }

        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();
    }

    public static IEnumerable<string> GetKeyContainerNames()
    {
        var keyContainerNameList = new List<string>();

        IntPtr hProv = IntPtr.Zero;
        uint flags = (uint)(CRYPT_MACHINE_KEYSET | CRYPT_VERIFYCONTEXT);
        if (CryptAcquireContext(ref hProv, null, null, PROV_RSA_FULL, flags) == false)
            throw new Exception("CryptAcquireContext");

        uint bufferLength = 2048;
        StringBuilder stringBuilder = new StringBuilder((int)bufferLength);
        if (CryptGetProvParam(hProv, PP_ENUMCONTAINERS, stringBuilder, ref bufferLength, CRYPT_FIRST) == false)
            return keyContainerNameList;

        keyContainerNameList.Add(stringBuilder.ToString());

        while (CryptGetProvParam(hProv, PP_ENUMCONTAINERS, stringBuilder, ref bufferLength, CRYPT_NEXT))
        {
            keyContainerNameList.Add(stringBuilder.ToString());
        }

        if (hProv != IntPtr.Zero)
        {
            CryptReleaseContext(hProv, 0);
        }

        return keyContainerNameList;
    }
}
    
risposta data 16.10.2015 - 20:02
fonte
2

Alcuni CSP consentono di enumerare i contenitori di chiavi. Devi farlo con codice nativo (C, non C #), e usa CryptGetProvParam () con il flag PP_ENUMCONTAINERS . Il codice ha il seguente aspetto:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <windows.h>
#include <wincrypt.h>

static void
usage(void)
{
        fprintf(stderr,
"Usage: enumkeys.exe [ csp ]\n");
        exit(EXIT_FAILURE);
}

static void
failerr(char *funName)
{
        fprintf(stderr, "%s() failed: 0x%08lX\n",
                funName, (unsigned)GetLastError());
        exit(EXIT_FAILURE);
}

int
main(int argc, char *argv[])
{
        int size;
        char *csp, buf[1024];
        HCRYPTPROV hprov;
        DWORD flags, buf_len;

        if (argc > 2) {
                usage();
        }
        if (argc > 1) {
                csp = argv[1];
        } else {
                csp = MS_STRONG_PROV_A;
        }
        hprov = 0;
        flags = CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET;
        if (!CryptAcquireContextA(&hprov, NULL, csp, PROV_RSA_FULL, flags)) {
                failerr("CryptAcquireContext");
        }
        buf_len = sizeof buf;
        if (!CryptGetProvParam(hprov, PP_ENUMCONTAINERS,
                buf, &buf_len, CRYPT_FIRST))
        {
                if (GetLastError() == ERROR_NO_MORE_ITEMS) {
                        printf("No container.\n");
                        exit(EXIT_SUCCESS);
                } else {
                        failerr("CryptGetProvParam");
                }
        }
        for (;;) {
                printf("Container: '%s'\n", buf);
                buf_len = sizeof buf;
                if (!CryptGetProvParam(hprov, PP_ENUMCONTAINERS,
                        buf, &buf_len, CRYPT_NEXT))
                {
                        if (GetLastError() == ERROR_NO_MORE_ITEMS) {
                                break;
                        }
                        failerr("CryptGetProvParam");
                }
        }
        CryptReleaseContext(hprov, 0);
        return EXIT_SUCCESS;
}

(Questo codice presuppone che i nomi dei contenitori di chiavi si adattino a 1024 byte, non è un'ipotesi irragionevole.)

Per ciascun contenitore di chiavi, puoi quindi "aprirlo" e ottenere il tipo e la dimensione della chiave; eventualmente esportare la chiave pubblica del tutto. Questo può essere fatto con codice .NET (usare System.Security.Cryptography.CspParameters per designare un contenitore di chiavi specifico su un CSP specifico).

Nota importante: non tutti i CSP supportano tale enumerazione. In alcuni casi, l'insieme di chiavi esistenti è mal definito, ad es. se il CSP richiede dinamicamente una password utente e genera una coppia di chiavi al volo, con un seed PRNG dal nome del contenitore e la password. Per tale CSP, il numero di chiavi "esistenti" (almeno in potentia ) è virtualmente infinito, quindi non sarai in grado di enumerarle tutte.

    
risposta data 28.01.2014 - 15:28
fonte
1

Il nome del contenitore chiave è incorporato nel file nella codifica ASCII a partire dal byte 40; la sua lunghezza è memorizzata al byte 8 (ma sottrarre 1). La maggior parte dei file (almeno sul mio sistema) ha un GUID come nome del contenitore, nel formato di 38 caratteri incluso {-}, e ha 39 come il loro ottavo byte. Il seguente codice estrarrà il nome del contenitore da uno di questi file:

byte[] bytes = File.ReadAllBytes(fileName);<br>
string containerName = Encoding.ASCII.GetString(bytes, 40, bytes[8] - 1);

Questo nome contenitore può quindi essere utilizzato per caricare i dettagli della chiave tramite CspParameters .

    
risposta data 28.01.2014 - 12:15
fonte
1

La stessa risposta di @ian ma come uno script PowerShell. In questo esempio, leggendo i tasti della macchina.

$containerPath = "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys"

Get-ChildItem $containerPath | ForEach-Object {
    try { 
        $bytes = [System.IO.File]::ReadAllBytes("$($_.fullName)")
        # Decode $byte[8]-1 bytes from position 40, assuming ASCII encoding.
        $ContainerName = [System.Text.Encoding]::ASCII.GetString($bytes, 40, $bytes[8]-1)
    } catch {
        $ContainerName="No read access"
    }
    [PSCustomObject]@{
        Container = $ContainerName
        FileName = $_.Name
    }
}
    
risposta data 29.12.2016 - 10:38
fonte

Leggi altre domande sui tag