翻译:张勇,周竞
摘要:
如何使用支持WS安全规范的WSE(Microsoft Web Services Enhancements)使加密SOAP能够跨越标准HTTP呢?讲述了SOAP报文加密是如何进行,在WS安全和XML加密规范中又是如何定义的。
目录:
? 介绍WSE
? WSE的安全特性
? 加密SOAP报文
? WSE对加密的支持
? 配置WSE
? SOAP报文的对称加密算法
? 使用X.509证书来加密SOAP报文
? 选择报文的节点(组成部分)来加密
? 局限性和协作性细节
? 结论
WSE介绍
为了使Web 服务在企业内部运行得更好,新一代的Web 服务规范被提出来.建议应该改善对Web 服务很重要的方面如 安全,可靠报文,发送附件对地区间的协调性.为了支持这些提议的标准,ms发布了WSE1.0 sp1,它包含了一系列的类来支持这些新的协议,如基于Microsoft的asp.net宿主的过滤器,拦截进入和发出的SOAP报文,拦截或者产生SOAP头来支持需求的功能.WSE支持以下的规范:
? WS-安全和Web 服务安全补遗
? WS附件
? WS路由
? WS引用
WSE的安全特性
在WSE运行时,由一系列过滤器产生和读取WS-security兼容的SOAP报头.当在一台支持WSE的Web服务器上接收到SOAP报文时,SOAP报文经过一系列输入过滤器读取WS-*兼容的报头,如有必要修改它们,产生一系列相关程序对象.同样,输出的报文是经过一些列输出过滤器,序列化一定的报头如WSE对象定义的. 所有被WSE1.0 sp1所支持的Web服务安全特征由安全输入和输出过滤器通过SecurityInputFilter和SecurityOutputFilte对象来实现.它包含:数字签名,加密,签署和加密用户标识,签署和加密用x.509证书, 签署和加密用自定义2进制标识.
加密SOAP报文
用统一的格式来传输数据,使得有价值的数据容易被恶意用户访问,以至被拦截.使用SOAP和xml来传输数据不但数据有潜在的安全威胁,而且你Web 服务的内在工作方式有可能被发现,通过观察SOAP 报文本身带的 xml 语法.使用合适的加密算法,数据和信息接口可以得到完全的保护.加密是简单的使用一种可逆的算法使用特定的密钥对明文进行加密,使得数据如果不解密则无法阅读. 如今,互联网加密的最常见的形式引入了一种传输级的加密模式,例如IPSEC和SSL,在传输层加密 .有了一定的安全性,但是传输层加密影响性能,尤其是当只有SOAP报文的一部分需要加密时.而且传输层加密不允许报文安全的路由通过Web 服务作为中介.因为报文需要解密由媒介在她能以新的加密的流传送到最终接收者之前,
Xml加密是如何工作的
Xml加密协议指定了SOAP报文的部分或者全部可以被加密.当使用xml加密时,将针对xml文档的部分进行加密,加密后的内容在EncryptedData节点内部.WS安全是基于 xml加密的,充分保证了使用xml加密来加密SOAP 报文时, EncryptedData 是security头部节点元素的引用.如果在SOAP报文的主体中有多个节点被加密时,每个节点参考自每个独立的且在ReferenceList 中的ReferenceData节点
对于一个EncryptedData 节点,一些密钥的信息可以在KeyInfo节点指定,加密的算法则在EncryptionMethod节点中指定,keyinfo节点按照xml签名规范来定义的.
一个加密后的SOAP 报文
下面的SOAP报文示例有一个payment节点,含有一些敏感的客户信息.
> 引用
>
> * * *
>
> **
>
1<soap:envelope soap:xmlsn="http://www.w3.org/2002/12/SOAP-envelope">
2> <soap:header>
3> ...
4> </soap:header>
5> <soap:body>
6> ...
7> <x:order type="Purchase" x:xmlns="http://andsky.com/order">
8> <x:payment type="CreditCard">
9> <x:creditcard type="Visa">
10> <x:cardnumber>123456789123456
11> <x:experationdate>1108
12> </x:experationdate></x:cardnumber></x:creditcard>
13> </x:payment>
14> ...
15> </x:order>
16> ...
17> </soap:body>
18> </soap:envelope>
> ** > > * * *
因为payment节点含有敏感的数据,所以它应该被加密.下面的例子显示了相同的信息,但是payment节点被EncryptedData节点代替了.EncryptedData节点内含有对payment 节点内部内容加密后的密文. EncryptedData节点是参考自security头中的DataReference 节点的
> 引用
>
> * * *
>
> **
>
1<soap:envelope soap:xmlsn="http://www.w3.org/2002/12/SOAP-envelope"> xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"
2> xmlns:xsig="http://www.w3.org/2000/09/xmldsig#"
3> xmlns:WSse="http://schemas.xmlSOAP.org/WS/2002/04/secext">
4> <soap:header>
5> <wsse:security>
6> <xenc:referencelist>
7> <xenc:datareference uri="#OrderID"></xenc:datareference>
8> </xenc:referencelist>
9> </wsse:security> ...
10> </soap:header>
11> <soap:body>
12> ...
13> <x:order type="Purchase" x:xmlns="http://andsky.com/order">
14> <xenc:encrypteddata id="OrderId">
15> <xenc:encryptionmethod> Algorithm= "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"
16> <xsig:keyinfo>
17> <xsig:keyname>My Symmetric Key</xsig:keyname>
18> </xsig:keyinfo>
19> <xenc:cipherdata>
20> <xenc:ciphervalue>...
21> </xenc:ciphervalue></xenc:cipherdata>
22> </xenc:encryptionmethod></xenc:encrypteddata>
23> ...
24> </x:order>
25> ...
26> </soap:body>
27> </soap:envelope>
> ** > > * * *
当然,在这个例子中,你可以用数字签名来签署该报文,来防止怀有恶意的人来窜改数据,或者用时间戳或者其他唯一的标识来判断信息是否受到攻击.
加密的种类
对称加密和不对成加密
加密得算法可以分为对称加密和不对称加密.在对称加密算法中,一个密钥被用来进行信息交换得两方共享.发送方利用私钥得拷贝来加密数据.在接收方,利用同样得私钥的拷贝来解密数据.绝大多数得加密,如基于共享密码和共享安全标识都是对成加密得例子.
在这种类型的系统中,一个中心的服务器分发共享的密钥给需要安全交互的使用者.对称加密的缺点是共享密钥的管理,分发和保护它们的安全性,特别是在象internet这样的公网上.
为了克服在公共网络中管理密钥的的难度,使用成对的密钥来取代单一的密钥.在不对成加密算法中,双方都互相拥有一个私钥和一个密钥.
公钥是利用一种不可逆的方法对私钥进行操作后产生的,因此一旦两者中的一种用来加密数据,另外一种就可以用来解密.另外,不可由公钥来推测出私钥,而且只有用私钥来解密用公钥加密的数据.当发送异步加密的报文时,发送者利用接收者的公钥加密报文,确保只有接收者可以利用他的私钥来解密报文.如果你用另外一种方式来处理,任何人都可利用可利用的公钥来解密报文.不对称加密是PKI的基础,pki是x.509安全标准的基础.不对成加密算法是一种典型基于对大数处理的算法,如指数合对数运算.它比对成加密算法需要更多的cpu时间来进行加密和解密.,因为这个原因,不对成加密经常用来安全的传送一个对称的”会话”密钥,用来加密交互的剩余部分,这也只是在信息交换的持续周期内有效.
因为公钥可以很容易的获得,使用公钥进行加密减轻了分发和管理密钥的难度.不幸的是,这种方便性的代价是不对成加密算法通常比对成加密算法慢几个数量级.由于此,不对称加密方法只用来处理比较小的数据.例如安全密钥和标识以及数字签名.
WSE对加密的支持
WSE支持对SOAP 报文的部分加密.对称加密使用一个共享的密钥,不对称加密支持使用x.509证书.当使用WSE来加密SOAP报文时,整个body节点的内容被加密,除非明确指定不要加密.下面举了2个例子,一个加密这个主体部分,一个加密部分.
WSE运行时库实现了所有的WS-security.在SecurityInputFilter 和SecurityOutputFilter 类中SecurityOutputFilter 类中.前者通过查找Security节点在一个
进入的SOAP报文中,如果该节点存在.它创建了一个代表任意安全标记和加密密钥,解密节点,验证任何数字签名的对象.对于一个进入的报文,任何任何安全的节点都剋通过报文产生的SOAPcontext对象的安全属性进行访问.相反的, SecurityOutputFilter为进出报文实施加密和签名的操作,附带任何特定的安全标记或者加密密钥.安全措施,比如添加标记,加密,或者签署进出报文利用报文的SOAPContext.Security和SOAPContext.ExtendedSecurity属性, ExtendedSecurity只在需要创建安全报头时候使用Security属性只在为包含最终目的地时使用
配置WSE
尽管在安装时,WSE已经被安装到asp.net Web 服务器上,但是还需要一些额外的配置,如果需要那些asp.net应用使用安全支持的话.当创建完毕asp.net Web 服务后工程后,visual studio.net,引用Microsoft.Web.Services.dll 程序集需要加载到该项目中.你也需要对SOAPExtensionTypes节点添加一个新的SOAP扩展.这可在 Web.config文件中创建一个新的 add 节点.如下所示
> 引用
>
> * * *
>
> **
>
1<configuration>
2> <system.web>
3> ...
4> <webservices>
5> <soapextensiontypes>
6> <add type=""> "Microsoft.Web.Services.WebServicesExtension,
7> Microsoft.Web.Services,
8> Version=1.0.0.0,
9> Culture=neutral,
10> PublicKeyToken=31bf3856ad364e35"
11> priority="1" group="0" />
12> </add></soapextensiontypes>
13> </webservices>
14> </system.web>
15> </configuration>
> ** > > * * *
type属性的值必须不能包含任何间断或者额外的空格.这个例子为了可读性有额外的换行. 如果WebServices 和SOAPExtensionTypes 节点不存在,它们必须加到 Web.config文件里面.一种更容易的方法是完全WSE配置工具.一种visual studio的插件,使用它您可以非常容易的配置使用WSE的Web service 项目.当然还有一下其他相关的配置必须手工配置.
当使用WSE进行编程时,你需要添加一个Microsoft.Web.Services 和一个System.security名字空间的引用.在客户端和服务器端的工程中,如果既在客户请求和服务器回应中加密了.在客户部分你应该使用添加Web引用工具为基于WSE的Web service工程产生Web service代理.
给SOAP报文进行对称加密
接下来,让我们再来看看如何用WSE来给SOAP报文使用对称密钥加密。下面的例子是基于一个启用WSE的Web服务的,这个Web服务将返回一个SOAP回应报文,在该报文正文内包含了一些敏感数据。这么说,客户端给服务发送一个简单的Web服务请求,该请求将返回一个由三元 DES对称加密算法(使用了一个共享密钥和一个初始向量,IV)加密过的XML文档,当客户端收到了加密后的回应信息后,SecurityInputFilter将调用一个在客户端的解密密钥提供程序,来访问客户端上相同的共享密钥,以此来对报文体进行解密,这个解密密钥提供程序必须由你来编写,并且提供一个用于同步共享密钥的方法。这些例子假设双方都知道密钥,并且我们所要做的,仅仅是提供密钥的名字,以此作为一个暗示,给对方知道我们用的是哪个密钥加密的信息。
双方之间管理、同步和和对密钥保密的时候一定要多加小心。有个解决方案使用了一个分布式密钥机制,例如Kerberos。但从WSE的1.0版本开始,WSE就不再继续支持Kerberos了。
加密对外发送的报文
这里我简单描述下如何创建一个可以返回个被加密的XML文档的Web服务。第一步先用using指示符来添加必要的命名空间,如下:
> 引用
>
> * * *
>
> **using System.Web.Services;
> using Microsoft.Web.Services;
> using Microsoft.Web.Services.Security;
> using System.Security.Cryptography;
> using System.Security.Cryptography.Xml;
> using System.Xml;
> **
>
> * * *
GetXmlDocument方法使用了的.NET框架实现的三元DES算法,采用128位密钥和64位初始化向量(IV),能够生成对称密钥。这个密钥还将拥有一个名字,并被添加到应答报文的SoapContext元素上,之后被SecurityOutputFilter使用于加密简单的XML文档,这个方法最后将返回给客户端。更多关于.NET框架的加密技术,请看.NET框架开发者指南上的Cryptography Overview一文。
> 引用
>
> * * *
>
> **
> //返回由三元DES对称算法加密后的数据
> [WebMethod (Description="返回一个由对称加密算法机密后的敏感XML文档", EnableSession=false)]
>
> public XmlDocument GetXmlDocument()
> {
> //创建一个用于返回的简单的XML文档
> XmlDocument myDoc = new XmlDocument();
> myDoc.InnerXml =
> "
1<encryptedresponse>这里是敏感数据.</encryptedresponse>
";
>
> //得到对外发送的回应报文的SoapContext
> SoapContext myContext = HttpSoapContext.ResponseContext;
>
> //创建一个用于加密的对称密钥,由于密钥是对称的,这些相同的数据必须存在有需求的客户端上。
>
> //定义共享的16字节数组,用来表示128位密钥
> byte[] keyBytes = {48, 218, 89, 25, 222, 209, 227, 51, 50, 168, 146,
> 188, 250, 166, 5, 206};
>
> //定义共享的8字节(64位)数组,也就是初始化向量(IV)
> byte[] ivBytes = {16, 143, 111, 77, 233, 137, 12, 72};
>
> //创建三元DES算法的新实例
> SymmetricAlgorithm mySymAlg = new TripleDESCryptoServiceProvider();
>
> //设置好密钥和IV
> mySymAlg.Key = keyBytes;
> mySymAlg.IV = ivBytes;
>
>
> //创建一个新的WSE对称加密密钥
> EncryptionKey myKey = new SymmetricEncryptionKey(mySymAlg);
>
>
> //给他取个名字?
> KeyInfoName myKeyName = new KeyInfoName();
> myKeyName.Value = "http://andsky.com/symmetrictestkey";
> myKey.KeyInfo.AddClause(myKeyName);
>
>
> //使用对称密钥来创建一个新的EncryptedData元素
> EncryptedData myEncData = new EncryptedData(myKey);
>
>
> //将EncryptedData元素添加到SOAP回应上,告诉过滤器用指定的密钥来加密信息正文
>
> myContext.Security.Elements.Add(myEncData);
>
> return myDoc;
> }
> **
>
> * * *
基于前面的方法,WSE管道产生了下面有相应的安全头信息,密文和密钥信息的回应报文:
> 引用
>
> * * *
>
> **
>
>
1<soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2> xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3> <soap:header>
4> <wsu:timestamp> xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">
5> <wsu:created>2003-02-11T02:07:23Z</wsu:created>
6> <wsu:expires>2003-02-11T02:12:23Z</wsu:expires>
7> </wsu:timestamp>
8> <wsse:security soap:mustunderstand="1"> xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/07/secext">
9> <xenc:referencelist> xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
10> <xenc:datareference uri=""> "#EncryptedContent-f50076e3-5aea-435e-8493-5d7860191411" />
11> </xenc:datareference></xenc:referencelist>
12> </wsse:security>
13> </soap:header>
14> <soap:body xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility"> wsu:Id="Id-d2f22e02-a052-4dcb-8fbc-8591a45b8a9f">
15> <xenc:encrypteddata> Id="EncryptedContent-f50076e3-5aea-435e-8493-5d7860191411"
16> Type="http://www.w3.org/2001/04/xmlenc#Content"
17> xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
18> <xenc:encryptionmethod> Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
19> <keyinfo xmlns="http://www.w3.org/2000/09/xmldsig#">
20> <keyname>http://andsky.com/symmetrictestkey</keyname>
21> </keyinfo>
22> <xenc:cipherdata>
23> <xenc:ciphervalue>0T5ThoGg14JmElph...qDJS=</xenc:ciphervalue>
24> </xenc:cipherdata>
25> </xenc:encryptionmethod></xenc:encrypteddata>
26> </soap:body>
27> </soap:envelope>
> ** > > * * *
注意,在报文正文中ReferenceList元素包含了一个到EncryptedData元素的引用,这个元素包含了密钥的名字,使用的加密算法和一个数据的密文形式。
解密收到的报文
不管是在客户端还是在服务器端,WSE总是在SecurityInputFilter实现报文解密的,由于对称加密需要由公共密钥派生出来的加密密钥,你需要创建一个SecurityInputFilter能够调用的方法来得到这个对称密钥,然后你就能使用包含在EncryptedData中的密钥和算法信息来帮你找到正确的共享密钥和加密算法了。这个方法必须实现在从Microsoft.Web.Services.Security.IDecryptionKeyProvider派生出来的类中。在我的例子中,DecryptionKeyProvider.GetDecryptionKey方法返回了对称密钥,如下:
> 引用
>
> * * *
>
> **
>
> public DecryptionKey GetDecryptionKey(string encAlgorithmUri,
> KeyInfo keyInfo)
> {
>
> //重新创造同样的用于表示128位密钥的16个字节
> byte[] keyBytes = {48, 218, 89, 25, 222, 209, 227, 51, 50, 168, 146,
> 188, 250, 166, 5, 206};
>
>
> //重新创造表示初始化向量的8个字节(64位)
> byte[] ivBytes = {16, 143, 111, 77, 233, 137, 12, 72};
>
> SymmetricAlgorithm mySymAlg = new TripleDESCryptoServiceProvider();
> mySymAlg.Key = keyBytes;
> mySymAlg.IV = ivBytes;
>
> //重新创建对称加密密钥
> DecryptionKey myKey = new SymmetricDecryptionKey(mySymAlg);
>
> return myKey;
> }
> **
>
> * * *
即便在我的方法中并没使用他们,WSE还是要把KeyInfo元素和加密算法的URI传递给这个方法的,决定使用哪一个共享密钥或者加密算法来产生对称密钥
为了让SecurityInputFilter能够访问到GetDecryptionKey方法,下面的配置信息必须加入到应用程序的配置文件中(也就是 app.config 文件)
> 引用
>
> * * *
>
> **
>
>
1<configuration>
2> ...
3> <microsoft.web.services>
4> <security>
5> <decryptionkeyprovider> type="MyClient Assembly.DecryptionKeyProvider,
6> MyClientAssembly" />
7> </decryptionkeyprovider></security>
8> **
9>
10> * * *
11
12
13
14type 属性不能有任何过多的空格或者任何换行。他们只包含上面的内容以增强可读性,这个也可一用WSE设置工具来修改。一旦DecryptionKeyProvider类被添加到客户端而且WSE安全支持已经配置好了,WSE将自动拦截加密数据,一个基于标准的Web服务的2次开发平台程序,就可以让你随心所欲的给客户端编程了。
15
16**使用 X.509 证书来给SOAP报文加密**
17正如我前面提到的,非对称操作有一定开销。当传输大量数据时,从性能上来说,用非对称算法来加密这些数据会显得不太实际,WSE就这个问题,实现了一种伪非对称性加密(pseudo-asymmetric encryption)。和非对称性加密的报文相比,WSE使用一个非对称性算法和X.509证书的一个公开备份,以此来加密对称密钥,而实际上这些被用来给报文加密。当收到报文后,SecurityInputFilter得到和X.509证书相关联的私有密钥,以此给对称密钥解密,然后用解密后的密钥给报文正文解密。为了能让这个例子能够正常工作,一个来自受信任的证书认证的X.509证书(支持加密),必须出现在客户机器上当前用户帐号的个人证书储藏室里面,这个证书的私有密钥也必须出现在本地机器在主管Web服务的服务器的帐号里。另外,CA证书链中的一个证书必须出现在客户端的受信任储存室里,那样WSE才知道可以信任接受到的X.509证书。
18**加密对外发送的报文**
19我已经修改了前面的GetXmlDocument方法,让它可以使用由WSE实现的基于X.509非对称加密技术。加密回应报文,FindCertificateBySubjectString方法可以用来接收客户端证书的公开备份,一个来自本地机器账号的个人储存室给的客户端证书。这个证书然后被用来创建一个新的 X.509安全Token,这个Token将被加入到响应报文的SoapContext的安全Token集合里。另外,在对称加密例子中引用的命名空间,你应该再加一个using指示附来引用一个Microsoft.WebServices.Security.X509命名空间。GetXmlDocument方法代码如下:
20
21
22> 引用
23>
24> * * *
25>
26> **//创建一个用于返回的简单XML文档
27> XmlDocument myDoc = new XmlDocument();
28> myDoc.InnerXml =
29> "<encryptedresponse>This is sensitive data.</encryptedresponse>";
30> "<encryptedresponse>这里是敏感数据.</encryptedresponse>";
31>
32> //得到响应报文的SoapContext
33> SoapContext myContext = HttpSoapContext.ResponseContext;
34>
35> //打开并读取本地机器帐号的个人证书储存室
36> X509CertificateStore myStore =
37> X509CertificateStore.LocalMachineStore(
38> X509CertificateStore.MyStore);
39> myStore.OpenRead();
40>
41> //查找所有名为”我的证书”的证书,然后将所有匹配的证书添加到证书集合中
42> X509CertificateCollection myCerts =
43> myStore.FindCertificateBySubjectString("My Certificate");
44> X509Certificate myCert = null;
45>
46> //查找在集合中中的第一个证书
47> if (myCerts.Count > 0)
48> {
49> myCert = myCerts[0];
50> }
51>
52> //确定我们有一个可以用于加密的证书
53> if (myCert == null || !myCert.SupportsDataEncryption)
54> {
55> throw new ApplicationException("Service is not able to
56> encrypt the response");
57>
58> return null;
59> }
60> else
61> {
62> //使用有效的证书来创建一个安全Token
63> X509SecurityToken myToken = new X509SecurityToken(myCert);
64> //WSE将使用这个标记来加密报文正文的
65> //WSE产生一个KeyInfo元素,用来请求客户端上曾用于给报文解密的证书
66>
67> EncryptedData myEncData = new EncryptedData(myToken);
68> //将已加密数据元素添加到响应报文的SoapContext上
69> myContext.Security.Elements.Add(myEncData);
70>
71> return myDoc;
72> }
73> **
74>
75> * * *
76
77
78
79基于前面的方法,WSE管道产生了下面的有相应Security头、密文和密钥信息的元素:
80
81
82> 引用
83>
84> * * *
85>
86> **
87> <?xml version="1.0" encoding="utf-8"?>
88> <soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
89> xmlns:xsd="http://www.w3.org/2001/XMLSchema">
90> <soap:header>
91> <wsu:timestamp> xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">
92> <wsu:created>2003-02-11T01:34:01Z</wsu:created>
93> <wsu:expires>2003-02-11T01:39:01Z</wsu:expires>
94> </wsu:timestamp>
95> <wsse:security soap:mustunderstand="1"> xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/07/secext">
96> <xenc:encryptedkey> Type="http://www.w3.org/2001/04/xmlenc#EncryptedKey"
97> xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
98> <xenc:encryptionmethod> Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
99> <keyinfo xmlns="http://www.w3.org/2000/09/xmldsig#">
100> <wsse:securitytokenreference>
101> <wsse:keyidentifier valuetype="wsse:X509v3">
102> YmlKVwXYD8vuGuYliuIYdEAQQPw=
103> </wsse:keyidentifier>
104> </wsse:securitytokenreference>
105> </keyinfo>
106> <xenc:cipherdata>
107> <xenc:ciphervalue>UJ64Addf3Fd59XsaQ=…</xenc:ciphervalue>
108> </xenc:cipherdata>
109> <xenc:referencelist>
110> <xenc:datareference uri=""> "#EncryptedContent-608eef8b-4104-4469-95b6-7cb4703cfa03" />
111> </xenc:datareference></xenc:referencelist>
112> </xenc:encryptionmethod></xenc:encryptedkey>
113> </wsse:security>
114> </soap:header>
115> <soap:body xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility"> wsu:Id="Id-70179c5b-4975-4932-9ecd-a58feb34b0d3">
116> <xenc:encrypteddata> Id="EncryptedContent-608eef8b-4104-4469-95b6-7cb4703cfa03"
117> Type="http://www.w3.org/2001/04/xmlenc#Content"
118> xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
119> <xenc:encryptionmethod> Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
120> <xenc:cipherdata>
121> <xenc:ciphervalue>
122> 4o1b4befwBJu6tzuaygfrAaX0UGtaYKcw2klIbuZPjLi...z8i2ypHN4+w==
123> </xenc:ciphervalue>
124> </xenc:cipherdata>
125> </xenc:encryptionmethod></xenc:encrypteddata>
126> </soap:body>
127> </soap:envelope>
128> **
129>
130> * * *
131
132
133
134注意在这个已加密的报文里面,由非对称加密过的EncryptedKey元素包含了用于给报文正文加密的对称加密密钥。ReferenceList元素引用了报文正文的EncryptedData元素的Id属性。虽然我在我的例子中没这样做,标记这个消息以便能让容器验证发送者其实是个不错的想法。关于使用WSE来标记报文的详细信息,看WS-Security Authentication and Digital Signatures with Web Services Enhancements
135**给收到的报文解密**
136当收到一个由X.509证书加密后的报文后,SoapInputFilter会自动尝试使用用户密钥储存室的私有密钥来进行解密,当然,这个需要告诉WSE运行时哪里可以找到这个证书的额外配置信息。这个信息由应用程序配置文件的Security元素所指定,这个例子在客户端上的应用程序配置文件是App.config。对于 X.509加密,你只需要添加一个x509子节点,内容和下面一样就可以了
137
138
139> 引用
140>
141> * * *
142>
143> **
144> <x509> storeLocation="CurrentUser"
145> verifyTrust="true"
146> allowTestRoot="false" />
147> **
148>
149> * * *
150
151
152
153
154在我的例子中,我将x509节点的storeLocation属性设为CurrentUser,假设证书在当前用户的证书储存室里,当我使用了来自CA的受信任证书之后,我也将verifyTrust设为true了。这些属性还能够用WSE的设置工具来修改。利用这些信息,WSE能够得到报文中证书的私有密钥,还能用这个来给对称性会话密钥解密,解密后的内容到头来还要给报文正文解密。
155**选择用于解密的报文元素**
156当整个消息正文由默认设置给加密后,WSE能被用来给SOAP报文内的特定元素加密;唯一的问题是,在Security头元素那的元素不能被加密。你还可以加密嵌套的元素,
157在这个例子服务中,我修改了GetXmlDocument方法用的X.509版本,用一个基于X.509的安全Token来同时给EncryptedSubResponse和它的EncryptedResponse父节点进行数字化加密,返回的XML文档如下:
158
159
160> 引用
161>
162> * * *
163>
164> ** <response>
165> <notencrypted>
166> 回应报文的这里没有必要被加密
167> </notencrypted>
168> <encryptedresponse>
169> <encryptedsubresponse>
170> 这里是敏感数据.
171> </encryptedsubresponse>
172> </encryptedresponse>
173> </response>
174> **
175>
176> * * *
177
178
179
180为了加密一个元素,它需要一个wsu:Id属性,以便当XML被序列化后引用可以加到该节点上了。命名空间wsu被定义为:
181
182
183> 引用
184>
185> * * *
186>
187> **xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility**
188>
189> * * *
190
191
192
193为了完成这个,我将这个XML加到一个新的XML文档,然后通过.NET框架支持的Microsoft XML文档对象模型(DOM)给它添加一个Id属性,此外还需要将配件System.Xml加入到工程引用里面,加上下面的话:
194
195
196> 引用
197>
198> * * *
199>
200> **
201> using System.Xml;
202> using System.Xml.Serialization;
203> **
204>
205> * * *
206
207
208
209当我将多个Id属性加到嵌套的元素上后,我由EncryptedSubResponse元素开始依次遍历到它的父节点EncryptedResponse,如下:
210
211
212> 引用
213>
214> * * *
215>
216> **
217> string [] myId = {"Id:" + Guid.NewGuid(),"Id:" + Guid.NewGuid()};
218>
219>
220> //创建一个用于返回XML的XML文档
221> XmlDocument myDoc = new XmlDocument();
222> myDoc.LoadXml("<response>" +
223> "<notencrypted>回应报文的这里没有必要加密" +
224> "</notencrypted>" +
225> "<encryptedresponse>" +
226> "<encryptedsubresponse>" +
227> "这里是敏感数据. " +
228> "</encryptedsubresponse>" +
229> "</encryptedresponse>" +
230> "</response>");
231>
232> //得到EncryptedSubResponse节点
233> XmlNode = myDoc.FirstChild.LastChild.FirstChild;
234>
235>
236> //向上遍历元素,添加两个Id属性
237> //向上保证内部的多数元素可以优先被加密
238> //否则我们会得到一个异常
239> for (int i=0;i<myid.length;i++)> {
240>
241> //创建新的Id属性
242> string wsu = "http://schemas.xmlsoap.org/ws/2002/07/utility";
243> XmlNode myAttr = myDoc.CreateNode(XmlNodeType.Attribute, "wsu",
244> "Id", wsu);
245> myAttr.Value = myId[ i ];
246>
247> //将属性添加到文档
248> root.Attributes.SetNamedItem(myAttr);
249> root = root.ParentNode; // 移动到父节点
250> }
251> **
252>
253> * * *
254
255
256
257假设我早就用我前面的逻辑得到了来自X.509证书的安全记号,我将这些引用添加到EncryptedData元素,如下:
258
259
260> 引用
261>
262> * * *
263>
264> **
265> //循环遍历Id值,将其添加到新的EncryptedData元素上
266> for (int i=0;i<myid.length;i++)> {
267> //创建一个新的头,”#”是的前缀,用来保证相关的URI能够引用到头
268> EncryptedData myEncHeader = new EncryptedData(myToken, "#"+myId[ i ]);
269> //添加一个新的头到集合中
270> myContext.Security.Elements.Add(myEncHeader);
271> }
272> //返回加密数据
273> return myDoc;
274> **
275>
276> * * *
277
278
279
280下面是被WSE在运行时序列化后产生的报文加密后的结果
281
282
283> 引用
284>
285> * * *
286>
287> **
288> <?xml version="1.0" encoding="utf-8"?>
289> <soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
290> xmlns:xsd="http://www.w3.org/2001/XMLSchema">
291> <soap:header>
292> <wsu:timestamp> xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">
293> <wsu:created>2003-02-11T20:21:52Z</wsu:created>
294> <wsu:expires>2003-02-11T20:26:52Z</wsu:expires>
295> </wsu:timestamp>
296> <wsse:security soap:mustunderstand="1"> xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/07/secext">
297> <xenc:encryptedkey> Type="http://www.w3.org/2001/04/xmlenc#EncryptedKey"
298> xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
299> <xenc:encryptionmethod> Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
300> <keyinfo xmlns="http://www.w3.org/2000/09/xmldsig#">
301> <wsse:securitytokenreference>
302> <wsse:keyidentifier valuetype="wsse:X509v3">
303> YmlKVwXYD8vuGuYliuIOXOY7ZYN9PwHbfAhCiYOV0aYdEAQQPw=
304> </wsse:keyidentifier>
305> </wsse:securitytokenreference>
306> </keyinfo>
307> <xenc:cipherdata>
308> <xenc:ciphervalue>
309> UyKGBEXdY8lYSzqgdgxOXOY7ZYN9PwHbfAhCiYOV0...bwRnWk=
310> </xenc:ciphervalue>
311> </xenc:cipherdata>
312> <xenc:referencelist>
313> <xenc:datareference uri=""> "#EncryptedContent-cf014249-0e2a-4f8b-9002-13a7de916be0" />
314> </xenc:datareference></xenc:referencelist>
315> </xenc:encryptionmethod></xenc:encryptedkey>
316> <xenc:encryptedkey> Type="http://www.w3.org/2001/04/xmlenc#EncryptedKey"
317> xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
318> <xenc:encryptionmethod> Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
319> <keyinfo xmlns="http://www.w3.org/2000/09/xmldsig#">
320> <wsse:securitytokenreference>
321> <wsse:keyidentifier valuetype="wsse:X509v3">
322> YmlKVwXYD8vuGuYliuIYdEAQQPw=
323> </wsse:keyidentifier>
324> </wsse:securitytokenreference>
325> </keyinfo>
326> <xenc:cipherdata>
327> <xenc:ciphervalue>
328> In8Kf1cIdiJJJXCLZ+... wMqBEevXmzk=
329> </xenc:ciphervalue>
330> </xenc:cipherdata>
331> <xenc:referencelist>
332> <xenc:datareference uri=""> "#EncryptedContent-0744279a-02bf-4ad1-998e-622208eded0e" />
333> </xenc:datareference></xenc:referencelist>
334> </xenc:encryptionmethod></xenc:encryptedkey>
335> </wsse:security>
336> </soap:header>
337> <soap:body>
338> <getxmldocumentresponse xmlns="http://andsky.com/dime/">
339> <getxmldocumentresult>
340> <response>
341> <notencrypted>
342> This part of the response does not need encryption
343> </notencrypted>
344> <encryptedresponse> wsu:Id="Id:e5e8d792-abe7-4476-91d0-856fbdf4a958"
345> xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">
346> <xenc:encrypteddata> Id=
347> "EncryptedContent-cf014249-0e2a-4f8b-9002-13a7de916be0"
348> Type="http://www.w3.org/2001/04/xmlenc#Content"
349> xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
350> <xenc:encryptionmethod> Algorithm=
351> "http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
352> <xenc:cipherdata>
353> <xenc:ciphervalue>
354> 2MNHCkGVH/5jb0pF4pCh3u2VaUKsWSA...AfEvJZT=
355> </xenc:ciphervalue>
356> </xenc:cipherdata>
357> </xenc:encryptionmethod></xenc:encrypteddata>
358> </encryptedresponse>
359> </response>
360> </getxmldocumentresult>
361> </getxmldocumentresponse>
362> </soap:body>
363> </soap:envelope>
364> **
365>
366> * * *
367
368
369
370注意,在这个加密后的报文里,这里有一个用于表示X.509证书的BinarySecurityToken元素,但有两个分开的EncryptedKey元素,每个EncryptedData元素都被添加到SoapContext,在最外面的被加密的元素中(EncryptedResponse),你只能看到EncryptedData元素,当EncryptedResponse被加密后,表示EncryptedSub元素的EncryptedData元素也随之转换为密文了。当这个报文在客户端被收到时,SecurityInputFilter使用来自记号的信息来得到曾给两个EncryptedKey元素解密过的私有密钥。</myid.length;i++)></myid.length;i++)></x509></microsoft.web.services></configuration>