Microsoft Corporation
目标
本模块用于:
适用于:
本模块适用于下列产品和技术:
-
Microsoft® Windows® XP 或 Windows 2000 Server(带 Service Pack 3)以及更高版本的操作系统
-
Microsoft Internet 信息服务 5.0
-
Microsoft 证书服务(如果需要生成自己的客户端证书)
-
Microsoft .NET Framework 版本 1.0(带 Service Pack 2)
-
Microsoft SQL Server™ 2000(带 Service Pack 2)以及更高版本
-
Microsoft Visual C#® .NET 开发工具
本模块的使用方法
要最大程度的使用本模块:
-
必须有使用 Visual C# .NET 和 Microsoft Visual Studio_ .NET 开发系统的经验。
-
必须有使用 ASP.NET 开发 Web 应用程序的经验。
-
必须有开发和实现在 Enterprise Services (COM+) 上下文中运行的服务组件的经验。
-
必须有开发和实现 Windows 服务的经验。
-
必须了解如何使用 Windows 管理工具创建 Windows 用户帐户。
-
必须能够访问证书颁发机构 (CA),如 Microsoft 证书服务(如果要生成新的客户端证书)。
-
如果不愿生成自己的证书,则必须决定从哪个商业 CA 申请一个客户端证书。某些 CA 将对此服务收费。
-
必须有一个安装了 SSL 证书的 Web 服务器。有关详细信息,请参阅“How To Set Up SSL on a Web Server”。
-
阅读模块“Security Model for ASP.NET Applications”,此模块介绍了客户端证书并将其与其他形式的身份验证进行比较。
本页内容
摘要
预备知识
创建一个简单的 Web 服务
将 Web 服务虚拟目录配置为需要客户端证书
创建运行服务组件的自定义帐户
为自定义帐户申请一个客户端证书
使用浏览器测试客户端证书
将客户端证书导出到文件
开发用于调用 Web 服务的服务组件
配置和安装服务组件
开发一个 Web 应用程序来调用服务组件
其他资源
摘要
客户端证书为 Web 应用程序提供了一种出色的身份验证机制。浏览器或其他客户端应用程序必须提供有效的证书才能被授予对应用程序的访问权,从而使客户端无需再提供用户名和密码。这就使得在创建由其他客户端应用程序访问的安全 Web 服务时,客户端证书非常有用。
本模块描述了如何调用一个配置为需要 Web 应用程序颁发客户端证书的 Web 服务。
预备知识
在使用客户端证书时,您的应用程序还可从创建客户端应用程序和 Web 服务之间的安全信道(使用安全套接字层 [SSL])中获益。这使您能够安全地将保密信息发送给 Web 服务,或从 Web 服务送回。SSL 确保了消息的完整性和保密性。
注 本模块中的信息还适用于 ASP.NET 和 IIS 宿主的远程组件。
为什么使用服务组件?
本模块中提供的解决方案使用了一个服务组件,该服务组件配置为使用自定义服务帐户在 Enterprise Services 服务器应用程序中运行。ASP.NET Web 应用程序调用服务组件,这会导致对 Web 服务的调用(传递一个客户端证书)。此解决方案的配置如图 1 所示。
图 1. ASP.NET 调用一个服务组件来调用 Web 服务
这种安排确保了系统在与 Web 服务通信时有权访问用户配置文件。最初的 SSL 握手需要这种方式。
注 用于运行 Web 应用程序的 ASPNET 帐户具有“拒绝交互登录”特权,从而禁止使用此帐户进行交互式登录。因此,此帐户没有用户配置文件。不要向 ASPNET 帐户(或用于运行 Web 应用程序的任何帐户)授予交互登录功能。在配置帐户来运行 Web 应用程序时,应始终遵循最小特权原则,尽可能少地向帐户授予特权。有关详细信息,请参阅“How To Create a Custom Account to Run ASP.NET”。
为什么需要用户配置文件?
在您申请使用需要客户端证书的 Web 服务时,客户端和服务器之间会发生 SSL 握手。要交换的一些组件有服务器证书、客户端证书以及由客户端生成的“预主机密 (pre-master secret)”。稍后在协议中使用此机密来生成“主机密 (master secret)”。
为了让服务器验证证书的提交者是否确实是私钥的持有者,客户端必须使用私钥加密预主机密,并将已加密的预主机密发送给服务器。为了使系统获得客户端的私钥来对预主机密进行签名,系统必须从客户端的密钥存储区中获得私钥。密钥存储区位于客户端的配置文件中,必须加载此配置文件。
注 在本模块中,Web 服务计算机(它宿主 Web服务)被命名为“WSServer”,Web 服务客户端计算机(它宿主 ASP.NET Web 应用程序和服务组件)被命名为“WSClient”。
创建一个简单的 Web 服务
要在 Web 服务宿主计算机上创建一个简单的 Web 服务,请执行下列操作:
-
启动 Visual Studio .NET 并创建一个新的名为 SecureMath 的 Visual C# ASP.NET Web 服务应用程序。
-
将 service1.asmx 重命名为 math.asmx。
-
打开 math.asmx.cs 并将 Service1 类重命名为 math。
-
将下列 Web 方法添加到 math 类。
[WebMethod]
public long Add(long operand1, long operand2)
{
return (operand1 + operand2);
}
-
在 Build 菜单上,单击 Build Solution 来创建 Web 服务。
将 Web 服务虚拟目录配置为需要客户端证书
此过程使用 Internet 信息服务将 Web 服务的虚拟目录配置为用于 SSL 并需要证书。
此过程假定您在 Web 服务器上安装了有效的证书。有关安装 Web 服务器证书的详细信息,请参阅“How To Setup SSL on a Web Server”。
要将 Web 服务虚拟目录配置为需要客户端证书,请执行下列操作:
-
启动 Web 服务宿主计算机上的 Internet 信息服务。
-
导航到 SecureMath 虚拟目录。
-
右键单击 SecureMath,然后单击 Properties。
-
单击 Directory Security 选项卡。
-
在 Secure communications 下,单击 Edit。
如果 Edit 不可用,最可能的原因是尚未安装 Web 服务器证书。
-
选择 Require secure channel (SSL) 复选框。
-
选择 Require client certificates 选项。
-
单击 OK,然后再次单击 OK。
-
在 Inheritance Overrides 对话框中,单击 Select All,然后单击 OK 关闭 SecureMath 属性对话框。
此操作将新的安全性设置应用到虚拟目录根下的所有子目录。
创建运行服务组件的自定义帐户
此过程在 Web 服务客户端计算机上创建一个新的用户帐户,您将使用这个帐户运行调用 Web 服务的服务组件。
要创建运行服务组件的自定义帐户,请执行下列操作:
-
用强密码在客户端计算机上创建一个新的用户帐户。清除 User must change password at next logon 复选框,然后选择 Password never expires 选项。
-
将此帐户添加到 Administrators 组。
用于加载用户配置文件的帐户必须是本地计算机上的管理员。
为自定义帐户申请一个客户端证书
在此过程中,您将使用新的自定义帐户登录到客户端计算机。然后,将发出要获得证书的申请。此过程假定您使用 Microsoft 证书服务。如果您没有使用 Microsoft 证书服务创建新证书,则在使用自定义帐户登录的同时,向您希望的 CA 发出要获得客户端证书的申请并安装证书。
此过程还假定已将 Microsoft 证书服务配置为自动颁发证书来响应证书申请。还可以将其配置为挂起请求,这要求管理员明确颁发证书。
要检查 Microsoft 证书服务设置,请执行下列操作:
-
在 Microsoft 证书服务计算机上,单击 Administrative Tools 程序组中的 Certification Authority。
-
展开 Certification Authority (Local),右键单击证书颁发机构,然后单击 Properties。
-
单击 Policy Module 选项卡,然后单击 Configure。
-
检查默认操作。
下列过程假定已选中 Always issue the certificate。
要为自定义帐户请求一个客户端证书,请执行下列操作:
-
注销客户端计算机并使用自定义帐户重新登录。
此操作强制为自定义帐户创建用户配置文件。
-
为了申请客户端证书,请浏览 CA。例如,如果您的 CA 位于 CAServer 计算机上,则浏览到下列位置。
http://caserver/certsrv
-
单击 Request a certificate,然后单击 Next。
-
确保选中 User Certificate,然后单击 Next。
-
单击 Submit。
生成申请并发送到 CA 进行处理。
-
在证书颁发且您收到来自 CA 服务器的响应后,单击 Install this certificate。
-
确保颁发 CA 的证书作为受信任的根证书颁发机构安装在本地计算机上。
要确认此操作,请执行下列步骤:
-
在任务栏上,单击 Start 按钮,然后单击 Run。
-
键入 mmc,然后单击 OK。
-
在 File 菜单上,单击 Add/Remove Snap-in。
-
单击 Add。
-
单击 Certificates,然后单击 Add。
-
单击 Computer account,然后单击 Next。
-
单击 Local Computer: (the computer this console is running on),然后单击 Finish。
-
单击 Close,然后单击 OK。
-
在 MMC 管理单元的左侧窗格中,展开 Certificates (Local Computer)。
-
展开 Trusted Root Certification Authorities,然后单击 Certificates。
-
确认已列出了 CA 证书。
如果未列出 CA,则执行下列步骤:
-
浏览到 http://caserver/certsrv。
-
单击 Retrieve the CA certificate or certificate revocation list,然后单击 Next。
-
单击 Install this CA certification path。
使用浏览器测试客户端证书
在此过程中,您将浏览到 Web 服务,以便确认服务器证书或客户端证书没有问题。
要使用浏览器测试客户端证书,请执行下列操作:
-
使用 Internet Explorer 并导航到 https://server/SecureMath/Math.asmx。
确保您指定了“https”,因为此站点配置为需要 SSL。
-
Client Authentication 对话框应显示出来。选择客户端证书,然后单击 OK。
-
确认在浏览器内成功地显示了 Web 服务测试页。
如果看到图 2 所示的对话框,则需要将证书颁发机构的证书安装到如前一过程中所述的 Trusted Root Certification Authorities 存储中。
图 2. Security Alert 对话框
将客户端证书导出到文件
此过程将客户端证书导出到文件。随后,当服务组件在需要将证书传递给 Web 服务时,将检索此文件。
要将客户端证书导出到文件,请执行下列操作:
-
在 Internet Explorer 中,单击 Tools 菜单上的 Internet Options。
-
单击 Content 选项卡。
-
单击 Certificates。
-
单击客户端证书,然后单击 Export。
-
单击 Next 跳过“证书导出向导”的欢迎对话框。
-
确保选中了 No, do not export the private key,然后单击 Next。
-
确保选中了 DER encoded binary X.509 (.CER),然后单击 Next。
您必须使用此格式,因为 .NET Framework 不支持 Base-64 或 PKCS #7 格式。
-
输入一个导出文件名。注意 .cer 导出文件的位置,因为在后续过程中将再次需要此位置。
-
单击 Next,然后单击 Finish 导出此证书。
-
关闭 Internet Explorer。
-
注销此计算机并使用常规开发帐户重新登录。
开发用于调用 Web 服务的服务组件
此过程创建了一个新的 Visual C# 类库应用程序以及用于调用 Web 服务的服务组件。此过程假定您正在使用客户端计算机。
要开发用于调用 Web 服务的服务组件,请执行下列操作:
-
启动 Visual Studio.NET 并创建一个新的名为 WebServiceRequestor 的 Visual C# 类库项目。
-
添加一个对 SecureMath Web 服务的 Web 引用。
重要事项 在添加 Web 引用前,必须临时地将 Web 服务的虚拟目录重新配置为不需要客户端证书(尽管仍需要 SSL)。在成功添加 Web 引用后,将虚拟目录配置更改回需要客户端证书。
实际上,如果站点需要客户端证书,则服务的发行商将 WSDL 作为独立的脱机文件使用,服务的使用者可以使用此文件来创建代理。
在 Add Web Reference 对话框中,确保在指定 Web 服务位置时指定 https。如果失败,则会产生错误,因为 Web 服务虚拟目录配置为需要 SSL。
-
添加一个到 System.EnterpriseServices 程序集的引用。
-
将 class1.cs 重命名为 ProfileManager.cs。
-
将下列类定义添加到 ProfileManager.cs(替换主干 class1 类)。ProfileManager 类使用 P/Invoke 调用 LoadUserProfile 和 UnloadUserProfile Win32 API。
internal class ProfileManager
{
[DllImport("Userenv.dll", SetLastError=true,
CharSet=System.Runtime.InteropServices.CharSet.Auto)]
internal static extern bool LoadUserProfile(IntPtr hToken,
ref PROFILEINFO lpProfileInfo);
[DllImport("Userenv.dll", SetLastError=true,
CharSet=System.Runtime.InteropServices.CharSet.Auto)]
internal static extern bool UnloadUserProfile(IntPtr hToken,
IntPtr hProfile);
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct PROFILEINFO
{
public int dwSize;
public int dwFlags;
public String lpUserName;
public String lpProfilePath;
public String lpDefaultPath;
public String lpServerName;
public String lpPolicyPath;
public IntPtr hProfile;
}
}
-
将另一个名为 MathServiceComponent.cs 的类文件添加到项目中。
-
将下列 using 语句添加到 MathServiceComponent.cs 的现有 using 语句下。
using System.Net;
using System.Web.Services;
using System.Security.Principal;
using System.EnterpriseServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using WebServiceRequestor.WebReference1;
-
添加下列类定义,它提供了一个公共 CallMathWebService 方法。在稍后的过程中,您将从客户端 ASP.NET Web 应用程序中调用此方法。
注 在下列代码中,用在步骤 3“Create a Custom Account for Running the Serviced Componen”中创建的自定义帐户的名称替换用于加载用户配置文件的帐户名。
// This class calls the web service that requires a certificate.
public class MathServiceComponent : ServicedComponent
{
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL,
ref IntPtr DuplicateTokenHandle);
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private extern static bool CloseHandle(IntPtr handle);
// Calls the Web service that requires client certificates
// certFilepath points to the .cer file to use
// url is the Web service url
// operand1 and operand2 are the parameters to pass to the Web service
public long CallMathWebService(String certFilepath,
String url, int operand1, int operand2)
{
bool retVal = false;
// Need to duplicate the token. LoadUserProfile needs a token with
// TOKEN_IMPERSONATE and TOKEN_DUPLICATE.
const int SecurityImpersonation = 2;
IntPtr dupeTokenHandle = DupeToken(WindowsIdentity.GetCurrent().Token,
SecurityImpersonation);
if(IntPtr.Zero == dupeTokenHandle)
{
throw new Exception("Unable to duplicate token.");
}
// Load the profile.
ProfileManager.PROFILEINFO profile = new ProfileManager.PROFILEINFO();
profile.dwSize = 32;
//TODO: Replace with custom account name created in step 3.
profile.lpUserName = @"machinename\customaccountname";
retVal = ProfileManager.LoadUserProfile(dupeTokenHandle, ref profile);
if(false == retVal)
{
throw new Exception("Error loading user profile. " +
Marshal.GetLastWin32Error());
}
// Instantiate the Web service proxy
math mathservice = new math();
mathservice.Url = url;
String certPath = certFilepath;
mathservice.ClientCertificates.Add(
X509Certificate.CreateFromCertFile(certPath));
long lngResult = 0;
try
{
lngResult = mathservice.Add(operand1, operand2);
}
catch(Exception ex)
{
if(ex is WebException)
{
WebException we = ex as WebException;
WebResponse webResponse = we.Response;
throw new Exception("Exception calling method. " + ex.Message);
}
}
ProfileManager.UnloadUserProfile(WindowsIdentity.GetCurrent().Token,
profile.hProfile);
CloseHandle(dupeTokenHandle);
return lngResult;
}
private IntPtr DupeToken(IntPtr token, int Level)
{
IntPtr dupeTokenHandle = new IntPtr(0);
bool retVal = DuplicateToken(token, Level, ref dupeTokenHandle);
if (false == retVal)
{
return IntPtr.Zero;
}
return dupeTokenHandle;
}
} // end class
-
在 Build 菜单上,单击 Build Solution。
配置和安装服务组件
此过程将配置服务组件,生成强名称,将其安装到全局程序集缓存中并注册到 COM+。
-
打开 assemblyinfo.cs 并将下列 using 语句添加到现有 using 语句下。
using System.EnterpriseServices;
-
将下列程序集级属性添加到 assemblyinfo.cs,将服务组件配置为在 COM+ 服务器应用程序中运行。
[assembly: ApplicationActivation(ActivationOption.Server)]
-
打开一个命令提示符窗口并更改为当前项目的目录。
-
使用 sn.exe 实用工具来生成含有公共-私有密钥对的密钥文件。
sn.exe -k WebServiceRequestor.snk
-
返回到 Visual Studio .NET。
-
定位到 assemblyinfo.cs 内的 [AssemblyKeyFile] 属性,并将其修改为引用项目目录内的密钥文件,如下所示。
[assembly: AssemblyKeyFile(@"..\..\WebServiceRequestor.snk")]
-
在 Build 菜单上,单击 Build Solution。
-
返回到命令提示符,然后运行下列命令将程序集添加到全局程序集缓存。
gacutil.exe /i bin\debug\webservicerequestor.dll
-
运行下列命令来将此程序集注册到 COM+。
regsvcs bin\debug\webservicerequestor.dll
-
启动 Component Services(位于 Administrative Tools 程序组下)。
-
展开 Component Services、Computers 和 My Computer 节点。
-
展开 COM+ Applications 文件夹。
-
右键单击 WebServiceRequestor,然后单击 Properties。
-
单击 Identity 选项卡。
-
选择 This user: 选项并输入对应于此前创建的自定义帐户的帐户详细信息。
此操作将 COM+ 应用程序配置为使用自定义帐户来运行。
-
单击 OK 关闭 Properties 对话框。
-
关闭组件服务。
开发一个 Web 应用程序来调用服务组件
此过程创建一个简单的 ASP.NET Web 应用程序,可将其用作客户端应用程序来调用 Web 服务(通过服务组件)。
要开发一个 Web 应用程序来调用服务组件,请执行下列操作:
-
在 Web 服务客户端计算机上,创建一个新的名为 SecureMathClient 的 Visual C# ASP.NET Web 应用程序。
-
添加一个对 System.EnterpriseServices 的引用。
-
添加一个对 WebServiceRequestor 服务组件的引用。
浏览到位于 bin\debug 文件夹内 WebServiceRequestor 项目目录下的 WebServiceRequestor.dll。
-
打开 WebForm1.aspx.cs 并将下列 using 语句添加到现有的 using 语句下。
using WebServiceRequestor;
-
在 Designer 模式下查看 WebForm1.aspx 并使用下列 ID 创建一个如图 3 所示的窗体:
-
operand1
-
operand2
-
result
-
add
图 3. Web 窗体控件排列
-
双击 Add 创建一个按钮单击事件处理程序。
-
将下列代码添加到事件处理程序中:
注 将 certPath 字符串设置为在步骤 6“将客户端证书导出到文件”期间导出的证书文件的位置。
将带有 HTTPS URL 的 url 字符串设置为 Web 服务。
private void add_Click(object sender, System.EventArgs e)
{
// TODO: Replace with a valid path to your certificate
string certPath = @"C:\CustomAccountCert.cer";
// TODO: Replace with a valid URL to your Web service
string url = "https://wsserver/SecureMath/math.asmx";
MathServiceComponent mathComp = new MathServiceComponent();
long addResult = mathComp.CallMathWebService( certPath,
url,
Int32.Parse(operand1.Text),
Int32.Parse(operand2.Text));
result.Text = addResult.ToString();
}
-
在 Build 菜单上,单击 Build Solution。
-
运行此应用程序。输入要进行加法运算的两个数,然后单击 Add。
Web 应用程序将调用服务组件,此服务组件使用 SSL 并传递客户端证书来调用 Web 服务。