对消息进行签名
签名要比 “Axis2 WS-Security 基础” 中的 UsernameToken 示例用到更多规范。您需要:
- 识别用于在每个方向上创建签名的私有/公开密匙对,提供用于访问 keystore 和私有密匙的密码。
- 指定用于 XML 标准化、摘要生成和实际签名的算法集。
- 指定将消息的哪些部分包含到签名中。
这些信息中的一部分被作为配置数据处理,被嵌入到服务的 WS-SecurityPolicy 文档中。其他部分被包含到运行时消息交换中。
清单 1 展示了一个 WS-Policy 文档,用于将 Axis2 客户机配置为对消息使用签名。(清单 1 进行了编辑,以符合页面的宽度。在示例代码中可以查看 sign-policy-client.xml 的完整文本)。
清单 1. 用于签名(客户机)的 WS-Policy/WS-SecurityPolicy
<!-- Client policy for signing all messages, with certificates included in each message --> <wsp:Policy wsu:Id="SignOnly" xmlns:wsu="http://.../oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"> <wsp:ExactlyOne> <wsp:All> <sp:AsymmetricBinding xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"> <wsp:Policy> <sp:InitiatorToken> <wsp:Policy> <sp:X509Token sp:IncludeToken="http://.../IncludeToken/AlwaysToRecipient"/> </wsp:Policy> </sp:InitiatorToken> <sp:RecipientToken> <wsp:Policy> <sp:X509Token sp:IncludeToken="http://.../IncludeToken/AlwaysToInitiator"/> </wsp:Policy> </sp:RecipientToken> <sp:AlgorithmSuite> <wsp:Policy> <sp:TripleDesRsa15/> </wsp:Policy> </sp:AlgorithmSuite> <sp:Layout> <wsp:Policy> <sp:Strict/> </wsp:Policy> </sp:Layout> <sp:IncludeTimestamp/> <sp:OnlySignEntireHeadersAndBody/> </wsp:Policy> </sp:AsymmetricBinding> <sp:SignedParts xmlns:sp="http://.../ws-securitypolicy/200702"> <sp:Body/> </sp:SignedParts> <ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy"> <ramp:user>clientkey</ramp:user> <ramp:passwordCallbackClass >com.sosnoski.ws.library.adb.PWCBHandler</ramp:passwordCallbackClass> <ramp:signatureCrypto> <ramp:crypto provider="org.apache.ws.security.components.crypto.Merlin"> <ramp:property name="org.apache.ws.security.crypto.merlin.keystore.type" >JKS</ramp:property> <ramp:property name="org.apache.ws.security.crypto.merlin.file" >client.keystore</ramp:property> <ramp:property name="org.apache.ws.security.crypto.merlin.keystore.password" >nosecret</ramp:property> </ramp:crypto> </ramp:signatureCrypto> </ramp:RampartConfig> </wsp:All> </wsp:ExactlyOne> </wsp:Policy> |
在 清单 1 中,策略中的 <sp:AsymmetricBinding> 元素给出了在消息交换中使用公开密匙加密的基本配置信息。在这个元素内,使用了一些嵌套元素来提供配置的细节。<sp:InitiatorToken> 识别出用于对由客户机(发起者)发往服务器(接收者)的消息进行签名的密匙对,在本例中指定这个公开密匙将采用 X.509 证书的形式并连同来自客户机的消息一起发送(sp:IncludeToken=".../AlwaysToRecipient")。<sp:RecipientToken> 识别用于对从服务器返回给客户机的消息进行签名的密匙对,同样使用的是 X.509 证书,并且证书被包含在来自服务器的消息中(sp:IncludeToken=".../AlwaysToInitiator")。
<sp:AlgorithmSuite> 元素识别用于进行签名的一组算法。<sp:IncludeTimestamp> 提供了用于每个消息的时间戳(用于保护服务免受重播式攻击,在这种攻击下,消息在传输中被捕获并被重新提交,从而扰乱或中断服务)。<sp:OnlySignEntireHeadersAndBody> 元素指定签名将在消息的整个头部或消息体中完成,而不是在某些嵌套的元素(另一种安全措施,防止发生重写消息的攻击类型)中完成。<sp:SignedParts> 元素识别将要被签名的消息部分 — 在本例中为 SOAP 消息 Body。
清单 1 中的 WS-Policy 文档的最后一部分提供了特定于 Rampart 的配置信息。这是 “Axis2 WS-Security 基础” 中使用的 Rampart 配置的更加复杂的版本,这一次包括了一个 <ramp:user> 元素来识别将用于消息签名的密匙,以及一个 <ramp:signatureCrypto> 元素,用于配置包含客户机私有密匙和服务器证书的 keystore。被引用的 keystore 文件必须在运行时提供给类路径。在示例应用程序中,keystore 文件在构建期间被复制到 client/bin 目录中。
密码回调类与 “Axis2 WS-Security 基础” 中使用的类稍有不同。在那篇文章中,只需要在服务器使用密码回调,并且只需要检验(用于明文式 UsernameToken)或设置(用于散列式 UsernameToken)与特定用户名匹配的密码。对于本文使用的公开密匙加密,回调必须提供用于保护 keystore 内的私有密匙的密码。并且需要对客户机(为客户机私有密匙提供密码)和服务器(为服务器私有密匙提供密码)使用单独的回调。清单 2 展示了回调的客户端版本:
清单 2. 客户机密码回调
/** * Simple password callback handler. This just checks if the password for the private key * is being requested, and if so sets that value. */ public class PWCBHandler implements CallbackHandler { public void handle(Callback[] callbacks) throws IOException { for (int i = 0; i < callbacks.length; i++) { WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i]; String id = pwcb.getIdentifer(); int usage = pwcb.getUsage(); if (usage == WSPasswordCallback.DECRYPT || usage == WSPasswordCallback.SIGNATURE) { // used to retrieve password for private key if ("clientkey".equals(id)) { pwcb.setPassword("clientpass"); } } } } } |
清单 2 中的回调旨在支持使用同一个公开-私有密匙对进行签名和解密,因此将在两种情况下进行检查并对任意一种情况返回相同的密码。
清单 1 中的 WS-Policy 用于客户机,与此相对应的是用于服务器的 WS-Policy(示例代码中的 sign-policy-server.xml),两者只在 Rampart 配置的细节方面略有不同。同样,清单 2 密码回调类的服务器版本只在标识符值和返回的密码方面有所差异。
运行示例应用程序
build.properties 文件引用了将由示例应用程序使用的 client.policy 和 server.policy 文件。在文件的补充版中分别被设置为 sign-policy-client.xml 和 sign-policy-server.xml,因此您应当只需要构建应用程序。通过将控制台打开到 jws05code 目录并输入 ant,您可以使用 Ant 完成任务。如果所有内容均配置正确,那么应当在 jws05code 目录中得到一个 library-signencr.aar Axis2 服务归档文件。通过使用 Axis2 Administration 页面上传 .aar 文件,将该服务部署到您的 Axis2 服务器安装中,然后通过在控制台输入 ant run 来试用客户机。如果所有内容都正确设置,那么应当看到如图 1 所示的输出:
图 1. 运行应用程序的控制台输出
要查看消息中的实际 WS-Security 信息,需要使用 TCPMon 之类的工具(见 参考资料)。首先设置 TCPMon 并在一个端口上接受来自客户机的连接,该连接随后转发给运行在另一个端口上的服务器(或另一个主机)。随后可以编辑 build.properties 文件并将 host-port 值修改为 TCPMon 的侦听端口。如果在控制台中再一次输入 ant run,应当会看到被交换的消息。清单 3 展示了一个样例客户机消息捕捉:
清单 3. 从客户机发送给服务器的第一条消息
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Header> <wsse:Security xmlns:wsse=".../oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1"> <wsu:Timestamp xmlns:wsu=".../oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Timestamp-3753023"> <wsu:Created>2009-04-18T19:26:14.779Z</wsu:Created> <wsu:Expires>2009-04-18T19:31:14.779Z</wsu:Expires> </wsu:Timestamp> <wsse:BinarySecurityToken xmlns:wsu=".../oasis-200401-wss-wssecurity-utility-1.0.xsd" EncodingType=".../oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType=".../oasis-200401-wss-x509-token-profile-1.0#X509v1" wsu:Id="CertId-2650016">MIICoDC...0n33w==</wsse:BinarySecurityToken> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="Signature-29086271"> <ds:SignedInfo> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> <ds:Reference URI="#Id-14306161"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <ds:DigestValue>SiU8LTnBL10/mDCPTFETs+ZNL3c=</ds:DigestValue> </ds:Reference> <ds:Reference URI="#Timestamp-3753023"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <ds:DigestValue>2YopLipLgBFJi5Xdgz+harM9hO0=</ds:DigestValue> </ds:Reference> </ds:SignedInfo> <ds:SignatureValue>TnUQtz...VUpZcm3Nk=</ds:SignatureValue> <ds:KeyInfo Id="KeyId-3932167"> <wsse:SecurityTokenReference xmlns:wsu=".../oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="STRId-25616143"> <wsse:Reference URI="#CertId-2650016" ValueType=".../oasis-200401-wss-x509-token-profile-1.0#X509v1"/> </wsse:SecurityTokenReference> </ds:KeyInfo> </ds:Signature> </wsse:Security> </soapenv:Header> <soapenv:Body xmlns:wsu=".../oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Id-14306161"> <ns2:getBook xmlns:ns2="http://ws.sosnoski.com/library/wsdl"> <ns2:isbn>0061020052</ns2:isbn> </ns2:getBook> </soapenv:Body> </soapenv:Envelope> |