¿Cómo puedo enumerar todas las claves RSA guardadas en el CSP de Microsoft?

14

Tengo una aplicación que crea varias claves y las almacena en varias tiendas (en este caso, la tienda de la Máquina).

¿Cómo puedo enumerar todas las claves en un sistema Windows dado?

      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);
    
pregunta random65537 20.01.2011 - 20:19
fuente

5 respuestas

15

Estas claves se almacenan en las ubicaciones enumeradas al final de esta publicación. Muchos administradores de red no conocen el propósito de estos archivos, y algunas publicaciones de foros en la web aconsejan incorrectamente a las personas que eliminen estos archivos. Por supuesto, el impacto de tal acción es la implementación / aplicación específica. No pude leer los archivos con el siguiente código (quizás sea necesario algún cambio)

  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);

        }

Parece que la herramienta "Herramienta de migración de estado de usuario" es necesaria para mover estos datos de una computadora a otra. Además, alguna herramienta necesitará exponer las claves de CryptoAPI al CNG después de tal movimiento.

No conozco ninguna forma de ver los archivos relacionados containerName referenciados en el CSP.

  

Los CSP de CryptoAPI heredados de Microsoft   almacenar claves privadas en los siguientes   directorios.

Usuario privado

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

Sistema local privado

% ALLUSERSPROFILE% \ Application Data \ Microsoft \ Crypto \ RSA \ S-1-5-18 \ % ALLUSERSPROFILE% \ Application Data \ Microsoft \ Crypto \ DSS \ S-1-5-18 \

Servicio local privado

% ALLUSERSPROFILE% \ Application Data \ Microsoft \ Crypto \ RSA \ S-1-5-19 \ % ALLUSERSPROFILE% \ Application Data \ Microsoft \ Crypto \ DSS \ S-1-5-19 \

Servicio de red privado

% ALLUSERSPROFILE% \ Application Data \ Microsoft \ Crypto \ RSA \ S-1-5-20 \ % ALLUSERSPROFILE% \ Application Data \ Microsoft \ Crypto \ DSS \ S-1-5-20 \

Privado compartido

% ALLUSERSPROFILE% \ Application Data \ Microsoft \ Crypto \ RSA \ MachineKeys % ALLUSERSPROFILE% \ Application Data \ Microsoft \ Crypto \ DSS \ MachineKeys

  

CNG almacena claves privadas en el   siguientes directorios.

Usuario privado
% APPDATA% \ Microsoft \ Crypto \ Keys

Sistema local privado % ALLUSERSPROFILE% \ Application Data \ Microsoft \ Crypto \ SystemKeys

Servicio local privado % WINDIR% \ ServiceProfiles \ LocalService

Servicio de red privado % WINDIR% \ ServiceProfiles \ NetworkService

Privado compartido % ALLUSERSPROFILE% \ Application Data \ Microsoft \ Crypto \ Keys

Referencia:

enlace

LDAP

Estas claves también se almacenan en LDAP si se habilita el roaming de credenciales

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

Reemplace la palabra USERNAME en este comando con el nombre de usuario donde la credencial de itinerancia no funciona. Para asegurarse de que ya se realizó la replicación de Active Directory, use la opción -s en el comando y reemplace% LOGONSERVER% con el servidor en el que el usuario realmente inicia sesión. Asegúrese de que el archivo cscverify.ldf muestra valores para los atributos exportados.

El tamaño de las entradas LDAP está controlado por las claves de registro DIMSRoarmingMaxNumTokens y DIMSRoamingMaxTokenSize ( source )

    
respondido por el random65537 20.01.2011 - 20:39
fuente
5

Puede enumerar contenedores de claves usando solo C #, pero debe aprovechar P / Invoke para hacerlo. En realidad, este es el enfoque utilizado por la infame utilidad KeyPal . Aquí hay una pequeña aplicación de C # para listar los nombres de los contenedores de claves de la máquina Una vez que tenga los nombres, puede utilizar CspParameters para crear una instancia del conjunto de claves RSA correspondiente al contenedor de claves.

Gracias a Pinvoke.net por las firmas P / Invoke de CryptAcquireContext , CryptGetProvParam , CryptReleaseContext para aprovechar lo que se requiere de la CryptoAPI de 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;
    }
}
    
respondido por el Derek W 16.10.2015 - 20:02
fuente
2

Algunos CSP permiten enumerar los contenedores clave. Debe hacerlo con código nativo (C, no C #) y usar CryptGetProvParam () con el indicador PP_ENUMCONTAINERS . El código se ve así:

#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;
}

(Este código asume que los nombres de los contenedores de claves caben en 1024 bytes; esto no es una suposición irrazonable.)

Para cada contenedor de claves, es posible que desee "abrirlo" y obtener el tipo y tamaño de clave; Posiblemente exportar la clave pública por completo. Esto se puede hacer con el código .NET (use System.Security.Cryptography.CspParameters para designar un contenedor de claves específico en un CSP específico).

Nota importante: no todos los CSP admiten dicha enumeración. En algunos casos, el conjunto de claves existentes está mal definido, por ejemplo, si el CSP solicita dinámicamente una contraseña de usuario y genera un par de claves sobre la marcha, con un PRNG sembrado desde el nombre del contenedor y la contraseña. Para tal CSP, el número de claves "existentes" (al menos in potentia ) es virtualmente infinito, por lo que no podrá enumerarlas todas.

    
respondido por el Tom Leek 28.01.2014 - 15:28
fuente
1

El nombre del contenedor de clave está incrustado en el archivo en la codificación ASCII a partir del byte 40; su longitud se almacena en el byte 8 (pero resta 1). La mayoría de los archivos (al menos en mi sistema) tienen un GUID como nombre de contenedor, en formato de 38 caracteres incluyendo {-}, y tienen 39 como su octavo byte. El siguiente código extraerá el nombre del contenedor de uno de estos archivos:

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

Este nombre del contenedor se puede usar para cargar los detalles de la clave a través de CspParameters .

    
respondido por el ian 28.01.2014 - 12:15
fuente
1

La misma respuesta que @ian pero como un script de Powershell. En este ejemplo leyendo las teclas de la máquina.

$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
    }
}
    
respondido por el 8DH 29.12.2016 - 10:38
fuente

Lea otras preguntas en las etiquetas