Набор ключей STS не существует даже после предоставления разрешения MMC

Мое приложение создает виртуальные каталоги на лету, а также пул приложений для веб-приложений с поддержкой STS, которые работают в этих виртуальных каталогах. Пулы приложений запускаются под учетной записью ApplicationPoolIdentity (IIS APPPOOL\MyAppPool). И я пытался найти способы программно предоставить доступ к установленному сертификату.

Мой первый подход состоял в том, чтобы использовать командный файл, выполняющий WinHttpCertCfg. Однако этот подход будет работать только для тех учетных записей пула приложений, которые были "активированы". Под "активированным" я имею в виду, что я хотя бы раз просмотрел новое приложение. Пока это не произойдет - WinHttpCertCfg всегда возвращает сообщение "Дескриптор недействителен".

Следующий подход, который я попробовал, был основан на решении, полученном здесь. Это решение работает в том смысле, что при просмотре сертификата в MMC и выборе "Управление ключами сертификата" отображаются учетные записи пула приложений. Даже когда я запускаю WinHttpCertCfg, чтобы вывести список учетных записей с доступом, в списке появляются новые пулы приложений.

Но после всего этого... я все еще получаю "набор ключей не существует", когда я просматриваю веб-приложение.

Сейчас я сосредоточен на исправлении второго подхода. Вот моя модификация к оригинальному коду

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

1 ответ

Решение

Теперь я могу подтвердить, что приведенный выше код действительно работает. Причина, по которой оказалось, что она не работает, была в том, что была другая учетная запись пула приложений, которой я не предоставил доступ к сертификату. Как только это было сделано, с этого момента все стало радужно.

Другие вопросы по тегам