Apache CXF 客户端在测试需要服务器名称指示 (SNI) 的服务器时出错

Apache CXF client error in testing server which needs Server Name Indication (SNI)

我们有一个使用 Apache CXF 制作的客户端,它运行正常,使用特定的服务器(即:https://serverexample.com/application/webservice?wsdl)。

但是服务器已经转移到另一个 IP,现在它在同一个 IP 中有两个带有 TLS 和 SNI(服务器名称指示)的 SSL 证书,现在我们的应用程序失败并出现此错误:

javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative DNS name matching serverexample.com found

我知道当 https 获得错误的证书(它有另一个服务器名称)时会发生这种情况,因此与我的不匹配。

我试图找出 openssl 发生了什么,url 只有在我输入 servername:

时才有效
# openssl s_client -connect serverexample.com:443 -tls1
Certificate chain
 0 s:/CN=otherserver.com/OU=Servers/O=MyOrganization/C=ES
   i:/CN=ACV20/OU=PKACV/O=ACV/C=ES

# openssl s_client -connect serverexample.com:443 -servername serverexample.com
Certificate chain
 0 s:/CN=serverexample.com/OU=Servers/O=MyOrganization/C=ES
   i:/CN=ACV220/OU=PKACV1/O=ACV2/C=ES

此时生成的apache客户端出现错误:

final URL wsdlURL = new URL("https://serverexample.com/application/webservice?wsdl");
final Operation_Service ss = new Operation_Service(wsdlURL, SERVICE_NAME);

在新 Operation_Service 中失败:

@WebServiceClient(name = "ENI.Operation", 
                  wsdlLocation = "https://serverexample.com/application/webservice?wsdl",
                  targetNamespace = "http://inter.ra.es/awrp/wsdl") 
public class Operation_Service extends Service {
    public final static URL WSDL_LOCATION;
    public final static QName SERVICE = new QName("http://inter.ra.es/awrp/wsdl", "ENI.Operation");
    public final static QName Operation = new QName("http://inter.ra.es/awrp/wsdl", "ENI.Operation");
    static {
        URL url = null;
        try {
            url = new URL("https://serverexample.com/application/webservice?wsdl");
        } catch (final MalformedURLException e) {
            java.util.logging.Logger.getLogger(Operation_Service.class.getName())
                .log(java.util.logging.Level.INFO, 
                     "Can not initialize the default wsdl from {0}", "https://serverexample.com/application/webservice?wsdl");
        }
        WSDL_LOCATION = url;
    }

     public Operation_Service(final URL wsdlLocation, final QName serviceName) {
        super(wsdlLocation, serviceName);
    }

javax.xml.ws.Service 调用 javax.xml.ws.spi.ServiceDelegate,一个抽象 class 由 org.apache.cxf.jaxws 中的一些 class 实现。但在这里我失去了它,我不不知道该看什么...

我们的客户端在 java 7.0 中运行,在 weblogic 12.1.1.0 上使用 apache cxf 3.0.4。我在 Java 6 中读到 SNI 有问题,但我们在这里使用 7.0。

我不知道我能做什么。 java 或我们的客户端中是否有一些选项来指示我们尝试连接的服务器名称(如在 openssl 中)?

希望对您有所帮助:

为了解决 SSL 握手异常,您可以选择 2 种方式: 在您的代码开头设置此系统 属性:

System.setProperty("jsse.enableSNIExtension", "false");

将程序的 VM 参数设置为:-Djsse.enableSNIExtension=false

它是 JDK 1.8 中的 "improvements" 和服务器具有多个证书这一事实的组合。

JDK 1.8 中的错误解释如下: http://lea-ka.blogspot.com/2015/09/wtf-collection-how-not-to-add-api.html

它是这样的:

  • 由于错误 https://bugs.openjdk.java.net/browse/JDK-8072464,未发送 SNI 扩展。
  • 服务器 returns 证书 "CN=otherserver.com",可能还有 SAN (SubjectAlternativeName) "otherserver.com" 而没有其他。
  • JDK 中的默认 "URL spoofing" 检查已执行,并比较 URL 中的 "serverexample.com" 和证书中的 "otherserver.com"。糟糕,你得到了例外。

最简单 修复是下载所有 WSDL 和 XSD 文件并将它们打包到您的 jar 中。

如果您确实需要从远程服务器获取 WSDL,这可能会有所帮助:

最终版 URL wsdlURL = 新版 URL("https://otherserver.com/application/webservice?wsdl");

但这可能行不通,因为“/application/webservice”可能只有 serverexample.com 知道。在这种情况下,您将获得 TLS 连接,然后获得 HTTP 404 响应代码。

如果这不起作用,您将不得不求助于各种 SSL 工厂(虽然冗长但确实可行)并尝试将它们挂接到 javax.ws... 类(这应该是可能的,但我从来没有做过那部分。