SSLHandshakeException:不存在主题替代名称

Posted

技术标签:

【中文标题】SSLHandshakeException:不存在主题替代名称【英文标题】:SSLHandshakeException: No subject alternative names present 【发布时间】:2012-05-02 17:45:23 【问题描述】:

我正在通过 java 代码调用 HTTPS SOAP Web 服务。我已经在 jre cacerts 密钥库中导入了自签名证书。现在我得到了:

com.sun.xml.internal.ws.com.client.ClientTransportException: HTTP transport error: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present

服务 URL 的主机名与证书中提供的 CN 的主机名不匹配。我读到了定义自定义主机名验证器here 的解决方法。但我无法确定我应该在我的代码中实现解决方法的位置。

public SOAPMessage invokeWS(WSBean bean) throws Exception 

    SOAPMessage response=null;
    try

    /** Create a service and add at least one port to it. **/
    String targetNameSpace = bean.getTargetNameSpace();
    String endpointUrl = bean.getEndpointUrl();
    QName serviceName = new QName(targetNameSpace, bean.getServiceName());
    QName portName = new QName(targetNameSpace, bean.getPortName());
    String SOAPAction = bean.getSOAPAction();
    HashMap<String, String> map = bean.getParameters();


    Service service = Service.create(serviceName);
    service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING, endpointUrl);

    /** Create a Dispatch instance from a service. **/
    Dispatch dispatch = service.createDispatch(portName, SOAPMessage.class,
            Service.Mode.MESSAGE);

    // The soapActionUri is set here. otherwise we get a error on .net based
    // services.
    dispatch.getRequestContext().put(Dispatch.SOAPACTION_USE_PROPERTY,
            new Boolean(true));
    dispatch.getRequestContext().put(Dispatch.SOAPACTION_URI_PROPERTY,
            SOAPAction);

    /** Create SOAPMessage request. **/
    // compose a request message
    MessageFactory messageFactory = MessageFactory.newInstance();
    SOAPMessage message = messageFactory.createMessage();

    // Create objects for the message parts
    SOAPPart soapPart = message.getSOAPPart();
    SOAPEnvelope envelope = soapPart.getEnvelope();
    SOAPBody body = envelope.getBody();

    SOAPElement bodyElement = body.addChildElement(bean.getInputMethod(),
            bean.getPrefix(), bean.getTargetNameSpace());

             ...more code to form soap body goes here

    // Print request
    message.writeTo(System.out);

    // Save the message
    message.saveChanges();

    response = (SOAPMessage)dispatch.invoke(message);
    
    catch (Exception e) 
        log.error("Error in invokeSiebelWS :"+e);
    
    return response;

请忽略 WSBean 参数,因为命名空间和其他 wsdl 属性都来自该 bean。如果这个异常可以通过一些不同的解决方法解决,请提出建议。

【问题讨论】:

您的服务 URI 是使用 IP 地址还是主机名? 【参考方案1】:

谢谢,Bruno 提醒我常用名称和主题备用名称。我们发现证书是使用 CN 生成的,带有网络的 DNS 名称,并要求使用主题备用名称条目重新生成新证书,即 san=ip:10.0.0.1。这是实际解决方案

但是,我们设法找到了一个解决方法,我们可以使用它在开发阶段运行。只需在我们建立 ssl 连接的类中添加一个静态块。

static 
    HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier()
        
            public boolean verify(String hostname, SSLSession session)
            
                // ip address of the service URL(like.23.28.244.244)
                if (hostname.equals("23.28.244.244"))
                    return true;
                return false;
            
        );

如果您碰巧使用的是 Java 8,有一种更巧妙的方法可以达到同样的效果:

static 
    HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> hostname.equals("127.0.0.1"));

【讨论】:

您可以使用CN=localhost 生成证书。 好的。 MITM 可以生成具有该 IP 地址的证书仍然存在轻微风险(因为您没有验证它是否与您所追求的证书匹配)。话虽如此,适当的 CA 不太可能向 IP 地址颁发证书。 实际上,这是一个内部组织开发的案例,服务器人员不想去获得 CA 批准的证书,也不想痛苦(讽刺)重新生成证书。所以,我们必须在客户端代码中进行这个小调整。 我们正在使用 Play 框架。我们的请求是在控制器中提出的。我们只需将您的代码放在控制器中(更改 IP)就可以了! 这就像魔术一样,使用它允许具有转发端口的本地主机通过远程机器从 Groovy 类连接到服务器。谢谢。【参考方案2】:

与某些浏览器不同,Java 在服务器身份验证(RFC 2818,第 3.1 节)和 IP 地址方面严格遵循 HTTPS 规范。

使用主机名时,可以回退到服务器证书的使用者 DN 中的公用名,而不是使用使用者备用名称。

当使用 IP 地址时,证书中必须有一个主题备用名称条目(类型为 IP 地址,而不是 DNS 名称)。

您将在this answer 中找到有关规范以及如何生成此类证书的更多详细信息。

【讨论】:

thnks,我明白这是因为服务器生成的自签名证书使用的 DNS 名称与我正在访问的 ip 不匹配。但是我对此无能为力,因为它不在我的控制范围内。我需要一个解决方法,它可能在错误的运行中很糟糕,但至少可以暂时解决我的问题。 如果证书中的主机名没有解析到地址,则服务器配置错误。不过,您应该能够更改您的本地 DNS 解析,以将该名称指向您的 /etc/hosts 文件中的此 IP 地址(如果在 Linux 或 Windows 等效项下)。

以上是关于SSLHandshakeException:不存在主题替代名称的主要内容,如果未能解决你的问题,请参考以下文章

处理异常:javax.net.ssl.SSLHandshakeException:没有合适的协议(协议被禁用或密码套件不合适)

javax.net.ssl.SSLHandshakeException 在一台机器上,但不是在另一台机器上

Intellij 的想法。 javax.net.ssl.SSLHandshakeException:没有合适的协议(协议被禁用或密码套件不合适)

为单个连接禁用 SSLHandshakeException

Android 4.4 及更低版本上的 SSLHandshakeException

通过 https 发送 POST 请求时出现 SSLHandshakeException