为了演示MLS,下面的例子对一个样本性的SOAP/HTTP消息进行了加密。这个作为示例用的是一个简单的计算器服务,其界面包括一个可以接受两个数字的乘法方法,还有一个使用Xfire and WSS4JJ框架的Java客户端。
为了演示MLS,下面的例子对一个样本性的SOAP/HTTP消息进行了加密。这个作为示例用的是一个简单的计算器服务,其界面包括一个可以接受两个数字的乘法方法,还有一个使用Xfire and WSS4JJ框架的Java客户端。(笔者在一台使用WebSphere 6.1的主机上测试了Web服务),不过多数应用程序服务器支持基本的SOAP消息安全,因此你可以在另外一台可选的服务器上运行这项服务)。你可以从这儿下载这个示例代码。其中的ReadMe.html文件描述了下载的zip文件的内容。
这个计算器服务通过执行EJB来备份。当然,你可以有另外的选择,如选择JavaBeans等。下面展示的是EJB的界面:
package com.dev.ws.security.ejb;
public interface Calculator extends javax.ejb.EJBObject {
public float multiply(float a, float b) throws java.rmi.RemoteException;
}
The EJB bean simply implements the multiply method as follows:
package com.dev.ws.security.ejb;
public class CalculatorBean implements javax.ejb.SessionBean {
...
public float multiply(float a, float b){
return a*b;
}
} |
此服务的WSDL显示如下。注意,它使用了一个标准的SOAP/HTTP绑定,并且使这个服务端点在URL中是可用的。
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions ...>
<wsdl:types>
...
<wsdl:portType name="Calculator">
<wsdl:operation name="multiply">
<wsdl:input message="intf:multiplyRequest"name="multiplyRequest"/>
<wsdl:output message="intf:multiplyResponse" name="multiplyResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="CalculatorSoapBinding" type="intf:Calculator">
<wsaw:UsingAddressing wsdl:required="false"
xmlns:wsaw="http://www.w3.org/2006/02/addressing/wsdl"/>
<wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="multiply">
<wsdlsoap:operation soapAction="multiply"/>
...
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="CalculatorService">
<wsdl:port binding="intf:CalculatorSoapBinding" name="Calculator">
<wsdlsoap:address
location="http://localhost:9080/WSUTSigEncRouterWeb/services/Calculator"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions> |
在此示例中,使用Xfire stub生成器从WSDL创建CalculatorServiceClient。下面的代码使用这个stub来调用web服务的乘法方法,并将参数5和7传递给它:
package com.dev.ws.client.calculator.driver;
...
public class WSClientUTSigEnc {
// Non-SSL URL
public static String UT_ENDPOINT
= "http://localhost:9080/WSUTSigEncRouterWeb/services/Calculator";
public static void main(String[] args) throws MalformedURLException {
CalculatorServiceClient sc = new CalculatorServiceClient();
Calculator calc = sc.getCalculator(UT_ENDPOINT);
float a = 5f;
float b = 7f;
System.out.println(a + " * " + b + " = " + calc.multiply(a,b));
}
After starting the server and running the client, you can use a TCP/IP monitor at port
9080 to observe the messages that the client sends to the web service. You should observe
a SOAP message that looks like this:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<multiply xmlns="http://ejb.security.ws.dev.com">
<a>5.0</a>
<b>7.0</b>
</multiply>
</soap:Body>
</soap:Envelope>
The client outputs the expected result after getting the SOAP response:
5.0 * 7.0 = 35.0 |
上面的SOAP消息只是简单地将方法请求包含进来,并没有安全的报头。为了在SOAP消息的级别上支持加密,应用程序服务器和客户端必须加以配置来支持XML加密。客户端从一个X509的证书使用一个公钥来对SOAP消息加密,而服务器相对应地使用私钥对此消息解密。一对公/私钥必须由给客户端的公钥和给服务器的私钥来生成。(可参考如下的XFire文章来查看如何创建这对密钥的相关细节信息。应用程序服务器的配置与服务器息息相关,因此请参考你的服务器的相关文档资料,以得到XML加密的用法说明。你可以在这里找到本文的WebSphere配置的相关指导。)
在XFire框架中,你使用处理程序来处理客户端的SOAP消息。每一个处理程序接受一套属性,这些属性可以规定WSS4J必须实施的消息安全性。为了执行加密,你必须为以下的几个方面规定属性:
客户端公钥的存储位置
加密的运行法则
要进行加密的消息的部分
下面列示计算器服务的客户端加密代码:
protected static void configureEncryption(Properties config)
{
// Encrypt action
config.setProperty(WSHandlerConstants.ACTION, WSHandlerConstants.ENCRYPT);
//The property file describes the public key used for encryption
config.setProperty(WSHandlerConstants.ENC_PROP_FILE,
"com/dev/ws/client/driver/outsecurity_enc.properties");
config.setProperty(WSHandlerConstants.ENCRYPTION_USER, "serveralias");
// The encryption algorithm
config.setProperty(WSHandlerConstants.ENC_SYM_ALGO,WSConstants.TRIPLE_DES);
// Encryption Key Identifier Types
config.setProperty(WSHandlerConstants.ENC_KEY_ID, "SKIKeyIdentifier");
// Encrypt the SOAP body
String bodyPart = "{Content}{}Body";
config.setProperty(WSHandlerConstants.ENCRYPTION_PARTS, bodyPart);
} |
注意:客户端的密钥存储配置包含在属性文件outsecurity_enc.properties中。这个属性文件就如下面的显示一样描述了密钥存储的位置和证书:
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=keystorePass
org.apache.ws.security.crypto.merlin.alias.password=client344Password
org.apache.ws.security.crypto.merlin.keystore.alias=serveralias
org.apache.ws.security.crypto.merlin.file=com/dev/ws/client/driver/clientStore.jks |
一旦客户端和服务器配置完毕,运行客户端就会在TCP/IP监视器上产生如下的SOAP消息:
<soap:Envelope ...>
<soap:Header>
<wsse:Security ...>
<xenc:EncryptedKey ...>
<xenc:EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
<ds:KeyInfo ...>
...
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>
M+Tp2Q4ZtBJtpT1q7SkOALNnpv57Cgh/4EHV0gHONtUZsYQLIVYYWNdYqIkjb81pgxBFU94WKQK
au2BEZbF8rL4KdA9tdfb3McRzCOJDcGl4eDs2FC1Pe1Bj0b2VJ+m4D83EhGSsUEeItp+SZcF0Kw
jh5dEcV61Q4cscMZaruSg=
</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedKey>
<xenc:EncryptedData ...>
<xenc:CipherData>
<xenc:CipherValue...>
P8D3xHloRUSCvMA7gNaezLTtENS2R6oXJ8jByaBKvBl5t4joml2qIo9V2LXsnM3nuYJun2UADKfg...
</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
</wsse:Security>
</soap:Header>
<soap:Body>
<xenc:EncryptedData ...>
<xenc:CipherData ...>
<xenc:CipherValue ...>
dIxwIHuC9TCLbSmfsgohBr2A81lY+GfPA7lofgXPcMvcblO+hOVeiKMyxXvuZF8M2fEtmHTa3kVY
fNDYFAKauoDwq4lWBKMuk4f0s8mTkhyBJrMmbD2mrw==
</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
</soap:Body>
</soap:Envelope> |
这个SOAP消息现在已经被加密,并且明文已经被其密码数值所替换。这个SOAP报头包含着加密方法的信息。
本示例演示了一个大体的配置,它需要使用消息级安全(Message Level Security)来执行加密。虽然消息级安全与TLS相比,在性能和对端到端的安全性的支持方面拥有十分确定的优越性,你必须考虑它本身所带来的额外的复杂性。对于许多应用程序来说,对这种精细的和端到端的消息安全的支持并非关键所在,而TLS就算是一个在保密和加密方面不错的方案了。
下一节我们将讨论身份验证问题并且讨论一个最简单的方案:UsernameToken身份验证。(责任编辑:李磊)