web-dev-qa-db-ja.com

Microsoft CSPに保存されているすべてのRSAキーを列挙するにはどうすればよいですか?

複数のキーを作成してさまざまなストア(この場合はMachineストア)に格納するアプリケーションがあります。

特定の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);
18

これらのキーは、この投稿の下部に記載されている場所に格納されています。多くのネットワーク管理者はこれらのファイルの目的を認識しておらず、Web上のフォーラム投稿の一部は、これらのファイルを削除するように人々に誤ってアドバイスしています。もちろん、そのようなアクションの影響は実装/アプリケーション固有です。次のコードを使用してファイルを読み取ることができませんでした(おそらくいくつかの変更が必要です)

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

        }

このデータをあるコンピューターから別のコンピューターに移動するには、ツール「ユーザー状態移行ツール」が必要なようです。さらに、一部のツールでは、このような移動後にCryptoAPIからCNGにキーを公開する必要があります。

CSPで参照されている関連ファイルcontainerNameを表示する方法を知りません。

Microsoftの従来のCryptoAPI CSPは、次のディレクトリに秘密鍵を格納します。

ユーザープライベート

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

ローカルシステムプライベート

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

ローカルサービスプライベート

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

Network service private

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

共有プライベート

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

CNGは秘密鍵を次のディレクトリに格納します。

ユーザープライベート
%APPDATA%\ Microsoft\Crypto\Keys

ローカルシステムプライベート%ALLUSERSPROFILE%\ Application Data\Microsoft\Crypto\SystemKeys

ローカルサービスプライベート%WINDIR%\ ServiceProfiles\LocalService

ネットワークサービスプライベート%WINDIR%\ ServiceProfiles\NetworkService

共有プライベート%ALLUSERSPROFILE%\ Application Data\Microsoft\Crypto\Keys

参照:

http://msdn.Microsoft.com/en-us/library/bb204778(v = vs.85).aspx

[〜#〜] ldap [〜#〜]

資格情報のローミングが有効な場合、これらのキーはLDAPにも保存されます

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

このコマンドの単語USERNAMEを、資格情報のローミングが機能しないユーザー名に置き換えます。 Active Directoryの複製がすでに実行されていることを確認するには、コマンドで-sオプションを使用し、%LOGONSERVER%をユーザーが実際にログオンするサーバーに置き換えます。 cscverify.ldfファイルに、エクスポートされた属性の値が表示されていることを確認してください。

LDAPエントリのサイズは、DIMSRoarmingMaxNumTokensおよびDIMSRoamingMaxTokenSizeレジストリキーによって制御されます( source

16

C#だけを使用してキーコンテナーを列挙できますが、そのためにはP/Invokeを利用する必要があります。実際、これは悪名高い KeyPalユーティリティ が利用するアプローチです。これは、マシンキーコンテナー名を一覧表示する小さなC#アプリケーションです。名前を取得したら、 CspParameters クラスを使用して、キーコンテナーに対応するRSAキーセットをインスタンス化できます。

何を活用するために CryptAcquireContextCryptGetProvParamCryptReleaseContext のP/Invokeシグネチャを Pinvoke.net に感謝Windows CryptoAPIから必要です。

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;
    }
}
5
Derek W

一部CSPでは、キーコンテナーを列挙できます。ネイティブコード(C#ではなくC)でそれを実行し、_ CryptGetProvParam()PP_ENUMCONTAINERSフラグを使用する必要があります。コードは次のようになります。

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

(このコードは、キーコンテナー名が1024バイトに収まると想定しています。これは不当な想定ではありません。)

次に、各キーコンテナについて、それを「開いて」、キーのタイプとサイズを取得します。公開鍵を完全にエクスポートする可能性があります。これは.NETコードで実行できます(特定のCSPの特定のキーコンテナーを指定するには、 System.Security.Cryptography.CspParameters を使用します)。

重要な注意:すべてのCSPがこのような列挙をサポートしているわけではありません。場合によっては、既存のキーのセットが不明確です。 CSPが動的にユーザーパスワードを要求し、PRNGがコンテナー名とパスワードからシードされた状態で、その場でキーペアを生成します。このようなCSPの場合、既存の"キー(少なくとものポテンシャ)は事実上無限なので、すべてを列挙することはできません。

3
Tom Leek

@ianと同じ答えですが、Powershellスクリプトです。この例では、マシンキーを読み取ります。

$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
    }
}
2
8DH

キーコンテナー名は、ファイルのASCIIエンコードでバイト40から開始して埋め込まれます。その長さはバイト8に格納されます(ただし1を差し引いてください)。ファイルの大部分(少なくとも私のシステム上) )GUIDがコンテナ名として、{-}を含む38文字の形式であり、8番目のバイトが39です。次のコードは、これらのファイルの1つからコンテナ名を抽出します。

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

このコンテナ名を使用して、CspParameters経由でキーの詳細をロードできます。

2
ian