67

サードパーティのサービス プロバイダから共有された証明書の秘密キーを読み取ろうとしています。これを使用して XML を暗号化してからネットワーク経由で送信できます。私は C# でプログラムでこれを行っていますが、これはアクセス許可または構成ミスの問題だと思うので、最も関連性が高いと思われる事実に焦点を当てます。

  • この問題はコードに関連しているとは思いません。私のコードは他のコンピューターで動作し、この問題は Microsoft のサンプル コードに影響します。
  • 証明書は PFX ファイルとして提供されており、テスト目的のためだけのものであるため、ダミーの証明機関も含まれています。
  • MMC.exe を使用して、証明書をローカル マシンの個人用ストアにインポートしてから、関連するすべてのアカウントに秘密キーのアクセス許可を付与し、証明機関を信頼されたルート証明機関にドラッグ アンド ドロップします。
  • C# を使用して、(拇印で識別される) 証明書を読み込み、X509Certificate2.HasPrivateKey. ただし、キーを読み取ろうとするとエラーが発生します。.NET ではCryptographicException、プロパティにアクセスしようとすると、「無効なプロバイダ タイプが指定されました」というメッセージがスローされますX509Certificate2.PrivateKey。Win32 では、メソッドを呼び出すとCryptAcquireCertificatePrivateKey、同等の HRESULT が返されますNTE_BAD_PROV_TYPE
  • これは、Microsoft 独自の 2 つのコード サンプルを使用して証明書の秘密キーを読み取るときに発生する例外と同じです。
  • ローカル マシンではなく、現在のユーザーの同等のストアに同じ証明書をインストールすると、秘密鍵を正常に読み込むことができます。
  • 私は Windows 8.1 をローカル管理者権限で使用しており、通常モードと昇格モードの両方でコードを実行しようとしました。Windows 7 と Windows 8 の同僚は、同じ証明書のローカル マシン ストアからキーを読み込むことができました。
  • 同じストアの場所にある自己署名 IIS テスト証明書の秘密キーを正常に読み取ることができます。
  • 既に .NET 4.5 をターゲットにしています (このエラーは、フレームワークの一部の古いバージョンで報告されています)。
  • これは証明書テンプレートの問題ではないと思います。ローカル マシンと現在のユーザー ストアの両方に等しく影響すると予想されるからです。

同僚とは異なり、IIS マネージャーを使用したり、同じ発行者からの古い証明書を含めたりするなど、さまざまな方法で証明書のアンインストールと再インストールを何度も試みました。MMC に古い証明書または重複した証明書の痕跡が見当たりません。しかし、同じサイズの秘密鍵ファイルが多数あります。最終書き込み時間に基づいて、さまざまなインストール試行の後に取り残されているに違いありません。これらは、それぞれローカル マシンと現在のユーザー ストアの次の場所にあります。

c:\ProgramData\Microsoft\Crypto\RSA\MachineKeys

c:\Users\\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-[残りのユーザー ID]

それで、誰でもアドバイスしてもらえますか:

  • MMC を使用して証明書をアンインストールし、孤立した秘密鍵のように見えるすべてのファイルを削除してから、証明書を再インストールして再試行することをお勧めします。
  • 手動で削除する必要がある他のファイルはありますか?
  • 他に試してみるべきことはありますか?

更新 - 秘密鍵を読み取る試みを示すコード サンプルを追加しました。

static void Main()
{
    // Exception occurs when trying to read the private key after loading certificate from here:
    X509Store store = new X509Store("MY", StoreLocation.LocalMachine);
    // Exception does not occur if certificate was installed to, and loaded from, here:
    //X509Store store = new X509Store("MY", StoreLocation.CurrentUser);

    store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);

    X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
    X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
    X509Certificate2Collection scollection = X509Certificate2UI.SelectFromCollection(fcollection, "Test Certificate Select", "Select a certificate from the following list to get information on that certificate", X509SelectionFlag.MultiSelection);
    Console.WriteLine("Number of certificates: {0}{1}", scollection.Count, Environment.NewLine);

    foreach (X509Certificate2 x509 in scollection)
    {
        try
        {
            Console.WriteLine("Private Key: {0}", x509.HasPrivateKey ? x509.PrivateKey.ToXmlString(false) : "[N/A]");
            x509.Reset();
        }
        catch (CryptographicException ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
    store.Close();

    Console.ReadLine();
}
4

20 に答える 20

49

Windows 8 と Server 2012/2012 R2 で、最近受け取った 2 つの新しい証明書で同じ問題が発生しました。Windows 10 では、この問題は発生しなくなりました (ただし、証明書を操作するコードがサーバーで使用されているため、これは役に立ちません)。Joe Strommen のソリューションは原則として機能しますが、別の秘密鍵モデルでは、証明書を使用するコードを大幅に変更する必要があります。Remy Blok here で説明されているように、秘密鍵を CNG から RSA に変換することをお勧めします。

Remy は、OpenSSL と 2 つの古いツールを使用して秘密鍵の変換を行います。私たちはそれを自動化したいと考え、OpenSSL のみのソリューションを開発しました。CNG形式のMYCERT.pfx秘密鍵パスワードを使用して、RSA 形式の秘密鍵と同じパスワードを使用しMYPWDて新しいパスワードを取得する手順は次のとおりです。CONVERTED.pfx

  1. 公開鍵、完全な証明書チェーンを抽出します。
OpenSSL pkcs12 -in "MYCERT.pfx" -nokeys -out "MYCERT.cer" -passin "pass:MYPWD"
  1. 秘密鍵を抽出します。
OpenSSL pkcs12 -in "MYCERT.pfx" -nocerts -out "MYCERT.pem" -passin "pass:MYPWD" -passout "pass:MYPWD"
  1. 秘密鍵を RSA 形式に変換します。
OpenSSL rsa -inform PEM -in "MYCERT.pem" -out "MYCERT.rsa" -passin "pass:MYPWD" -passout "pass:MYPWD"
  1. 公開鍵と RSA 秘密鍵を新しい PFX にマージします。
OpenSSL pkcs12 -export -in "MYCERT.cer" -inkey "MYCERT.rsa" -out "CONVERTED.pfx" -passin "pass:MYPWD" -passout "pass:MYPWD"

変換された pfx をロードするか、CNG 形式の pfx の代わりに Windows 証明書ストアにインポートすると、問題はなくなり、C# コードを変更する必要がなくなります。

これを自動化するときに遭遇したもう 1 つの落とし穴: 秘密鍵用に生成された長いパスワードを使用しており、パスワードに". OpenSSL コマンド ラインの場合"、パスワード内の文字は としてエスケープする必要があります""

于 2015-12-05T09:08:25.990 に答える
31

私の場合、PowerShell の New-SelfSignedCertificate コマンドで自己署名証明書を使用しようとしていました。デフォルトでは、古い/クラシックな暗号 CAPI の代わりに CNG (Crypto-Next Generation) API を使用して証明書を生成します。一部の古いコードでは、これに問題があります。私の場合は、IdentityServer STS プロバイダーの古いバージョンでした。

これを New-SelfSignedCertificate コマンドの最後に追加することで、問題を回避できました。

-KeySpec KeyExchange

powershell コマンドのスイッチに関するリファレンス:

https://docs.microsoft.com/en-us/powershell/module/pkiclient/new-selfsignedcertificate?view=win10-ps

于 2019-04-22T20:18:24.760 に答える
15

これが発生する別の理由があります。これは奇妙な問題であり、1 日苦労した後、問題を解決しました。実験として、マシン キー ストアを使用して証明書の秘密鍵データを保持する"C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys"フォルダーのアクセス許可を変更しました。このフォルダーのアクセス許可を変更すると、すべての秘密鍵がプロバイダーではない「Microsoft Software KSP provider」として表示されます (私の場合、「Microsoft RSA Schannel Cryptographic Provider」であるはずです)。

解決策: Machinekeys フォルダーへのアクセス許可をリセットします。

このフォルダの元の許可は、ここにあります。私の場合、「全員」のアクセス許可を変更し、「特別なアクセス許可」のチェックマークを削除した読み取りアクセス許可を与えました。そこで、チーム メンバーの 1 人に確認しました (フォルダを右クリック > [プロパティ] > [セキュリティ] > [詳細設定] > [すべてのユーザー] を選択 > [編集] > 権限チェック ボックス リストの [詳細設定] をクリックします)。

特別な権限

これが誰かの日を救うことを願っています!

これが私が回答を見つけた場所です。これを文書化した功績は彼にあります。

于 2017-01-11T23:56:14.247 に答える
10

Alejandro のブログへのリンクが重要です。

これは、証明書が CNG (「Crypto Next-Generation」) API を使用してマシンに保存されているためだと思います。古い .NET API は互換性がないため、動作しません。

この API には Security.Cryptography ラッパーを使用できます ( Codeplex で入手可能)。これにより、拡張メソッドが に追加されるX509Certificate/X509Certificate2ため、コードは次のようになります。

using Security.Cryptography.X509Certificates; // Get extension methods

X509Certificate cert; // Populate from somewhere else...
if (cert.HasCngKey())
{
    var privateKey = cert.GetCngPrivateKey();
}
else
{
    var privateKey = cert.PrivateKey;
}

残念ながら、CNG 秘密鍵のオブジェクト モデルはかなり異なります。元のコード サンプルのようにそれらを XML にエクスポートできるかどうかはわかりません。私の場合は、秘密鍵でいくつかのデータに署名する必要がありました。

于 2015-07-10T21:41:04.967 に答える
3

私にとってうまくいったこと:IIS /アプリケーションプール/ユーザープロファイルのロード= true

于 2019-11-20T22:49:37.587 に答える
0

このエラーは、ストアから証明書を削除し、証明書のインポート ウィザード (.pfx ファイルをダブルクリック) と追加のインポート オプションを使用して、.pfx ファイルから再度インポートした後に解消されました。

インポート オプションを確認した後 (パスワードを入力するのと同じ手順):

「このキーをエクスポート可能としてマークします。これにより、後でキーをバックアップまたは転送できます。」

コードから秘密鍵にエラーなしでアクセスできるようになりました。

また、2番目から最後のステップで「個人」ストアを明示的に選択しますが、これは問題ではないと思います。

于 2020-01-30T14:21:01.597 に答える
0

この投稿のすべてを試しましたが、私にとっての解決策は次のとおりです。

  1. 元の .p12 ファイルをローカル マシンにインポートする
  2. 「すべての拡張プロパティをエクスポートする」および「可能な場合は証明書パスにすべての証明書を含める」オプションをチェックして、.pfx ファイルとしてエクスポートします。
  3. この投稿ソリューションから、このフラグ オプションを使用して新しい証明書を読み取ります。

    var certificate = new X509Certificate2(certKeyFilePath, passCode,
    X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet |       
    X509KeyStorageFlags.PersistKeySet );
    
于 2020-02-05T11:37:39.037 に答える