.Net(C#)访问X509证书加密的Webservice(三)--客户端访问WSE

Web Service 、WS-Security、Java和.net的互通

在互连网应用中Web Service已经得到了广泛的认同,同时也是因为这种广泛的应用,使得Web Service在规范化方面越来越成熟。企业和企业之间的信息交互,很重要一点就是信息的安全性,电子商务等互连网应用这方面的需求更为突出,如果没有安全的保证,没有客户或者企业愿意将信息在网上交互,同时也不会信任任何接受到的信息。然而,作为SOA的有效技术手段,Web Service的动态性很强,服务的开发者无法预料到服务将在什么环境下被使用,因此服务的安全性变得更加复杂。

在考虑安全性方面主要有三个关键性的概念:

机密性(Confidentiality),完整性(Integrity),身份鉴别(Authentication)。

机密性:除了指定的接受者,其他人无法查看消息的内容。通常会使用密钥(对称或非对称)对消息加密,从而保证消息的机密性。

完整性:确保消息在传输的过程中没有被修改。即保证消息的接收者接收到的消息与消息发送者最初发送的消息完全一致。通常会对消息做摘要(Digest),再使用密钥(对称,非对称)加密摘要,从而保证消息的完整性。

身份鉴别:确保消息发送者的身份与消息中所声称的用户身份一致。最简单的做法就是让用户同时发送用户名和密码,服务方确认密码的有效性从而判断该用户身份是否与用户名所代表的一致。然而在实际的应用中的做法通常不会如此简单,此时往往会使用密钥对一段信息加密,服务方通过解密此信息确认用户的身份。

那么如何选择使用对称密钥还是非对称密钥?什么时候使用公钥,什么时候使用私钥?首先由于对称密钥的加密解密速度比非对称的密钥快很多(大约1000倍),所以在加密消息的时候一般都使用对称密钥。但是有一个问题就是对称密钥又如何发布呢?网络上两个没有联系的用户如何建立起一个共享的密钥?这时就需要PKI来帮助我们将这个共享的密钥建立起来,即利用非对称密钥中的公钥来加密那个共享密钥,传递到私钥的拥有方。由此可以知道:对称密钥加密普通信息,非对称密钥加密对称密钥。

在Integrity中,使用非对称密钥的私钥加密摘要,得到的东西是一个广为人知的东西—Digital Signature(数字签名). 而使用对称密钥加密摘要得到的是HMAC(Hash message authentication codes)。他们在保证消息完整性的同时,都具有身份鉴别的功能,不过前者还有一个功能---抗否认性。值得注意的是在Confidentiality中,使用非对称密钥方法是用公钥加密,私钥解密,而在Integrity中使用非对称密钥的方法恰恰相反。

在当前的安全策略上很多时候会使用到SSL和VPN,他们的好处和缺点网上都有评论,但作为和传输层无关的安全策略,WS-Security规范无疑是最具广泛的应用。IBM,BEA,Microsoft共同制定了WS-Security规范,解决了安全的三个基本问题:机密性、完整性、身份鉴别,在Web Services使用SOAP(XML 格式)作为消息封装协议的背景下,标准化组织分别制定了XML Encryption、XML Digital Signature、与SAML(XML格式的Security Token)三套规范,WS-Security则是规定了如何将以上规范组合起来以满足Web Services安全需求的一套规范。

XML Signature规范是将数字签名和XML组合而成的产物,不要以为XML Signature仅仅是将数字签名技术应用于XML文件。

XML Signature包括以下的功能:

    1.XML Signature可以对任何能够以URI形式(uniform resource identifier)定位的资源做签名。既包括与签名同在一个XML文件中的元素,也包括其他XML文件中的元素,甚至可以是非XML形式的资源(比如一个图形文件),只要能被URI定位到的资源都可以应用XML Signature. 这也代表了XML签名的对象可以是动态的变化。

    2.XML Signature可以对XML文件中的任一元素做签名,也可以对整个文件做签名。

    3.XML Signature 既可以用非对称密钥做签名(Digital Signature),也可以用对称密钥做签名(HMAC)。

具体的标签以及含义就不做解释了,不过如果要做签名策略配置,需要熟悉这些标签,这也会影响到后续开发中遇到的各种问题。另外两个规范这边就不做说明了,因为现在服务框架暂时只是提供了Signature的要求,如果内容需要加密,那么对于应用来说性能可能是不可接受的,因此需要的是利用SSL和签名结合的策略来实现完整的安全机制。

对于认证模式场景需求问题的解决

       ASF对于Web Service Security这部分只是要求了Signature,而对于Signature需要提供两种方式,UserNameToken和X.509证书。前者提供给内部的一些应用使用,后者提供给外部ISV使用,两者主要差别也就是在性能上,后者经过测试,在CPU的使用上,是6-8倍于不带Signature的Web Service。

       UserNameToken这种模式很简单,就很类似于我们平常的用户认证,用户名和密码作为明文或者加密,嵌入在Soap Header中即可,客户端根据本地保存的受信用户记录来交验是否是合法用户。

       X.509就比较特殊一些,所谓的证书,其中包括了公钥私钥对(作为签名和认证使用),证书颁发者的信息(可能是一些CA机构或者是用本地的证书生成工具自己生成),证书拥有者的身份信息,以及一些导入的受信第三方的公钥证书。当前的证书方式的签名认证主要有两种模式,一种是双方证书都是由第三方CA认证,同时双方都将对方的CA作为可信的CA,那么请求发起方将会把自己的证书放入到Soap Header中,接受方接受到请求以后,获取证书,发现是受信的CA,那么就认为请求发起方身份可信,同时在作完整性摘要校对。另一种模式就是双方的证书可以没有任何权威的CA作保证,但是双方首先就需要将附带自己公钥的证书发送给对方,对方将这附带有公钥的证书分别导入到证书管理库中(java是某个JKS,.net是windows的当前用户或者本机的证书管理文件)。双方交互的时候不需要将证书置入Soap Header中,但是需要将证书的引用标示(序列号或者是X.509SubjectKeyIdentifier)传递给服务端,服务端根据标示在本地的证书库中查找并且校验,这种模式的好处就是不需要CA的认证(省钱)同时传递的时候不需要将证书带入到Header中,节省了传递报文的大小,缺点就是双方需要互相保存对方的公钥证书到本地的证书库中,同时这种模式也为跨平台带来了很多问题。

       ASF的证书认证模式中采用了后者,因为要求每个ISV去有一个第三方CA作为授权不是很可行,但是问题就是如果ISV需要导入我们的公钥证书比较方便,但是我们需要管理那么多ISV的证书,同时又需要动态上传修改保存证书那么就不能用原有的手动导入的模式,同时由于到时候部署的服务器集群不可能都维护一份本地的证书库,因此需要用数据库手段来维护,但是在应用启动以后再要新增或者修改证书,将会比较麻烦,因此采取了扩展wss4j的策略读取方式来适应新的应用场景。

扩展的CrytoProvider类图

      .Net(C#)访问X509证书加密的Webservice(三)--客户端访问WSE 

扩展后的keystore 初始化以及signature部分流程图

.Net(C#)访问X509证书加密的Webservice(三)--客户端访问WSE

         这部分设计主要是扩展了WSS4J的CrytoProvider,扩展后的CrytoProvider重载了获取X509证书的几个方法,这里类似于AOP,通过提供ICertsLoaderHandler接口来回调获取其它存储内的certs 构建 visualkeystore,同时在作signature的时候,如果发现ISV的cert不存在于当前的visual keystore 中根据CrytoProvider的接口实现,来决定是全部刷新还是部分获取刷新当前的visual keystore中的certs。这样就可以达到了动态装载和刷新certs的要求,同时根据性能要求选择配置和实现不同的CrytoProvider。

.Net和Java的互通

       Java 的客户端和服务端Security互通很快就搞定了,只是对于一些应用场景做证书管理的扩展。然而,在经历了.net客户端调用Java发布的ws返回数组对象类型的问题以后,又一个大难题摆在了我的面前,ISV support小组和测试部的日报上把.net客户端无法在wsse的模式下调用Java 发布的 Web Service作为了重点问题,需要我支持,那么当然当仁不让的接下来尽快搞定了,虽然对.net来说,我算是新手中的新手,不过经过两天的测试,让我总结出了.net调试的技巧,那就是截包分析(感觉又回到我前几年在通信行业干活时的状况,对于对方协议不了解,又没有源码可看,那么就截报文来分析么)。开始挺乐观的,想着WS-Security微软也是参与者么,应该会严格遵守的,估计一天搞定,结果活活的折腾了两天,下面所描述的如何互通可能总的看起来应该不是很复杂,不过摸索的过程可真是很费事,google的每一条老外的信息都被我看过了,但是Question多Answer少。废话不多说,进入正题。

       首先不管是什么客户端调用什么服务端,都需要先做一件事情,准备key pairs。在Java中证书管理库可以通过Jdk提供的key tools这个工具生成jks格式的Java Key Store,可以将公钥导出,或者将公钥导入,同时可以生成秘钥对保存在证书中。在.net中可以通过makecert的工具来生成符合Public Key Cryptography Standards #12,PKCS#12标准定义,包含了公钥和私钥的二进制格式的证书形式,以pfx作为证书文件后缀,同时可以通过mmc对windows的证书存储区进行管理,导入或者导出证书。其实.net和java的互通关键问题就是出在证书格式不同以及获取证书的算法上。下面就具体的描述一下如何从Java的开发者来做好Java WebService和.net互通的工作。

 

一.准备双方的证书和公钥

首先通过Java的keytools 生成了两个jks,一个叫做alisoft.jks,另一个叫做myisvdemo.jks,这里需要注意,在使用keytools生成证书的时候会提示需要输入两个密码,一个是keystore的密码(每次对keystore作操作的时候都需要输入密码来验证),另一个是私钥保护密码(使用私钥作签名或者加密的时候需要输入),这两个密码可以设置为不同的值,不过为了后面转换为.net的pfx格式的证书需要,两者需要设置为相同,同时在早期的tomcat中使用证书也有这种限制。然后将两个证书都自签名并将公钥导出,分别叫做alisoft.rsa和myisvdemo.rsa(因为我这里用的是rsa算法,所以用了这个后缀,其实可以直接命名为.cer后缀,因为它们的类型其实就是base64编码后的没有私钥的证书,可以直接导入到windows中)。

准备好了这四份文件以后,将myisvdemo.rsa导入到alisoft.jks中(由于测试不采用上面提到的数据库存储的方式,因此直接导入作测试)。然后,通过一个叫做jks2pfx的工具包,将myisvdemo.jks转成为myisvdemo.pfx文件,通过mmc导入到本地计算机的证书管理中。  

这时候,双击这两个证书都会显示当前的Ca根证书不受信任,可以直接拖动复制增加到受信任的根证书颁发机构的证书中,就不会再显示这样的提示了。最后分别将这两个证书在复制到受信任人的证书中。至此,客户端和服务端的证书都已经准备好了,接下去就是如何配置使用.net的客户端来调用已经发布成为带有WSSE的Java Web Service了。

二.配置.net的客户端

首先,作为测试就建立了一个C#的Windows Application,然后在默认的form上面加了一个button作为激发调用服务端的事件控制载体。用.net来调用web service通常情况使用增加一个web reference来注入这个远程服务,不过这边要特别特别强调,如果你要使用WSSE的话,不要急于先建立一个Web Reference,首先要将你的安全策略配置好,然后再建立web reference,不然自己手动的要去修改后台客户端代码。那么接着来说说.net的WSE。

.net提供的WSE(Web Services Enhancements)是用来增强对于Web Service的支持配置(包括了WS-Security等规范)。当前.net的WSE最新版本是3.0的,可以很方便的集成到VS2005中,而它的2.0版本主要是针对过去的VS2003版本,不过两者都是可以用的,但是在配置上面有一些差异,同时使用的方便程度也有些差距,2.0使用起来相对3要复杂一些。不过我们这里介绍的都是通过设置WS-Policy来应用WS-Security,这样比较方便,同时代码简洁,业务逻辑和具体的WS配置分开,是一种较好的使用模式,.net也支持代码内嵌WS-Security逻辑来实现WS-Security的使用。

       我先来说一下3如何使用。安装好3以后,可以在我们的项目右键菜单中最下面看到WSE 3.0 setting,直接选择这项,弹出类似的设置页,第一页最顶上的一项选中。

.Net(C#)访问X509证书加密的Webservice(三)--客户端访问WSE 

       然后选择Policy Tab页面设置,首先选中Enable Policy,然后选择Add,输入一个Policy的名称,后面就是一个向导配置页面,第一页的选择如下图

.Net(C#)访问X509证书加密的Webservice(三)--客户端访问WSE 

       后面第一个要选择的x.509证书是本地的密码对存在的证书,也就是用来签名并向服务端发起请求的证书所在位置,选择了LocalMachine,然后再选择证书,也就是上面我导入到系统中的myisvdemo证书。然后出现如下图

.Net(C#)访问X509证书加密的Webservice(三)--客户端访问WSE  

       将配置修改成图中所示,首先不要选择支持1.1扩展,因为.net和java对1.1扩展的支持实现有所差异,特别是签名回执上,连域名都不一致,所以使用起来有问题。然后我们现在只是要用sign-only的功能,因此就选择这项。

       下一页是选择服务端响应时使用什么证书作为解析的证书,这里就选择local Machine的alisoft证书。至此Policy全部配置完成,关闭配置文件。在我们的工程里面会新增加一些文件,如下图所示:

.Net(C#)访问X509证书加密的Webservice(三)--客户端访问WSE 

       其中在references中的是引入的第三方包,主要是为了将普通的web Service的代理类转变成为支持WSSE的代理类(这也是为什么我说不要先急着建立web reference的缘故,如果先建立web reference的话),打开Reference.map中的reference.cs文件,可以看到服务代理类是继承System.Web.Services.Protocols.SoapHttpClientProtocol,而如果在配置好策略以后服务代理类就会自动继承 Microsoft.Web.Services3.WebServicesClientProtocol,此时的服务代理类才可以支持WSSE的功能,因此如果先建立reference就要手动的去修改这些客户端文件。

    然后打开wse3policyCache.config文件,这个文件就是刚才配置的Policy文件,修改establishSecurityContext="false",这个参数如果为true的话,会连续两次发送请求,后一次的请求内将不带有Policy中配置的信息(这个具体的我还不是很清楚,只是看到一个国外的人询问微软工程师的时候,认为这个是个缺陷,但是微软工程师则认为这是一个让用户集成自己配置的扩展点,不过个人认为,所谓扩展点应该是在默认没有扩展的时候不影响原有的配置)。第二需要在我们的axis2中除了配置signature还需要配置timestamp,默认每一次请求.net都会自动将timestamp作为action执行。

    打开app.config文件,这里面指定了web service的policy配置文件,这里没有什么需要修改的,只需要按照他生成的内容即可。然后建立好web Reference,这个十分简单,就是输入wsdl的URI,然后填入服务的名称即可。最后一部就是写测试代码,重新打开Form1.cs,在button的onclick事件中写入下面的测试代码:

.Net(C#)访问X509证书加密的Webservice(三)--客户端访问WSE
private void button1_Click(object sender, EventArgs e)

        {
// AccountServiceWse是你的客户段代理类,有WSSE的增强功能,参看reference.cs文件

            AccountServiceWse service = new AccountServiceWse();

            //这个客户端代理类需要指定刚才配置的策略名称  

            service.SetPolicy("clientPolicy");

            //简单的测试代码

            accountService.ArrayOfAccountBean result = service.getUserAccountArr("test");
        }
.Net(C#)访问X509证书加密的Webservice(三)--客户端访问WSE

噩梦开始了,到此为止,.net给web Service开发者带来的便利真的是无话可说,绝对的傻瓜级,但是就是这短短的几段代码,最后一句话执行的时候总是出现错误,那个黄色的小框让我看得快崩溃了。

.Net(C#)访问X509证书加密的Webservice(三)--客户端访问WSE  

       根据上图的提示,错误应该在客户端这边,Java服务端也看过,已经接收到请求并且认证签名通过,并且反签发送回客户端,同时从Response的InnerXml中拷贝出来可以看到返回的结果Soap包都是正确的,客户端的错误就是Referenced security token could not be retrieved,这个就表明了客户端的alisoft的公钥没有找到,导致检查返回结果的时候出现了问题,那么就去找是否是因为格式不同导致公钥没有导入或者导入错误(错误的查错方向)。

       最后发现原因出在了获取key的标示上,前面说到如果不是第三方权威的CA认证模式下,需要双方互相导入对方的公钥到本地的证书管理库中,我们是将Jks中的公钥导出,然后导入到了本机的证书管理模块中。公钥证书的引用在WS-Security中分成两种:SKIKeyIdentifier 和IssuerSerial,由于我们前面提到的扩展ISV证书动态载入的需要,我们一直使用的是IssuerSerical。但是微软的工具生成的是x.509v3版本,而java keytools生成的是x.509v1版本,如果使用IssuerSerical会有问题。那么只能使用SKIKeyIdentifier,但是使用SKIKeyIdentifier,.net默认使用的是自身的window的序列化方式,不过可以设置其为java的RFC3280协议方式,但是.net wse 2和wse 3的RFC3280方式都不一样,wse 2的可以和java互通,wse 3就不可以,也就是说2和3本身在RFC3280的序列化方式上都有差别,自己也不能互通,因此没有办法,只能够安装2版本,重新来生成。下面将描述一下如何用wse 2来订制web service 的WSSE客户端。

.Net(C#)访问X509证书加密的Webservice(三)--客户端访问WSE,布布扣,bubuko.com

.Net(C#)访问X509证书加密的Webservice(三)--客户端访问WSE

上一篇:php使用


下一篇:
关于css3的边框的border-radius和border-image用法的详解