问候好人。
我有一个我想使用的肥皂网络服务。我创建了一个小项目来模拟实际项目中的要求,尤其是在用户名令牌加密方面。
提供了有关如何在客户端加密密码的步骤,如下所示:
- 写入未加密的密码值。
- 然后,使用密码密钥证书的公共部分加密在步骤 1 中创建的数据块。使用 RSA 算法,并使用 PKCS #1.5 填充(不是 OAEP),并将结果添加到加密流中——这成为通过 API 提交的加密密码。
- 使用 base64 编码将生成的加密字节数组转换为字符串。在 API 请求中将此 base64 编码字符串显示为发起方 SecurityCredential 值。
- 使用专门为此目的颁发给发起者的 X509 证书中的公钥加密的密码。
到目前为止,我已经能够创建客户端和服务器,并且能够发送请求并获得响应。
我还可以通过在 ClientPasswordCallback 类中将用户名令牌和密码作为纯文本传递并在 ServerPasswordCallback 类中检查这些凭据来保护 Web 服务。
我走得更远,在一个单独的请求中,使用wss4j、RSA、X509加密了消息的正文部分,其中我将公钥存储在 clientKey.jks 和私钥存储在 privateKey.jks 中,并在客户端和服务器密码回调处理程序 我已经能够在客户端加密正文部分并在服务器上解密它。
挑战:我遇到的主要挑战是将上述两个步骤组合在一个请求中,以便使用公钥,我能够加密用户名令牌中的密码并使用私钥在服务器端解密相同钥匙。
注意我已经使用 jdk 附带的 keygen 工具生成了用于测试的密钥。
我想象 ClientPasswordCallback 类中会有两个密码,一个用于 clientKey.jks 密钥库,另一个用于需要加密的密码。
这是我迄今为止能够存档的内容:
客户端
TestMathUtility 类
public static void main(String[] args) {
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
// Use the URL defined in the soap address portion of the WSDL
factory.setAddress("http://localhost:8080/MathUtility/services/MathUtilityPort");
// Utilize the class which was auto-generated by Apache CXF wsdl2java
factory.setServiceClass(MathUtility.class);
Object client = factory.create();
// Adding Logging Interceptors
LoggingOutInterceptor loggingOutInterceptor = new LoggingOutInterceptor();
loggingOutInterceptor.setPrettyLogging(true);
ClientProxy.getClient(client).getOutInterceptors().add(loggingOutInterceptor);
LoggingInInterceptor loggingInInterceptor = new LoggingInInterceptor();
loggingInInterceptor.setPrettyLogging(true);
ClientProxy.getClient(client).getInInterceptors().add(loggingInInterceptor);
// Set up WS-Security Encryption
// Reference: https://ws.apache.org/wss4j/using.html
Map<String, Object> props = new HashMap<String, Object>();
props.put(WSHandlerConstants.USER, "testkey");
props.put(WSHandlerConstants.ACTION, WSHandlerConstants.ENCRYPT);
props.put(WSHandlerConstants.PASSWORD_TYPE, "PasswordText");
props.put(WSHandlerConstants.ENC_PROP_FILE, "clientKeystore.properties");
props.put(WSHandlerConstants.ENCRYPTION_PARTS, "{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body");
props.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientPasswordCallback.class.getName());
WSS4JOutInterceptor wss4jOut = new WSS4JOutInterceptor(props);
ClientProxy.getClient(client).getOutInterceptors().add(wss4jOut);
try {
// Call the Web Service to perform an operation
int response = ((MathUtility)client).addIntegers(5, 10);
System.out.println("Response we've got ========= "+response);
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
ClientPasswordCallback 类
public class ClientPasswordCallback implements CallbackHandler {
@Override
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
// set the password for our message.
pc.setPassword("clientstorepass");
}
}
服务器端
数学实用类
@WebService(targetNamespace = "http://utility.math.com/", portName = "MathUtilityPort", serviceName = "MathUtilityService")
public class MathUtility {
public int addIntegers(int firstNum, int secondNum) {
return firstNum + secondNum;
}
public int factorial(int n) {
int result = 1;
for (int i = 1; i <= n; i++) {
result = result * i;
}
return result;
}
}
ServerPasswordCallback 类
public class ServerPasswordCallback implements CallbackHandler {
@Override
public void handle(Callback[] arg0) throws IOException,
UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) arg0[0];
// set the password for our message.
pc.setPassword("storepass");
}
}
cxf-beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<bean id="myPasswordCallback" class="com.math.utility.security.ServerPasswordCallback"/>
<jaxws:endpoint xmlns:tns="http://utility.math.com/" id="mathutility"
implementor="com.math.utility.MathUtility" wsdlLocation="wsdl/mathutility.wsdl"
endpointName="tns:MathUtilityPort" serviceName="tns:MathUtilityService"
address="/MathUtilityPort">
<jaxws:features>
<bean class="org.apache.cxf.feature.LoggingFeature" />
</jaxws:features>
<jaxws:inInterceptors>
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="user" value="testkey"/>
<entry key="action" value="Encrypt"/>
<entry key="passwordType" value="PasswordText"/>
<entry key="decryptionParts" value="{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body"/>
<entry key="decryptionPropFile" value="serverKeystore.properties"/>
<entry key="passwordCallbackRef">
<ref bean="myPasswordCallback"/>
</entry>
</map>
</constructor-arg>
</bean>
</jaxws:inInterceptors>
</jaxws:endpoint>
clientKeyStore.properties 文件在服务器端使用相同的结构
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.file=clientkeystore.jks
org.apache.ws.security.crypto.merlin.keystore.password=clientstorepass
org.apache.ws.security.crypto.merlin.keystore.type=jks
未提供使用的 .jks 文件
注意我没有使用弹簧。