Question

I'm trying to find some information about how to (if at all possible) enumerate all container names in the Strong Name CSP (Cryptographic Service Provider).

Essentially, when you type sn.exe -i key.snk MyContainerName, the public and private key pairs are stored into what's called a "container". Later, within your code, you could specify the container name in the AssemblyKeyNameAttribute, e.g.:

[assembly: AssemblyKeyName("MyContainerName")]

This will cause the assembly to be signed at compile time.

I am trying to find out if it's possible to somehow enumerate all container names. I'm writing a plugin for ReSharper that provides code completion for the InternalsVisibleTo attribute. I'd also like to provide code completion for the AssemblyKeyName attribute, where I would pre-populate the list with known container names.

Is this information accessible?

EDIT: From a comment on this question at IT Security StackExchange, there's a link to a little util called KeyPal. Running this utility with LM will dump the local machine key store:

--------- KeyPal:  MACHINE store: 3 keycontainers ---------
[0] VS_KEY_F726FDF898BC4CB8
     Signature 1024
[1] IIS Express Development Certificate Container
     Exchange  1024
     CertE: CN=localhost
[2] MyContainerName
     Signature 1024
-------------------------------------------------

Where I can see that both [0] and [2] are valid container names to use with AssemblyKeyName. However, there's the [1] one - "IIS Express...", which is not a valid container. How do I distinguish between them?

Was it helpful?

Solution

Here is sample code that kinda does the same thing as that keypal tool. It enumerates all containers (for the local machine) and from there gets the one that can become StrongNameKeyPairs. Usually, strong name keys have a 160 bytes-length public key (SHA1):

foreach (var kc in KeyUtilities.EnumerateKeyContainers("Microsoft Strong Cryptographic Provider"))
{
    CspParameters cspParams = new CspParameters();
    cspParams.KeyContainerName = kc;
    cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
    using (RSACryptoServiceProvider prov = new RSACryptoServiceProvider(cspParams))
    {
        if (prov.CspKeyContainerInfo.Exportable)
        {
            var blob = prov.ExportCspBlob(true);
            StrongNameKeyPair kp = new StrongNameKeyPair(prov.ExportCspBlob(false));
            Console.WriteLine(kc + " pk length:" + kp.PublicKey.Length);
        }
    }
    Console.WriteLine();
}

...

public static class KeyUtilities
{
    public static IList<string> EnumerateKeyContainers(string provider)
    {
        ProvHandle prov;
        if (!CryptAcquireContext(out prov, null, provider, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET | CRYPT_VERIFYCONTEXT))
            throw new Win32Exception(Marshal.GetLastWin32Error());

        List<string> list = new List<string>();
        IntPtr data = IntPtr.Zero;
        try
        {
            int flag = CRYPT_FIRST;
            int len = 0;
            if (!CryptGetProvParam(prov, PP_ENUMCONTAINERS, IntPtr.Zero, ref len, flag))
            {
                if (Marshal.GetLastWin32Error() != ERROR_MORE_DATA)
                    throw new Win32Exception(Marshal.GetLastWin32Error());
            }

            data = Marshal.AllocHGlobal(len);
            do
            {
                if (!CryptGetProvParam(prov, PP_ENUMCONTAINERS, data, ref len, flag))
                {
                    if (Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS)
                        break;

                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }

                list.Add(Marshal.PtrToStringAnsi(data));
                flag = CRYPT_NEXT;
            }
            while (true);
        }
        finally
        {
            if (data != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(data);
            }

            prov.Dispose();
        }
        return list;
    }

    private sealed class ProvHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        public ProvHandle()
            : base(true)
        {
        }

        protected override bool ReleaseHandle()
        {
            return CryptReleaseContext(handle, 0);
        }

        [DllImport("advapi32.dll")]
        private static extern bool CryptReleaseContext(IntPtr hProv, int dwFlags);

    }

    const int PP_ENUMCONTAINERS = 2;
    const int PROV_RSA_FULL = 1;
    const int ERROR_MORE_DATA = 234;
    const int ERROR_NO_MORE_ITEMS = 259;
    const int CRYPT_FIRST = 1;
    const int CRYPT_NEXT = 2;
    const int CRYPT_MACHINE_KEYSET = 0x20;
    const int CRYPT_VERIFYCONTEXT = unchecked((int)0xF0000000);

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool CryptAcquireContext(out ProvHandle phProv, string pszContainer, string pszProvider, int dwProvType, int dwFlags);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool CryptGetProvParam(ProvHandle hProv, int dwParam, IntPtr pbData, ref int pdwDataLen, int dwFlags);
}

The following Namespaces are referenced:

using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top