问题描述
我需要在IIS中运行ASP.NET Core 2.2的Web服务器上对PDF文档进行数字签名。该Web应用程序正在与服务用户一起运行,并且模拟应在代码中完成。问题是,我无法通过X509Store类访问用户证书。我试图创建一个最小的示例,该示例仅模拟另一个用户并在控制台中输出证书。我以管理员身份运行它,但未找到证书。
我在“计算机配置> Windows设置>安全设置>本地策略>用户权限管理”下的“本地组策略编辑器”(gpedit.msc)中授予的权限:
- 充当操作系统的一部分:添加了ServiceUser
- 允许本地登录:添加了UserToBeImpersonated
- 作为批处理作业登录:添加了UserToBeImpersonated
- 创建令牌对象:添加了ServiceUser
- 替换流程级别令牌:添加了ServiceUser
- 调整进程的内存配额:添加了ServiceUser
对于模拟,我使用了https://github.com/mj1856/SimpleImpersonation中的SimpleImpersonation并将其扩展为还可以加载用户个人资料,如下所示:
Docs all and because but for has i i'm the was
144 0 9 0 5 5 2 0 0 17 1
236 0 7 4 2 4 5 0 0 15 7
237 1 11 1 3 3 2 0 0 30 2
246 0 9 0 0 6 1 0 0 18 2
248 1 6 1 1 2 0 0 0 27 4
273 0 5 2 2 4 1 0 0 21 1
368 0 1 0 1 0 0 0 0 11 2
489 0 5 0 0 4 0 0 0 8 0
502 0 6 0 1 5 0 0 0 13 0
704 0 5 1 0 3 2 0 0 21 0
测试的主要代码如下:
public static void RunAsUser(UserCredentials credentials,LogonType logonType,Action action)
{
// this method tells Windows to dynamically determine where to look for the HKEY_CURRENT_USER registry hive,// rather than using the cached location from when the process was initially invoked
RegDisablePredefinedCache();
using (var tokenHandle = credentials.Impersonate(logonType))
using (var profileToken = credentials.ImpersonateUserProfile(tokenHandle.DangerousGetHandle()))
{
RunImpersonated(tokenHandle,_ => action());
}
}
internal UserProfileToken ImpersonateUserProfile(IntPtr tokenHandle)
{
var tokenDuplicate = IntPtr.Zero;
// Not sure if the token needs to be duplicated or not
if (DuplicateToken(tokenHandle,2,ref tokenDuplicate) == 0)
HandleError(tokenHandle);
// Load User profile
var profileInfo = new ProfileInfo();
profileInfo.dwSize = Marshal.SizeOf(profileInfo);
profileInfo.lpUserName = _username;
profileInfo.dwFlags = 1;
// LoadUserProfile() failed
if (!LoadUserProfile(tokenDuplicate,ref profileInfo))
HandleError(tokenDuplicate);
// LoadUserProfile() failed - HKCU handle was not loaded
if (profileInfo.hProfile == IntPtr.Zero)
HandleError(tokenDuplicate);
return new UserProfileToken() { ProfileInfo = profileInfo,Token = tokenDuplicate };
}
public class UserProfileToken : IDisposable
{
public ProfileInfo ProfileInfo { get; set; }
public IntPtr Token { get; set; }
~UserProfileToken()
{
Dispose();
}
private bool isDisposed = false;
public void Dispose()
{
if (isDisposed) return;
isDisposed = true;
UnloadUserProfile(Token,ProfileInfo.hProfile);
}
}
[DllImport("userenv.dll",SetLastError = true,CharSet = CharSet.Auto)]
public static extern bool LoadUserProfile(IntPtr hToken,ref ProfileInfo lpProfileInfo);
[DllImport("Userenv.dll",CallingConvention = CallingConvention.Winapi,CharSet = CharSet.Auto)]
public static extern bool UnloadUserProfile(IntPtr hToken,IntPtr lpProfileInfo);
[DllImport("advapi32.dll",CharSet = CharSet.Auto,SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken,int impersonationLevel,ref IntPtr hNewToken);
[DllImport("advapi32.dll",CharSet = CharSet.Auto)]
public static extern int RegDisablePredefinedCache();
不幸的是,当相同的代码(没有模拟)在目标用户计算机上显示两个证书时,找不到证书。
我在服务器上发现的另一件有趣的事是C:\ Users 下的用户配置文件目录没有用户名,但有一些中文?字符。在系统设置中检查用户配置文件时,用户名正确无误。
解决方法
似乎模拟(带有加载配置文件)不会触发自动注册以从证书颁发机构接收证书。必须单独使用以下方法完成此操作:
using CERTENROLLLib;
public static void EnrollUserCertificateByTemplate(string templateName,string subjectName,string friendlyName = null)
{
var enrollment = new CX509Enrollment();
// Set target store
enrollment.InitializeFromTemplateName(X509CertificateEnrollmentContext.ContextUser,templateName);
var request = enrollment.Request;
var innerRequest = request.GetInnerRequest(InnerRequestLevel.LevelInnermost);
var innerRequestPkcs10 = innerRequest as IX509CertificateRequestPkcs10;
// Set the subject name
var distinguishedName = new CX500DistinguishedName();
distinguishedName.Encode(subjectName,X500NameFlags.XCN_CERT_NAME_STR_NONE);
innerRequestPkcs10.Subject = distinguishedName;
// Set the friendly name
if (friendlyName != null) enrollment.CertificateFriendlyName = friendlyName;
// Enroll for the certificate into MY store if it is successfully issued by CA
enrollment.Enroll();
}
此外,您需要检查用户证书存储中是否已经存在有效的证书。否则,您每次假冒时都要添加证书(并执行上面的代码)。
我需要做的Nonothr事情是向执行用户授予管理权限。我的问题中发布的所有其他权限都可以删除。