【问题标题】:Missing Client Certificate Chain Issue while connecting with Service与服务连接时缺少客户端证书链问题
【发布时间】:2020-01-06 14:44:37
【问题描述】:

有一个 Spring Boot 应用程序与基于 SOAP 的服务连接。我们的应用程序有一个 MQ 侦听器,它从本地队列接收消息。一旦消息从该队列中出列,它就会对服务进行 API 调用。

与基于 SOAP 的服务的所有通信都是通过 2 路 SSL 完成的。

现在我们在连接基于 SOAP 的服务时遇到了问题。在基于 SOAP 的服务端间歇性收到缺少客户端证书链错误。

在我们的 JmsConfig 类中,我们将侦听器工厂的并发设置为 10,如下所示,用于并发执行和并行处理来自队列的消息。

 @Bean
 public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(){
   DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
   try {
          factory.setConnectionFactory(cachingConnectionFactory(jmsConnectionFactoryX20()));
          factory.setConcurrency("10");
   } catch (JMSException e) {
          e.printStackTrace();
   }
   return factory;
 }

当我们在使用消息后进一步从 Spring Boot 应用程序调用 SOAP 服务时,大多数消息的 SOAP 服务日志中都会出现缺少客户端证书链错误。在队列中批量加载 1k 条消息并随后并发执行时,大约 850 条消息在 SOAP 服务端失败,只有 150 条通过。

只有在通过上面的代码 sn-p 以编程方式设置并发时才会出现错误。如果我注释掉 setConcurrency 调用,所有 1k 消息都会在 SOAP 服务端得到处理,而不会出现任何证书链错误。

您能否看看我们的配置,并分享一些关于并发导致此错误的原因。

ApplicationWebServiceImpl.java(从 JMS 监听器方法调用 callWebService() 方法)

  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.beans.factory.annotation.Value;
  import org.springframework.stereotype.Service;
  import org.springframework.util.StringUtils;
  import org.springframework.ws.client.core.WebServiceTemplate;
  import org.springframework.ws.soap.SoapMessage;

  @Service
  public class ApplicationWebServiceImpl implements ApplicationWebServices {

  @Autowired
  private WebServiceTemplate wsTemplate;

  @Value("${soap.clientEndpoint}")
  private String clientEndpoint;

  @Value("${soap.action}")
  private String soapAction;

  @Override
  public Object callWebService(Object request, String serviceName) throws ClientApplicationException {

      return wsTemplate.marshalSendAndReceive(clientEndpoint + serviceName, request, message -> {
          SoapMessage msg = (SoapMessage) message;
          msg.setSoapAction(soapAction + StringUtils.capitalize(serviceName));
      });
  }
}

ClientApplication(Spring boot 主类)

@SpringBootApplication
@ComponentScan("com.abc.ca")    
public class ClientApplication {

  @Value(value = "${cert.keyStoreFileName}")
  private String keyStoreFileName;

  @Value(value = "${cert.trustStoreFileName}")
  private String trustStoreFileName;

  @Value(value = "${cert.disabledAlgorithms}")
  private String disabledAlgorithms;

  @PostConstruct
  public void getKeyTrustStore() {
      Security.setProperty("jdk.certpath.disabledAlgorithms", disabledAlgorithms);
      String filePath = Thread.currentThread().getContextClassLoader().getResource(keyStoreFileName).getFile();

      System.setProperty("javax.net.ssl.keyStore", filePath);
      System.setProperty("javax.net.ssl.keyStorePassword", "changeme");
      filePath = Thread.currentThread().getContextClassLoader().getResource(trustStoreFileName).getFile();      

      System.setProperty("javax.net.ssl.trustStore", filePath);
      System.setProperty("javax.net.ssl.trustStorePassword", "changeme");
  }

  public static void main(String[] args) {
      SpringApplication.run(ClientApplication.class, args);
  }
}

【问题讨论】:

  • 这与 IBM MQ 有关吗?看来实际的证书问题是您的 SOAP 服务调用而不是 MQ?
  • 是的,它是 IBM MQ。证书问题与 SOAP 服务调用有关。未设置并发时,SOAP 服务可以完美地处理 1k 负载。当我在 MQ 配置类中将并发设置为 10 时,对于某些消息,SOAP 服务给出缺少客户端证书链错误。
  • 当您将并发设置为大于 1 时,您将在应用程序中引入并发。您的应用程序需要确保每个消费者都可以使用所需的客户端证书链。我怀疑您的变量要么超出范围要么被删除,然后才能使用它们。因此,正如@JoshMc 所暗示的那样,错误很可能在您调用 SOAP 服务的逻辑中不是并发安全的。你应该分享这个逻辑,这样我们就可以帮助确定这是否是问题所在。
  • 您好,我已经分享了服务调用代码以及设置证书的类。

标签: java spring-boot concurrency ibm-mq


【解决方案1】:

为了更新,我通过在 wsTemplate 上显式调用 setMessageSender 并设置 HttpsUrlConnectionMessageSender 解决了上述问题。

感谢@chughts 给我指点。

干杯!

 wsTemplate.setMessageSender(httpsUrlConnectionMessageSender());

 @Bean 
 public HttpsUrlConnectionMessageSender httpsUrlConnectionMessageSender() throws Exception { 
     HttpsUrlConnectionMessageSender httpsUrlConnectionMessageSender = new 
                HttpsUrlConnectionMessageSender();

    httpsUrlConnectionMessageSender.setTrustManagers(
            loadDefaultTrustStore());

    httpsUrlConnectionMessageSender.setKeyManagers(
            loadKeyStore());

    httpsUrlConnectionMessageSender.setHostnameVerifier(new HostnameVerifier() { 
       @Override 
       public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) { 
          if ("localhost".equals(hostname)) { 
            return true; 
          } 
          return false; 
       } 
    });     
    return httpsUrlConnectionMessageSender; 
}

private KeyManager[] loadKeyStore() throws KeyStoreException,
     FileNotFoundException, IOException, NoSuchAlgorithmException,
     CertificateException, UnrecoverableKeyException {

     KeyStore keyStore = KeyStore.getInstance("jks");

     FileInputStream fileInputStreamKeyStore = new FileInputStream("/ca/appKeystore.jks");
     keyStore.load(fileInputStreamKeyStore,"changeme".toCharArray());

     KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory
            .getDefaultAlgorithm());        
     keyFactory.init(keyStore,"changeme".toCharArray());
     return keyFactory.getKeyManagers();
}

private TrustManager[] loadDefaultTrustStore() throws NoSuchAlgorithmException,
                KeyStoreException {
     TrustManagerFactory trustFactory = 
     TrustManagerFactory.getInstance(TrustManagerFactory
        .getDefaultAlgorithm());
     trustFactory.init((KeyStore)null);     
     return trustFactory.getTrustManagers();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-05
    • 2020-07-08
    • 1970-01-01
    • 1970-01-01
    • 2012-08-25
    • 2021-03-09
    • 2020-10-23
    • 2016-11-18
    相关资源
    最近更新 更多