Spring Boot WS-Security SOAP客户端报WSSecurityException找不到密钥别名
Spring Boot SOAP客户端证书签名异常排查方案
问题背景
基于Spring Boot开发SOAP客户端时,接口要求请求头携带数字证书,配置Wss4jSecurityInterceptor证书环节抛出异常。客户端计划部署在WildFly服务器,证书文件存放在项目resources目录,打包war后证书文件保留在包内对应路径。
现有配置代码
private static final Resource KEYSTORE_LOCATION = new ClassPathResource("client-keystore.jks"); private static final String KEYSTORE_PASSWORD = "password"; private static final String KEY_ALIAS = "alias"; @Bean TrustManagersFactoryBean trustManagers() throws Exception { TrustManagersFactoryBean factoryBean = new TrustManagersFactoryBean(); factoryBean.setKeyStore(keyStore().getObject()); return factoryBean; } @Bean HttpsUrlConnectionMessageSender messageSender() throws Exception { HttpsUrlConnectionMessageSender sender = new HttpsUrlConnectionMessageSender(); KeyManagersFactoryBean keyManagersFactoryBean = new KeyManagersFactoryBean(); keyManagersFactoryBean.setKeyStore(keyStore().getObject()); keyManagersFactoryBean.setPassword(KEYSTORE_PASSWORD); keyManagersFactoryBean.afterPropertiesSet(); sender.setKeyManagers(keyManagersFactoryBean.getObject()); sender.setTrustManagers(trustManagers().getObject()); return sender; } @Bean KeyStoreFactoryBean keyStore() throws GeneralSecurityException, IOException { KeyStoreFactoryBean factoryBean = new KeyStoreFactoryBean(); factoryBean.setLocation(KEYSTORE_LOCATION); factoryBean.setPassword(KEYSTORE_PASSWORD); return factoryBean; } @Bean public Jaxb2Marshaller marshaller() { Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); marshaller.setContextPath("contextpath"); return marshaller; } @Bean Wss4jSecurityInterceptor securityInterceptor() throws Exception { Wss4jSecurityInterceptor securityInterceptor = new Wss4jSecurityInterceptor(); securityInterceptor.setSecurementActions("Signature"); securityInterceptor.setSecurementUsername(KEY_ALIAS); securityInterceptor.setSecurementPassword(KEYSTORE_PASSWORD); securityInterceptor.setSecurementSignatureCrypto(cryptoFactoryBean().getObject()); return securityInterceptor; } @Bean SOAPConnector client() throws Exception { SOAPConnector client = new SOAPConnector(); System.out.println("client(): "); client.setInterceptors(new ClientInterceptor[] { securityInterceptor() }); client.setMessageSender(messageSender()); client.setMarshaller(marshaller()); client.setUnmarshaller(marshaller()); client.afterPropertiesSet(); return client; }
异常信息
Caused by: org.apache.wss4j.common.ext.WSSecurityException: Error during Signature: Original Exception was org.apache.wss4j.common.ext.WSSecurityException: Cannot find key for alias: [certificado] Original Exception was org.apache.wss4j.common.ext.WSSecurityException: Cannot find key for alias: [certificado] at org.apache.wss4j.dom.action.SignatureAction.execute(SignatureAction.java:174) at org.apache.wss4j.dom.handler.WSHandler.doSenderAction(WSHandler.java:238) at org.springframework.ws.soap.security.wss4j2.Wss4jHandler.doSenderAction(Wss4jHandler.java:58) at org.springframework.ws.soap.security.wss4j2.Wss4jSecurityInterceptor.secureMessage(Wss4jSecurityInterceptor.java:609) ... 80 more Caused by: org.apache.wss4j.common.ext.WSSecurityException: Cannot find key for alias: [certificado] Original Exception was org.apache.wss4j.common.ext.WSSecurityException: Cannot find key for alias: [certificado] at org.apache.wss4j.dom.message.WSSecSignature.computeSignature(WSSecSignature.java:615) at org.apache.wss4j.dom.action.SignatureAction.execute(SignatureAction.java:166) ... 83 more Caused by: org.apache.wss4j.common.ext.WSSecurityException: Cannot find key for alias: [certificado] at org.apache.wss4j.common.crypto.Merlin.getPrivateKey(Merlin.java:696) at org.apache.wss4j.dom.message.WSSecSignature.computeSignature(WSSecSignature.java:558)
排查与解决方案
- 优先修复别名不匹配问题
异常核心提示为找不到别名为certificado的密钥,代码中硬编码的KEY_ALIAS值为"alias",和密钥库实际存储的证书别名不一致。WSS4J执行签名时,会将securementUsername配置值作为别名去密钥库查找对应私钥。
先执行以下命令查看密钥库内实际的证书别名:
输出结果中keytool -list -v -keystore client-keystore.jks -storepass 你的密钥库密码Alias name字段即为可用别名,将代码中KEY_ALIAS的值替换为该实际值即可,按异常提示此处应为certificado。 - 补全缺失的Crypto配置Bean
现有代码中securityInterceptor()调用了cryptoFactoryBean().getObject(),但未给出该Bean的定义。WSS4J签名逻辑不会复用HTTPS消息发送器配置的密钥库,必须单独为签名拦截器指定Crypto实例,补充以下Bean配置:@Bean public CryptoFactoryBean cryptoFactoryBean() throws GeneralSecurityException, IOException { CryptoFactoryBean cryptoFactoryBean = new CryptoFactoryBean(); cryptoFactoryBean.setKeyStoreLocation(KEYSTORE_LOCATION); cryptoFactoryBean.setKeyStorePassword(KEYSTORE_PASSWORD.toCharArray()); return cryptoFactoryBean; } - WildFly部署配置说明
不需要提前将证书导入WildFly系统级密钥库,只要证书文件打包在war包类路径下即可正常加载。如果部署后提示找不到密钥库文件,可将ClassPathResource替换为ServletContextResource读取war包内资源,本地开发阶段ClassPathResource可正常读取resources目录下文件。 - 密钥条目有效性校验
若别名配置正确仍报错,用上述keytool命令检查对应别名的条目类型:WS-Security签名必须读取证书对应的私钥,若条目类型为trustedCertEntry说明仅导入了公钥,无法完成签名,需要重新导入包含私钥的证书条目,正确的签名用条目类型应为PrivateKeyEntry。
内容的提问来源于stack exchange,提问作者JuamBer
相关产品推荐
相关产品推荐

