我的应用程序动态创建虚拟目录以及在这些虚拟目录中运行的启用STS的Web应用程序的应用程序池。应用程序池在ApplicationPoolIdentity帐户(IIS APPPOOL \ MyAppPool)下运行。我一直在试图找出以编程方式授予对已安装证书的访问权限的方法。
我的第一种方法是使用执行WinHttpCertCfg的批处理文件。但是,此方法仅适用于已“激活”的应用池帐户。通过“激活”,我的意思是我已经浏览了新应用程序至少一次。在此之前 - WinHttpCertCfg始终返回消息“句柄无效”。
我尝试的下一个方法是基于solution obtained from here。此解决方案的工作原理是,当我在MMC中浏览证书并选择“管理证书密钥”时,将列出应用程序池帐户。即使我运行WinHttpCertCfg列出具有访问权限的帐户 - 也会列出新的应用程序池。
但是毕竟......当我浏览网络应用程序时,我仍然得到“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;
}
}
答案 0 :(得分:0)
我现在可以确认上面的代码确实有效。它似乎无法正常工作的原因是我还没有授予访问证书权限的另一个应用程序池帐户。一旦完成,那里的一切都很美好。