扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
作者:闹市孤僧 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领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者