Набор ключей 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 ответ
Теперь я могу подтвердить, что приведенный выше код действительно работает. Причина, по которой оказалось, что она не работает, была в том, что была другая учетная запись пула приложений, которой я не предоставил доступ к сертификату. Как только это было сделано, с этого момента все стало радужно.