异常: javax.net.ssl.SSLPeerUnverifiedException:对等体未经身份验证

61
public HttpClientVM() {

    BasicHttpParams params = new BasicHttpParams();
    ConnManagerParams.setMaxTotalConnections(params, 10);
    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
    HttpProtocolParams.setUseExpectContinue(params, false);
    HttpConnectionParams.setStaleCheckingEnabled(params, true);
    HttpConnectionParams.setConnectionTimeout(params, 30000);
    HostnameVerifier hostnameVerifier=
          org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
    HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
    SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
    socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
    SchemeRegistry schemeRegistry = new SchemeRegistry();
    schemeRegistry.register(new Scheme("http",socketFactory, 80));
    schemeRegistry.register(new Scheme("https",socketFactory, 443));
        ThreadSafeClientConnManager manager = new ThreadSafeClientConnManager(params, schemeRegistry);
        // Set verifier     
        client = new DefaultHttpClient(manager, params);    
    }

问题:

执行client.accessURL(url)时,出现以下错误:

Exception in thread "main" 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.connectSocket(SSLSocketFactory.java:397)
    at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:495)
    at org.apache.http.conn.scheme.SchemeSocketFactoryAdaptor.connectSocket(SchemeSocketFactoryAdaptor.java:62)
    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:148)
    at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:150)
    at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:121)
    at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:575)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:425)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:754)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:732)

附加信息:

  • Windows 7
  • HttpClient 4
12个回答

33

我们的"javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated"异常是由证书过期引起的。

keytool -list -v -keystore filetruststore.ts

    Enter keystore password:
    Keystore type: JKS
    Keystore provider: SUN
    Your keystore contains 1 entry
    Alias name: somealias
    Creation date: Jul 26, 2012
    Entry type: PrivateKeyEntry
    Certificate chain length: 1
    Certificate[1]:
    Owner: CN=Unknown, OU=SomeOU, O="Some Company, Inc.", L=SomeCity, ST=GA, C=US
    Issuer: CN=Unknown, OU=SomeOU, O=Some Company, Inc.", L=SomeCity, ST=GA, C=US
    Serial number: 5011a47b
    Valid from: Thu Jul 26 16:11:39 EDT 2012 until: Wed Oct 24 16:11:39 EDT 2012

3
抱歉,这可能是一个“愚蠢”的问题,但过期的证书是在服务器端还是客户端? - isapir
1
@Igal 如果我记得正确的话,是在服务器端。 - buzz3791

16

这个错误是因为你的服务器没有有效的SSL证书。因此,我们需要告诉客户端使用不同的TrustManager。以下是一个示例代码:

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,SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager ccm = base.getConnectionManager();
SchemeRegistry sr = ccm.getSchemeRegistry();
sr.register(new Scheme("https", 443, ssf));

client = new DefaultHttpClient(ccm, base.getParams());

10
去掉这一端的安全性能怎么可能解决另一端的问题?那个TrustManager代码甚至不符合规范。 - user207421
我不明白你的观点。问题来自客户端,因此我们只需要更改它。哪一行TrustManager代码不符合规范? - bnguyen82
8
getAcceptedIssuers()被规定永远不会返回null。你发布的代码极其不安全。它没有解决任何问题,只是创造了另一个问题,例如容易遭受中间人攻击的漏洞。 - user207421
4
这份代码适合测试,但绝对不适合生产使用。 - yegor256
我不得不给这个点踩,因为它完全取消了使用双向认证的意义。你还不如直接使用普通的HTTP(反正实现起来也没那么麻烦)。 - Jas
1
谢谢您。在我们解决证书细节的同时需要进行一些测试,这有助于推进开发进程。 - DocWatson

16

如果您的服务器基于JDK 7,而客户端使用SSL证书且基于JDK 6,则会出现此异常。在JDK 7中默认禁用了sslv2hello消息握手,而在JDK 6中启用了sslv2hello消息握手。因此,当客户端尝试连接服务器时,将向服务器发送一个sslv2hello消息,由于禁用了sslv2hello消息,您将收到此异常。要解决此问题,您可以将客户端迁移到JDK 7或使用JDK 6u91版本。但是要获取此版本的JDK,您必须获得MOS(My Oracle Support)企业支持。此补丁不公开。


在Java 1.7中有相同的问题,当我在1.8中编译并运行它时,在服务器上它可以正常工作。 - Lucky

6
如果客户端指定了“https”,但服务器仅运行“http”,则会出现这种情况。因此,服务器不希望建立安全连接。

3

如果您试图通过HTTPS连接,而服务器没有正确配置处理SSL连接,则也可能会发生这种情况。

我建议检查一下应用服务器的SSL设置,并确保证书被正确配置。


10
有什么配置可能出错?SSL证书在浏览器中能够正常工作,但为什么在Java中无法工作?为什么Java如此特殊? - Robert Reiz

2

1
在我的情况下,我使用的是JDK 8客户端,而服务器使用的是不安全的旧密码。服务器是Apache,我将以下行添加到Apache配置中:
SSLCipherSuite ALL:!ADH:RC4+RSA:+HIGH:!MEDIUM:!LOW:!SSLv2:!EXPORT

您应该使用此类工具来验证您的SSL配置目前是否安全: https://www.ssllabs.com/ssltest/analyze.html


这个修复了你的错误吗?你是否在属性文件中添加了SSLCipherSuite属性?请详细说明。 - Tiny
我怀疑我没有对客户端做出任何更改。我认为我只是在服务器上更改了密码,这就解决了问题。 - Sarel Botha

1

0

Spring应用程序和“peer not authenticated”错误

如果您在连接到Spring应用程序时遇到此错误,请检查安全配置(根据您使用的方法,如@EnableWebSecurityWebSecurityConfigurerAdapter等)。如果您保护了应用程序的端点,请确保将目标端点添加到排除列表(并且不要求进行身份验证),或者在尝试使用应用程序中设置的策略进行连接时确保适当的身份验证。

在我的情况下,我正在尝试使用支持TLS的WebSocket连接到应用程序。我在安全配置中有以下代码片段(这是Kotlin代码,但您可以理解其中的思路):

@Bean
fun filterChain(http: HttpSecurity): SecurityFilterChain {
    http.csrf {
        it.disable()
    }.authorizeHttpRequests {
        it
            .requestMatchers(PUT, "/tasks/**").hasAuthority("<scope1>")
            .requestMatchers(POST, "/tasks").hasAuthority("<scope2>")
            .requestMatchers(GET, "/tasks").hasAuthority("<scope3>")
            .requestMatchers(GET, "/tasks/**").hasAuthority"<scope4>")
    }.oauth2ResourceServer {
        it.jwt(withDefaults())
    }

    return http.build()
}

根据这个配置,所有传入的请求都应该由经过身份验证的用户处理。当我尝试使用Postman连接到wss://<host>:<port>/websocket时,我遇到了javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated错误,因为我没有在Postman中设置身份验证。
我通过在authorizeHttpRequests部分添加一行来调整配置:
authorizeHttpRequests {
   it
     .requestMatchers(PUT, "/tasks/**").hasAuthority("<scope1>")
     .requestMatchers(POST, "/tasks").hasAuthority("<scope2>")
     .requestMatchers(GET, "/tasks").hasAuthority("<scope3>")
     .requestMatchers(GET, "/tasks/**").hasAuthority"<scope4>")
     .requestMatchers("/websocket").permitAll() // new setting
}

使用新的设置,我可以在没有身份验证的情况下向/websocket发送请求。
第二个选项是在我尝试使用配置的第一个版本连接到WebSocket时,在Postman中设置身份验证。

0

这是您在系统上创建的证书问题。在创建证书[密钥库]时,您需要指定IP地址。请参考以下命令。

keytool -genkeypair -alias baeldung -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore baeldung.p12 -validity 36500 -ext "SAN:c=DNS:<localhost/Server DNSName>,IP:"


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接