1

我的应用程序即时创建虚拟目录以及在这些虚拟目录中运行的启用 STS 的 Web 应用程序的应用程序池。应用程序池在 ApplicationPoolIdentity 帐户 (IIS APPPOOL\MyAppPool) 下运行。而且我一直在尝试找出以编程方式授予对已安装证书的访问权限的方法。

我的第一种方法是使用批处理文件执行 WinHttpCertCfg。但是,此方法仅适用于已“激活”的应用程序池帐户。通过“激活”,我的意思是我至少浏览过一次新应用程序。直到发生这种情况 - WinHttpCertCfg 总是返回消息“句柄无效”。

我尝试的下一个方法是基于从这里获得的解决方案。此解决方案的工作原理是,当我在 MMC 中浏览证书并选择“管理证书密钥”时,会列出应用程序池帐户。即使我运行 WinHttpCertCfg 来列出具有访问权限的帐户 - 也会列出新的应用程序池。

但毕竟......当我浏览 Web 应用程序时,我仍然得到“keyset 不存在”。

我现在的重点是修复第二种方法。这是我对原始代码的修改

public class CertificateHandler
{
    private const string CommonApplicationKeys = @"Microsoft\Crypto\RSA\MachineKeys";
    private const string PersonalKeys = @"Microsoft\Crypto\RSA\";
    private static X509Certificate2 _personalCertificate = null;
    private static X509Certificate2 _trustedCertificate = null;

    public CertificateHandler(string thumbPrint)
    {
        X509Store personalStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
        X509Store trustedStore = new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine);

        //open the stores to locate the certificates and cache for future use
        if (_personalCertificate == null)
        {
            _personalCertificate = LoadCertificateFromStore(thumbPrint, personalStore);
        }

        if (_trustedCertificate == null)
        {
            _trustedCertificate = LoadCertificateFromStore(thumbPrint, trustedStore);
        }
    }

    /// <summary>
    /// Grants access to the specified certificate.
    /// </summary>
    /// <param name="thumbPrint">The thumb print of the certificate.</param>
    /// <param name="user">The domain qualified user.</param>
    public void GrantAccessToCertificate(string user)
    {
        //open the store to locate the certificate
        GrantAccessToCertificate(user, _personalCertificate);
        GrantAccessToCertificate(user, _trustedCertificate);
    }

    /// <summary>
    /// Grants access to the specified certificate.
    /// </summary>
    /// <param name="user">The domain qualified user.</param>
    /// <param name="certificate">The certificate to which access is granted</param>
    private void GrantAccessToCertificate(string user, X509Certificate2 certificate)
    {
        RSACryptoServiceProvider crypto = certificate.PrivateKey as RSACryptoServiceProvider;

        if (crypto != null)
        {
            //determine the location of the key
            string keyfilepath = FindKeyLocation(crypto.CspKeyContainerInfo.UniqueKeyContainerName);

            //obtain a file handle on the certificate
            FileInfo file = new FileInfo(Path.Combine(keyfilepath, crypto.CspKeyContainerInfo.UniqueKeyContainerName));

            //Add the user to the access control list for the certificate
            FileSecurity fileControl = file.GetAccessControl();
            NTAccount account = new NTAccount(user);
            fileControl.AddAccessRule(new FileSystemAccessRule(account, FileSystemRights.FullControl, AccessControlType.Allow));
            file.SetAccessControl(fileControl);
        }
    }

    /// <summary>
    /// Loads the certificate mathing the thumbprint from the specified store.
    /// </summary>
    /// <param name="thumbPrint">The thumb print of the certificate.</param>
    /// <param name="store">The store.</param>
    private X509Certificate2 LoadCertificateFromStore(string thumbPrint, X509Store store)
    {
        X509Certificate2 cert = null;

        try
        {
            //fetch the certificates in the store
            store.Open(OpenFlags.MaxAllowed);

            //locate by the specified thumbprint
            var results = store.Certificates.Find(X509FindType.FindByThumbprint, thumbPrint, true);

            if (results.Count > 0)
            {
                cert = results[0];
            }
            else
            {
                throw new FileNotFoundException("No certificate was found matching the specified thumbprint");
            }
        }
        finally
        {
            store.Close();
        }

        return cert;
    }

    /// <summary>
    /// Finds the key location.
    /// </summary>
    /// <param name="keyFileName">Name of the key file.</param>
    /// <returns></returns>
    private string FindKeyLocation(string keyFileName)
    {
        string location = string.Empty;

        //start the search from the common application folder
        string root = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
        string commonLocation = Path.Combine(root, CommonApplicationKeys);

        //filter for the key name
        var keys = Directory.GetFiles(commonLocation, keyFileName);

        if (keys.Length > 0)
        {
            location = commonLocation;
        }
        else
        {
            //now try the personal application folder
            root = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
            string privateLocation = Path.Combine(root, PersonalKeys);

            var subFolders = Directory.GetDirectories(privateLocation);

            if (subFolders.Length > 0)
            {
                foreach (string folder in subFolders)
                {
                    //filter for the key
                    keys = Directory.GetFiles(folder, keyFileName);

                    if (keys.Length != 0)
                    {
                        location = folder;
                    }
                }
            }
            else
            {
                throw new InvalidOperationException("Private key exists but is not accessible");
            }
        }

        return location;
    }
}
4

1 回答 1

0

我现在可以确认上面的代码确实有效。它似乎不起作用的原因是我没有授予另一个应用程序池帐户访问证书的权限。一旦完成,从那里开始一切都是美好的。

于 2012-08-28T07:19:45.587 回答