11

私はこの人と同じ問題を抱えていると思いますが、提供された解決策が私にはうまくいかないので、私は彼/彼女ほど幸運ではありませんでした。

提供されるソリューションは、 (サブディレクトリではなく)およびC:\ProgramData\Microsoft\Crypto\RSA\MachineKeys(およびサブディレクトリ)上のファイルを検索します。ただし、すべてのユーザーにアプリケーションをインストールするセットアップが必要なため、カスタムアクションは-Userの下で実行され、実際に作成されるファイルがリードされます。で。C:\Users\[Username]\AppData\Roaming\Microsoft\Crypto\RSASYSTEMC:\ProgramData\Application Data\Microsoft\Crypto\RSA\S-1-5-18

「通常の」アプリケーションを管理者として実行すると(右クリック->管理者として実行)、まったく同じコードを実行すると、にファイルが作成されC:\Users\[Username]\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-1154405193-2177794320-4133247715-1000ます。

WIXカスタムアクションを使用して生成された証明書には秘密鍵がないようです(「キーセットは存在しません」)が、「通常の」アプリケーションによって生成された証明書にはあります。

ファイルのパーミッションを見ると、たとえ異なっていても(動作しているものにはSYSTEMユーザーが含まれています)、(「動作していない」)ファイルにファイルを追加した後でも、SYSTEM私は読むことができません。秘密鍵、ここでも同じエラー。

次に、FindPrivateKey utilを使用して、対応するファイルを検索しましたが、取得するのは。だけです"Unable to obtain private key file name"

さて、ここで何をしますか?Windowsは、ユーザーが保存した証明書の秘密鍵をどこに保存しSYSTEMますか?秘密鍵ファイルが作成されていないのではないでしょうか。なんで?

4

1 に答える 1

17

私はほとんどすべてをグーグルで検索することによって解決策を得ました...私はやるべきことがいくつかあることを理解しているので:

  1. を生成しますX509Certificate2
  2. 秘密鍵コンテナが永続的(一時的ではない)であることを確認してください
  3. 認証されたユーザーが秘密鍵を見ることができるように、アクセスルールがあることを確認してください

したがって、思いついた最終的なコードは次のとおりです。

X509Certificate2 nonPersistentCert = CreateACertSomehow();

// this is only required since there's no constructor for X509Certificate2 that uses X509KeyStorageFlags but a password
// so we create a tmp password, which is not reqired to be secure since it's only used in memory
// and the private key will be included (plain) in the final cert anyway
const string TMP_PFX_PASSWORD = "password";

// create a pfx in memory ...
byte[] nonPersistentCertPfxBytes = nonPersistentCert.Export(X509ContentType.Pfx, TMP_PFX_PASSWORD);

// ... to get an X509Certificate2 object with the X509KeyStorageFlags.PersistKeySet flag set
X509Certificate2 serverCert = new X509Certificate2(nonPersistentCertPfxBytes, TMP_PFX_PASSWORD,
    X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable); // use X509KeyStorageFlags.Exportable only if you want the private key to tbe exportable

// get the private key, which currently only the SYSTEM-User has access to
RSACryptoServiceProvider systemUserOnlyReadablePrivateKey = serverCert.PrivateKey as RSACryptoServiceProvider;

// create cspParameters
CspParameters cspParameters = new CspParameters(systemUserOnlyReadablePrivateKey.CspKeyContainerInfo.ProviderType, 
    systemUserOnlyReadablePrivateKey.CspKeyContainerInfo.ProviderName, 
    systemUserOnlyReadablePrivateKey.CspKeyContainerInfo.KeyContainerName)
{
    // CspProviderFlags.UseArchivableKey means the key is exportable, if you don't want that use CspProviderFlags.UseExistingKey instead
    Flags = CspProviderFlags.UseMachineKeyStore | CspProviderFlags.UseArchivableKey,
    CryptoKeySecurity = systemUserOnlyReadablePrivateKey.CspKeyContainerInfo.CryptoKeySecurity
};

// add the access rules
cspParameters.CryptoKeySecurity.AddAccessRule(new CryptoKeyAccessRule(new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null), CryptoKeyRights.GenericRead, AccessControlType.Allow));

// create a new RSACryptoServiceProvider from the cspParameters and assign that as the private key
RSACryptoServiceProvider allUsersReadablePrivateKey = new RSACryptoServiceProvider(cspParameters);
serverCert.PrivateKey = allUsersReadablePrivateKey;

// finally place it into the cert store
X509Store rootStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
rootStore.Open(OpenFlags.ReadWrite);
rootStore.Add(serverCert);
rootStore.Close();

// :)
于 2012-11-08T12:23:43.130 に答える