最近在学习WCF X.509证书验证,想实现使用证书实现服务端和客户端的双向认证,实现原理是利用了数字证书包含的一对非对称密钥来实现数字签名及加密。所谓非对称密钥是采用两个密钥将加密和解密能力分开:一个公钥和一个私钥,公钥可解密私钥加密的信息,私钥也可以解密公钥加密的信息,前者用于数字签名后者用于信息加密,但从一个密钥是不可能分析出另一个密钥。利用非对称密钥的特点,我们将私钥签名的证书安装在服务器端,把公钥签名的证书放在客户端,就可以实现在客户端对服务器端的身份认证。反之,我们将公钥签名的证书安装在服务器端,把私钥签名的证书放在客户端,就可以实现在服务器端对客户端的身份认证。所以利用非对称密钥的特点,我们可以使用证书实现服务器端和客户端的双向认证。具体步骤如下:
【1】制作证书
(1) 需要创建一个证书作为证书认证中心(CA)的根证书,它用来签名所有在该CA注册的其他证书。这里使用makecert 工具:Microsoft Visual Studio 2010-->Visual Studio Tools-->Visual Studio 2010 命令提示行。在Visual Studio命令窗口输入以下命令创建证书
makecert -n "CN=WCFRootCA" -r -sv WCFRootCA.pvk WCFRootCA.cer
生成证书时会提示设置私钥密码,这个密码是证书启用、导出、导入时的保护密码,为了防止其他人以不法手段窃取钥匙。我这里设置成WCF1234.
相关命令参数含义可以在命令窗口输入:makecert查看。制作的证.书中扩展名为.cer的文件是公钥,而.pvk文件则是私钥;还有一种以.pfx为扩展名的文件是密钥交换文件既可以包含公钥和也可以包含私钥(也可以根据设置,让.pfx文件只包含公钥,不包含私钥)。
接着,将根证书分别安装到服务端和客户端的计算机上。运行“mmc”命令添加证书单元,在受信任的根证书颁发机构节点下的证书节点导入根证书,根证书就安装完毕了。
(2) 将 WCFRootCA 安装的服务器和客户机上
运行“mmc”命令添加证书单元,将证书WCFRootCA.cer安装在本地计算机(Local Machine)—>受信任的根证书颁发机构(Trusted Root Certification Authorities)中。
(3) 在服务器和客户端上创建并安装服务证书
因为要采用双向认证,所以服务器端和客户端都各需要一个证书,先通过在服务器端执行下列命令,来创建一个由根证书WCFRootCA签名的服务器端证书:
makecert -sk AutoWCF -iv WCFRootCA.pvk -n "CN=WCFServer" -ic WCFRootCA.cer -sr localmachine -ss my -sky exchange -pe WCFServer.cer
执行这个命令后,服务证书WCFServer.cer被安装到服务器端上的“本地计算机(Local Machine)—>个人(Personal)中,可以在MMC中看到
注意:-sk 指定主题的密钥容器位置,该位置包含私钥。如果密钥容器不存在,系统将创建一个,但每个证书的密钥容器时唯一的。
然后将该服务器端证书通过MMC导出(注意:导出服务器端证书WCFServer时要选择不导出私钥。这样私钥只留在服务器端),然后将导出的.pfx文件copy到客户端,并在客户端上运行“mmc”命令,在打开的MMC中将刚才在服务器端导出的.pfx证书文件安装(导入)在客户端本地计算机(Local Machine)上。
最后,还需在服务器端和客户端上分别执行下列命令:
FindPrivateKey.exe My LocalMachine –n "CN= WCFServer "
给在服务器端的WCF进程访问服务端证书WCFServer私钥的权利,和给客户端的WCF进程访问服务端证书WCFServer公钥的权利。这样现在就实现了在客户端验证服务器端身份的数字签名了。
反之可以用同样的方法,在客户端先使用命令makecert创建一个由根证书WCFRootCA签名的客户端证书WCFClient,然后在MMC中导出只带公钥的.pfx文件copy给服务器端,并在服务器端用MMC进行安装(导入),然后同样在服务器端和客户端上使用命令:
FindPrivateKey.exe My LocalMachine –n "CN= WCFClient "
来授予WCF进程访问证书WCFClient的权限,从而又实现了在服务器端验证客户端身份的数字签名。
【2】配置文件编写
证书设置完成后就可以来配置所选用的安全策略了,WCF既可以使用代码完成也可以通过配置文件完成。使用配置文件配置结构清晰,易于修改,所以此例采用这种方式。
使用消息安全模式,客户端采用证书身份验证策略,服务器端的证书验证方式配置信息如下
< bindings >
< wsHttpBinding >
< binding name = " BindingConfigration " >
< security mode = " Message " >
< transport clientCredentialType = " None " />
< message clientCredentialType = " Certificate " negotiateServiceCredential = " true " establishSecurityContext = " true " />
</ security >
</ binding >
</ wsHttpBinding >
</ bindings >
这个配置要应用到服务的终结点配置上才会生效。
除此之外,还要在服务器端的行为中配置使用服务器端证书WCFServer的相关信息。
< behaviors >
< serviceBehaviors >
< behavior name = " WCFService.WCFServiceBehavior " >
< serviceMetadata httpGetEnabled = " true " />
< serviceDebug includeExceptionDetailInFaults = " false " />
< serviceCredentials >
< serviceCertificate storeName = " My " x509FindType = " FindBySubjectName " findValue = " WCFServer " storeLocation = " LocalMachine " />
</ serviceCredentials >
</ behavior >
这里指定了服务器端证书WCFServer的查找位置和查找条件,服务器端会根据该信息查找到证书WCFServer的公钥来对消息进行签名,然后将签名后的消息发送给客户端进行验证。将此配置信息应用到服务器端,服务器端的安全配置就算完成了。
客户端配置与之相似,也要配置绑定和终结点服务行为。客户端上终结点服务行为中的证书设置应与客户端证书WCFClient的相关信息相符。
<behaviors >
< endpointBehaviors >
< behavior name = " endpointBehavior " >
< clientCredentials >
< clientCertificate storeName = " My "
x509FindType = " FindBySubjectName "
findValue = " WCFClient "
storeLocation = " CurrentUser " />
</ clientCredentials >
</ behavior >
</ endpointBehaviors >
</ behaviors >
这里指定了客户端证书WCFClient的查找位置和查找条件,客户端会根据该信息查找到证书WCFClient的公钥来对消息进行签名,然后将签名后的消息发送给服务器端进行验证。将此配置信息应用到客户端,客户端的安全配置也就完成了。
本文改编自WCF X.509证书双向认证小结