科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道应用软件基于XFire实施WS-Security(第二部分)

基于XFire实施WS-Security(第二部分)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

对SOAP报文进行身份认证的方式很多,不过都是通过在SOAP报文头中添加一些安全凭证(Security Token)信息来完成的,主要包括以下一些身份凭证。

作者:闹市孤僧 2007年5月30日

关键字:

  • 评论
  • 分享微博
  • 分享邮件

服务端在接收添加WS-Security信息的SOAP请求报文前必须进行前置处理,和服务端相对应,客户端在发送SOAP请求报文前进行前置处理,添加WS-Security信息。因此使用DOMOutHandler将DOM转换为STAX的流模型,此外通过定义一个WSS4JOutHandler完成在SOAP头添加用户名/密码信息的操作。

在④-2处,定义了密码的类型,WSConstants.PW_DIGEST表示密码以摘要方式发送,这可以防止密码的传输安全。设置为WSConstants.PW_TEXT表示密码以明文方式发送。④-3指定了一个用户名client,一般情况下,用户的密码保存在文件中或数据库中,因此通过指定一个回调类UtPasswordHandler完成用户密码的获取操作,如④-4,回调类必须实现javax.security.auth.callback.CallbackHandler接口,UtPasswordHandler的代码如下所示:

代码清单4 UtPasswordHandler

package com.baobaotao.xfire.wss4j.client;

import java.util.*;

import javax.security.auth.callback.Callback;

import javax.security.auth.callback.CallbackHandler;

import org.apache.ws.security.WSPasswordCallback;

 

public class UtPasswordHandler implements CallbackHandler {

①客户端用户模拟数据库

private static final Map<String,String> pwMockDB = new HashMap<String,String>()

static{

pwMockDB.put("tom", "123456");

}

public void handle(Callback[] callbacks) {

WSPasswordCallback callback = (WSPasswordCallback) callbacks[0];

String id = callback.getIdentifer();

callback.setPassword(pwMockDB.get(id));②获取用户对应的密码

}

}

运行代码清单3,通过类似于TcpTrace的工具截取SOAP请求报文,你将可以看到带WS-Security报文头的SOAP报文:

<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:Header>

<wsse:Security soap:mustUnderstand="1"

xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401

-wss-wssecurity-secext-1.0.xsd">

<wsse:UsernameToken>

<wsse:Username>client</wsse:Username>

<wsse:Password>wOtW+umoWdf/7s7pyJrlFAp2xJo=</wsse:Password>

<wsse:Nonce>zJlCTQNCa+jHur2JdJF8Fw==</wsse:Nonce>

<wsu:Created>2007-04-09T09:11:05.593Z</wsu:Created>

</wsse:UsernameToken>

</wsse:Security>

</soap:Header>

<soap:Body>

<getRefinedTopicCount

xmlns="http://server.xfire.baobaotao.com">

<in0 xmlns="http://server.xfire.baobaotao.com">20</in0>

</getRefinedTopicCount>

</soap:Body>

</soap:Envelope>

报文中粗体的部分为WS-Security的信息,为了简洁,我们特意删除了报文中一些命名空间的内容。

<wsse:UsernameToken>元素被导入到SOAP报头中以携带身份信息。wsse:Username用于指定一个用户名,而<wsse:Password>则用于指定密码。有两种类型的密码:PasswordText和PasswordDigest,PasswordText以明文格式表示密码,而PasswordDigest则被定义为Base64编码的SHA-1散列值。还有两个可选元素<wsse:Nonce>和<wsu:Created>:前者是发送方创建的一个随机值,后者则是一个时间戳。如果<wsse:Nonce>和<wsu:Created>两个元素中至少出现了一个,计算PasswordDigest的算法如下(每次PasswordDigest的值都是不一样的):

PasswordDigest = Base64(SHA-1(nonce+created+password))

如果没有使用其他的安全机制,PasswordDigest是通过非保密渠道发送用户名和口令的最佳方法。即使使用XML加密对<wsse:Password>元素进行加密,PasswordText依然可以使用。

对SOAP报文进行数字签名

使用用户名/密码虽然可以验证SOAP请求报文发送者的身份实现授权访问,但是服务端却无法保证报文在传输过程中没有被篡改——黑客可以截取使用了UsernameToken的SOAP报文并在篡改后再发送给服务端,就会使SOAP报文的完整性遭受破坏。

此外,仅使用UsernameToken的SOAP,客户端用户可以抵赖自己的操作行为,因为黑客确实可以通过一些手段(如键盘监听、暴力破解等)获取用户的密码。

而数字签名则可以解决以上的问题,保证交易的完整性和不可抵赖性。客户端通过私钥对SOAP报文进行数字签名,由于私钥只为个人拥有,因此不可抵赖性得到了保证。数字签名其实是使用私钥对报文的摘要进行加密,只有报文在传输过程中不被篡改,接收端在进行数字签名验证时才可能成功,因此完整性又得到了保证。

下面,我们在客户端对请求SOAP进行数字签名,而服务端验证客户端签名的合法性。客户端使用client私钥进行数字签名,服务端使用client的数字证书(包含client的公钥)验证客户端的签名。

服务端

服务端在验证客户端的签名时,必须访问serverStore.jks中的client数字证书,所以需要进行相应的配置:

代码清单5 applicationContext-ws-security.xml:数字签名

<bean id="bbtForumServiceSign"parent="baseWebService" >

<property name="serviceBean" ref="bbtForum" />

<property name="serviceClass"value="com.baobaotao.xfire.server.BbtForumService" />

<property name="name" value="BbtForumServiceSign" />

<property name="inHandlers">

<list>

<ref bean="domInHandler" />

<ref bean="wss4jInHandlerSign" />①通过该Handler进行数字签名验证

</list>

</property>

</bean>

<bean id="wss4jInHandlerSign"class="org.codehaus.xfire.security.wss4j.WSS4JInHandler">

<property name="properties">

<props>

②数字签名动作(因为是InHandler所以对应的操作是验证数字签名)

<prop key="action">Signature</prop>

③验证数字签名需要访问保存着client数字证书的密钥库,

通过属性文件提供相应的配置信息。

<prop key="signaturePropFile">

com/baobaotao/xfire/wss4j/server/insecurity_sign.properties

</prop>

</props>

</property>

</bean>

insecurity_sign.properties定义了serverStore.jks的位置以及访问密码,WSS4J使用org.apache.ws.security.components.crypto.Merlin类作为SecurityProvider。insecurity_sign.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=storepass②serverStore.jks的访问密码

org.apache.ws.security.crypto.merlin.file=META-INF/xfire/serverStore.jks③密钥库文件所在位置

客户端

客户端必须通过注册OutHandler,使用私钥对SOAP报文进行数字签名。客户端的私钥别名为client,存储在clientStore.jks的密钥库中。访问密钥库和私钥都必须提供密码,因此必须进行相应的设置。

代码清单6 BbtForumServiceSignClient

package com.baobaotao.xfire.wss4j.client;

public class BbtForumServiceSignClient {

private static XFireProxyFactory serviceFactory = new XFireProxyFactory();

public static void main(String[] args) {

Service serviceModel = new ObjectServiceFactory().create(BbtForumService.class);

try {

String serviceURL = "http://localhost:8080/baobaotao/service/BbtForumServiceSign";

BbtForumService service =(BbtForumService) serviceFactory.create(serviceModel,

serviceURL);

Client client = ((XFireProxy)Proxy.getInvocationHandler(service)).getClient();

client.addOutHandler(new DOMOutHandler());

 

Properties properties = new Properties();

properties.setProperty(WSHandlerConstants.ACTION, ①指定动作为签名

WSHandlerConstants.SIGNATURE);

②数字签名需要访问client私钥,私钥位于密钥库中,通过属性文件提供相应的信息

properties.setProperty(WSHandlerConstants.SIG_PROP_FILE,

"com/baobaotao/xfire/wss4j/client/outsecurity_sign.properties");

properties.setProperty(WSHandlerConstants.USER, "client");③密钥库中密钥对的别名

通过密码回调类获得密钥对私密的访问密码

properties.setProperty(WSHandlerConstants.PW_CALLBACK_CLASS,

PasswordHandler.class.getName());

properties.setProperty(WSHandlerConstants.SIG_KEY_ID, "IssuerSerial");

client.addOutHandler(new WSS4JOutHandler(properties));

int count = service.getRefinedTopicCount(20);

System.out.println("count:"+count);

} catch (MalformedURLException e) {

e.printStackTrace();

}

}

}

和用户名/密码进行身份认证相似,在进行数字签名时,也需要提供用户名和密码,不过两者截然不同,后者的用户名为密钥库中密钥对的别名,密码为私钥的访问密钥。③处的client为clientStore.jks密钥库中客户端密钥对的别名,私钥访问密码通过PasswordHandler获取,如下所示:

代码清单7 PasswordHandler

package com.baobaotao.xfire.wss4j.client;

public class PasswordHandler implements CallbackHandler {

private static final Map<String,String> pwMockDB = new HashMap<String,String>();

static{

pwMockDB.put("client", "clientpass");①client私钥的访问密码

}

public void handle(Callback[] callbacks) {

WSPasswordCallback callback = (WSPasswordCallback) callbacks[0];

String id = callback.getIdentifer();

callback.setPassword(pwMockDB.get(id));

}

}

客户端的私钥位于clientStore.jks中,访问clientStore.jks的配置信息通过outsecurity_sign.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=storepass①访问clientStore.jks的密码

org.apache.ws.security.crypto.merlin.file=META-INF/xfire/clientStore.jks

运行BbtForumServiceSignClient后,查看SOAP请求报文,你将看到报文头拥有一个<ds:Signature>的元素,包含了签名信息<ds:SignedInfo>、<ds:SignatureValue>、<ds:KeyInfo>等元素,它们分别代表签名信息、签名值以及签名所用密钥的信息:

<SOAP:Envelope xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">

<SOAP:Header>

<wsse:Security>

<ds:Signature>

<ds:SignedInfo>…<ds:SignedInfo>

<ds:SignatureValue></ ds:SignatureValue>

<ds:KeyInfo></ds:KeyInfo >

</ds:Signature>

</wsse:Security>

</SOAP:Header>

<SOAP:Body Id='Body'>

</SOAP:Body>

</SOAP:Envelope>

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章