java 我得到 javax.net.ssl.SSLPeerUnverifiedException: peer not authentication on my JUnit test

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6991046/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 18:10:51  来源:igfitidea点击:

I got javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated on my JUnit test

javassljunit

提问by Cristian

I am creating some unit tests for an app that uses a REST API. When I try to send a HttpPost request to the server URL (https://some.server.com), I got this:

我正在为使用 REST API 的应用程序创建一些单元测试。当我尝试向服务器 URL (https://some.server.com) 发送 HttpPost 请求时,我得到了这个:

javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
    at com.sun.net.ssl.internal.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:352)
    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128)
    at org.apache.http.conn.ssl.SSLSocketFactory.createSocket(SSLSocketFactory.java:399)
    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:143)
    at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:149)
    at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:108)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:415)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:641)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:576)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:554)

The server has a verified HTTPS certificate. Also, when I run this on production, it works fine. So I think it has to do with JUnit and/or my local computer.

服务器具有经过验证的 HTTPS 证书。此外,当我在生产中运行它时,它工作正常。所以我认为这与 JUnit 和/或我的本地计算机有关。

I am also using a HttpClient created with this:

我还使用了一个用这个创建的 HttpClient:

public static class WebClientDevWrapper {
    public static HttpClient wrapClient(HttpClient base) {
        try {
            SSLContext ctx = SSLContext.getInstance("TLS");
            X509TrustManager tm = new X509TrustManager() {

                public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
                }

                public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
                }

                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };
            ctx.init(null, new TrustManager[]{tm}, null);
            SSLSocketFactory ssf = new SSLSocketFactory(ctx);
            ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            ClientConnectionManager ccm = base.getConnectionManager();
            SchemeRegistry sr = ccm.getSchemeRegistry();
            sr.register(new Scheme("https", ssf, 443));
            return new DefaultHttpClient(ccm, base.getParams());
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }
}

AFAIK, if I create a HTTPClient with the code above, it should avoid SSLPeerUnverifiedException, but it seems it is not working.

AFAIK,如果我用上面的代码创建一个 HTTPClient,它应该避免SSLPeerUnverifiedException,但它似乎不起作用。

So how can I solve this problem? I have tried more ways to create HttpClients which don't complain about SSL certs, but nothing has worked so far.

那么我该如何解决这个问题呢?我尝试了更多创建 HttpClients 的方法,这些方法不会抱怨 SSL 证书,但到目前为止没有任何效果。

回答by Ryan Stewart

Do you happen to be using Linux? I've had crazy problems using Java and SSL on an Ubuntu system. It's related to the /etc/hosts file mapping localhost to 127.0.1.1 instead of 127.0.0.1. Both are equally valid according to IANA mappings, but Java seems to wig out if it's not 127.0.0.1. When the problem manifests, it rarely even makes it past the handshake, so even if you've completely disabled all cert checking, it will still fail in unexpected ways.

你碰巧在使用 Linux 吗?我在 Ubuntu 系统上使用 Java 和 SSL 时遇到了疯狂的问题。它与将 localhost 映射到 127.0.1.1 而不是 127.0.0.1 的 /etc/hosts 文件有关。根据 IANA 映射,两者同样有效,但如果不是 127.0.0.1,Java 似乎会失效。当问题出现时,它甚至很少能通过握手,因此即使您完全禁用了所有证书检查,它仍然会以意想不到的方式失败。

回答by Ryan Stewart

Most responses to this question (both on StackOverflow and elsewhere) spend too much time focusing on the Java code itself, and the answer often given is to just bypass SSL verification for all hosts. Please only do this as an absolute last resort, even if it is only a development environment, and only do it per domain as necessary instead of across the board. It is a poor practice, and I wonder how often that "development environment only" code makes it into production.

大多数对这个问题的回答(在 StackOverflow 和其他地方)花费了太多时间关注 Java 代码本身,而通常给出的答案是绕过所有主机的 SSL 验证。请仅作为绝对的最后手段执行此操作,即使它只是一个开发环境,并且仅在必要时按域执行此操作,而不是全面执行此操作。这是一种糟糕的做法,我想知道“仅开发环境”代码多久将其投入生产。

The reality is that the error is entirely accurate, and in some situations there is actually a better alternative. You will only get this error when:

现实情况是,该错误是完全准确的,在某些情况下,实际上还有更好的选择。您只会在以下情况下收到此错误:

  • There is no SSL certificate installed on the host to which you want to connect.
  • The certificate is installed, but not valid. This could be because it is expired, or the CN does not match the host domain where the certificate is installed.
  • The certificate is valid, but the certificate authority (CA) is not recognized by your Java JDK implementation. If one authority in the "chain of trust" for the certificate is not recognized by your Java JDK implementation then you might instead receive the javax.net.ssl.SSLHandshakeException.
  • The certificate is self-signed, which is really the same as above, in that the self signer is not a recognized CA.
  • 您要连接的主机上没有安装 SSL 证书。
  • 证书已安装,但无效。这可能是因为它已过期,或者 CN 与安装证书的主机域不匹配。
  • 证书有效,但您的 Java JDK 实现无法识别证书颁发机构 (CA)。如果您的 Java JDK 实现无法识别证书“信任链”中的一个机构,那么您可能会收到 javax.net.ssl.SSLHandshakeException。
  • 证书是自签名的,实际上与上面相同,因为自签名者不是公认的 CA。

The first bullet point has its own set of issues. It's questionable why you would want to connect to a host in any production scenario when no certificate is installed, but the application is still serving traffic over HTTPs. If you're sending sensitive user data over that connection then you've created a serious security flaw. The only solution for this bullet point is to bypass verification, which is really no solution at all unless you're simply "crawling" or "scraping" content.

第一个要点有其自身的一系列问题。在没有安装证书的情况下,为什么要在任何生产场景中连接到主机,但该应用程序仍在通过 HTTPs 提供流量服务,这是有问题的。如果您通过该连接发送敏感的用户数据,那么您就造成了严重的安全漏洞。此要点的唯一解决方案是绕过验证,这实际上根本不是解决方案,除非您只是“爬行”或“抓取”内容。

Unfortunately, the same goes for the second bullet point as well. The only solution if you have to connect to the host over SSL is to bypass SSL verification. Even adding the certificate to your truststore will not prevent this exception when the certificate is expired or was created with an invalid CN. If you have an ongoing relationship with the host then ask them if they can correct the problem.

不幸的是,第二个要点也是如此。如果您必须通过 SSL 连接到主机,唯一的解决方案是绕过 SSL 验证。当证书过期或使用无效 CN 创建时,即使将证书添加到您的信任库也不会阻止此异常。如果您与房东有持续的关系,请询问他们是否可以解决问题。

The solution for the third and fourth bullet point depends on the level of trust you have established with the hosts to which you want to connect, but is much better than simply bypassing all SSL host verification. Simply add the certificate as a trusted certificate (authority).

第三和第四点的解决方案取决于您与要连接的主机建立的信任级别,但比简单地绕过所有 SSL 主机验证要好得多。只需将证书添加为受信任的证书(授权)。

First, download the certificate using your favorite browser. Both Firefox and Chrome provide utilities to download a certificate for a domain. This question and answers from SuperUser provide some details. In the Java JDK the CA truststore is located at [JAVA_HOME]/jre/lib/security/cacerts. Use the following keytool command to add to it the certificate downloaded:

首先,使用您喜欢的浏览器下载证书。Firefox 和 Chrome 都提供实用程序来下载域的证书。SuperUser 的这个问题和答案提供了一些细节。在 Java JDK 中,CA 信任库位于 [JAVA_HOME]/jre/lib/security/cacerts。使用以下 keytool 命令将下载的证书添加到其中:

keytool -import -trustcacerts -alias domainalias -file host.crt -keystore cacerts

For more information, and other keytool commands I like to use the following page:

有关更多信息和其他 keytool 命令,我喜欢使用以下页面:

The Most Common Java Keytool Keystore Commands

最常见的 Java Keytool Keystore 命令

回答by Karlo Smid

Here is solution for my context.

这是我的上下文的解决方案。

I worked on Windows 7, in corporate environment, which means that I had proxy. SOAP url was in testing environment which was not seen by that corporate proxy.

我在公司环境中使用 Windows 7,这意味着我有代理。SOAP url 处于该公司代理未看到的测试环境中。

Be sure that in File->Preferences you set Proxy settings to None.

确保在 File->Preferences 中将 Proxy settings 设置为 None。

Regards, Karlo.

问候,卡罗。

回答by user207421

if I create a HTTPClient with the code above, it should avoid SSLPeerUnverifiedException

如果我用上面的代码创建一个 HTTPClient,它应该避免 SSLPeerUnverifiedException

Not at all. All that insecure X509TrustManager does is accept any peer certificate whether trustworthy or not, which is entirely pointless. What you are seeing is that the peer hasn't sent a certificate at all.

一点也不。不安全的 X509TrustManager 所做的只是接受任何可信任的对等证书,这完全没有意义。您看到的是对等方根本没有发送证书