You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WSS4J在Web应用中SOAP请求签名验证失败问题咨询

问题:Web应用环境下WSS4J签名SOAP请求失败,本地运行正常

我正在开发一个需要通过数字签名调用Web服务的任务:已经用JAX-WS生成了Web Service客户端,实现了SOAP Handler,使用Apache WSS4J库在请求发送前对SOAP信封签名,签名用的是加载到密钥库的.pfx文件。

本地Eclipse运行时一切正常,能收到服务器正确响应,但把代码集成到Web应用后,服务器端无法验证签名,返回soap:Server 75275错误。

我的疑问是:WSS4J是否可用于Web服务器环境下的SOAP请求签名?如果不可行,有哪些替代方案或解决方向?


相关代码

SOAP Handler签名逻辑

Document doc = soapEnv.getOwnerDocument(); 
WSSecSignature builder = new WSSecSignature(); 
WSSecTimestamp timestamp = new WSSecTimestamp(); 
WSSecUsernameToken usernametoken = new WSSecUsernameToken(); 
usernametoken.setPasswordType(WSConstants.PASSWORD_TEXT); 
usernametoken.setUserInfo(SOAP_USER, SOAP_USER_PASS); 
usernametoken.addNonce(); 
usernametoken.addCreated(); 

WSSecHeader secHeader = new WSSecHeader(); 
// secHeader.setMustUnderstand(true); 
secHeader.insertSecurityHeader(doc); 

usernametoken.build(doc, secHeader); 
timestamp.build(doc, secHeader); 

builder.setUserInfo(KEYSTORE_ALIAS, KEYSTORE_PWD); 
builder.setX509Certificate(cert); 
builder.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE); 
builder.setSignatureAlgorithm(WSConstants.RSA_SHA1); 
builder.setSigCanonicalization(WSConstants.C14N_EXCL_OMIT_COMMENTS); 
builder.setDigestAlgo(WSConstants.SHA1); 
builder.setUseSingleCertificate(true); 
builder.appendBSTElementToHeader(secHeader); 

// Set message parts to sign 
List<WSEncryptionPart> parts = new ArrayList<WSEncryptionPart>(); 
WSEncryptionPart bodyPart1 = new WSEncryptionPart("Body", WSConstants.URI_SOAP11_ENV, "Content"); 
bodyPart1.setElement(soapBody); 
parts.add(bodyPart1); 
builder.setParts(parts); 

// Set keystore and sign the document 
Properties properties = new Properties(); 
properties.setProperty("org.apache.wss4j.crypto.provider", "class org.apache.ws.security.components.crypto.Merlin"); 
Merlin crypto = (Merlin) CryptoFactory.getInstance(properties); 
crypto.setKeyStore(store); 
doc = builder.build(doc, crypto, secHeader); 
soapMsg.saveChanges(); 

获取证书和密钥库的代码

private boolean getX509Certificate() { 
    String keyAlias = ""; 
    try { 
        Security.addProvider(new BouncyCastleProvider()); 
        FileInputStream stream = new FileInputStream(KEYSTORE_FILE); //.pfx file 
        this.store = KeyStore.getInstance("PKCS12"); 
        store.load(stream, KEYSTORE_PWD.toCharArray()); 
        Enumeration<String> aliases = store.aliases(); 
        while (aliases.hasMoreElements()) { 
            String alias = aliases.nextElement(); 
            if (store.getCertificate(alias).getType().equals("X.509")) { 
                keyAlias = alias; 
            } 
        } 
        this.cert = (X509Certificate) store.getCertificate(keyAlias); 
        return true; 
    } catch (Exception e) { 
        e.printStackTrace(); 
    } 
    return false; 
} 

异常信息

soap:Server.75275 Response Value:soap:Server.75275
2018-05-11 18:56:17,977 ERROR [WebService](WFA:132301310 - 152335722 Associate:349730687 IE=349730685) Exception javax.xml.ws.soap.SOAPFaultException:
2018-05-11 18:56:17,978 ERROR [WebService](WFA:132301310 - 152335722 Associate:349730687 IE=349730685) [org.apache.axis2.jaxws.marshaller.impl.alt.MethodMarshallerUtils.createSystemException(MethodMarshallerUtils.java:1363)
org.apache.axis2.jaxws.marshaller.impl.alt.MethodMarshallerUtils.demarshalFaultResponse(MethodMarshallerUtils.java:1089)
org.apache.axis2.jaxws.marshaller.impl.alt.DocLitWrappedMinimalMethodMarshaller.demarshalFaultResponse(DocLitWrappedMinimalMethodMarshaller.java:684)
org.apache.axis2.jaxws.client.proxy.JAXWSProxyHandler.getFaultResponse(JAXWSProxyHandler.java:626)
org.apache.axis2.jaxws.client.proxy.JAXWSProxyHandler.createResponse(JAXWSProxyHandler.java:566)
org.apache.axis2.jaxws.client.proxy.JAXWSProxyHandler.invokeSEIMethod(JAXWSProxyHandler.java:432)
org.apache.axis2.jaxws.client.proxy.JAXWSProxyHandler.invoke(JAXWSProxyHandler.java:213)
com.sun.proxy.$Proxy256.synchronousSendAndReceive(Unknown Source)


回答

首先明确:WSS4J完全可以在Web服务器环境下用于SOAP请求签名,你本地正常、Web环境失败的问题大概率是环境差异导致的,而非库本身不支持。下面给你几个排查方向和解决建议:

1. 密钥库文件路径问题

你在本地用FileInputStream(KEYSTORE_FILE)加载.pfx文件,到了Web应用中,这个路径可能不再有效。Web容器下,文件路径是相对于容器的工作目录,而非你本地项目的路径。

解决办法:

  • 把.pfx文件放到Web应用的WEB-INF/resources目录下,用类加载器获取输入流:
    InputStream stream = getClass().getResourceAsStream("/WEB-INF/resources/your-keystore.pfx");
    
  • 或者配置绝对路径,通过环境变量或配置文件注入,避免硬编码。

2. 安全提供者加载问题

你在代码中手动添加了BouncyCastleProvider(),但Web服务器可能已经有自己的安全提供者配置,或者类加载器隔离导致这个提供者没有被正确加载。

解决办法:

  • 检查Web服务器的JRE/lib/security/java.security文件,确认BouncyCastle是否已经被注册为安全提供者。如果没有,可以在那里添加:
    security.provider.N=org.bouncycastle.jce.provider.BouncyCastleProvider
    
    (N是序列数,放在现有提供者后面)
  • 或者在代码中用Security.insertProviderAt(new BouncyCastleProvider(), 1)确保它被优先加载,避免类加载器问题。

3. SOAP消息的Canonicalization(规范化)差异

本地和Web环境下,SOAP消息的规范化处理可能因为JAX-WS实现、容器的XML解析器不同,导致签名的哈希值不一致。你用的是C14N_EXCL_OMIT_COMMENTS,要确保两边的XML解析行为一致。

解决办法:

  • 开启WSS4J的调试日志,对比本地和Web环境下生成的SOAP消息(特别是签名部分)是否一致。可以添加日志配置:
    log4j.logger.org.apache.wss4j=DEBUG
    
  • 检查Web容器使用的XML解析器(比如Xerces vs 内置的JDK解析器),是否和本地Eclipse使用的一致。必要时可以在Web应用中指定使用特定的XML解析器。

4. 类加载器隔离问题

Web容器(比如Tomcat、WebLogic)有自己的类加载器层次,可能导致WSS4J的类和JAX-WS的类来自不同的类加载器,出现奇怪的兼容性问题。

解决办法:

  • 确保Web应用的WEB-INF/lib中的WSS4J、JAX-WS相关依赖版本和本地运行的一致,避免容器提供的旧版本覆盖你的依赖。
  • 如果是Tomcat,可以在conf/catalina.properties中添加org.apache.wss4j相关包的优先加载配置,或者把依赖放到容器的lib目录(不推荐,除非必要)。

5. 时间戳同步问题

你添加了WSSecTimestamp,如果Web服务器的系统时间和Web服务端的时间差过大,可能导致签名验证失败。

解决办法:

  • 检查Web服务器的系统时间,确保和服务端时间同步(误差尽量在几分钟内)。
  • 可以调整时间戳的有效期,比如timestamp.setTimeToLive(300)(设置5分钟有效期)。

替代方案(如果上述都无效)

如果确实遇到WSS4J在Web环境下的兼容性问题,你可以考虑:

  • 使用JAX-WS内置的javax.xml.ws.handler.soap.SOAPHandler结合Java Security API手动实现签名,但复杂度较高。
  • 改用Apache CXF作为JAX-WS客户端实现,它内置了对WS-Security的支持,配置更简洁,在Web环境下的兼容性更好。

内容的提问来源于stack exchange,提问作者Arun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 08:57:51