OkHttp javax.net.ssl.SSLPeerUnverifiedException:主机名domain.com未经过验证

时间:2015-08-10 11:09:05

标签: java android ssl okhttp

我一直在努力让这个工作。我正在尝试使用自签名证书通过 https 连接到我的服务器。我不认为现在还没有任何页面或示例。

我做了什么:

  1. 按照本教程创建bks密钥库:http://blog.crazybob.org/2010/02/android-trusting-ssl-certificates.html
  2. 它使用openssl s_client -connect domain.com:443从服务器获取证书。然后使用充气城堡创建一个bks密钥库。

    1. 从原始文件夹中读取创建的密钥库,将其添加到sslfactory,然后再添加到OkHttpClient。像这样:

      public ApiService() {
          mClient = new OkHttpClient();
          mClient.setConnectTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS);
          mClient.setReadTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS);
          mClient.setCache(getCache());
          mClient.setCertificatePinner(getPinnedCerts());
          mClient.setSslSocketFactory(getSSL());
      }
      
      protected SSLSocketFactory getSSL() {
          try {
              KeyStore trusted = KeyStore.getInstance("BKS");
              InputStream in = Beadict.getAppContext().getResources().openRawResource(R.raw.mytruststore);
              trusted.load(in, "pwd".toCharArray());
              SSLContext sslContext = SSLContext.getInstance("TLS");
              TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
              trustManagerFactory.init(trusted);
              sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
              return sslContext.getSocketFactory();
          } catch(Exception e) {
              e.printStackTrace();
          }
          return null;
      }
      
      public CertificatePinner getPinnedCerts() {
          return new CertificatePinner.Builder()
                  .add("domain.com", "sha1/theSha=")
                  .build();
      }
      
    2. 由于某种原因,这始终会生成带有或不带密钥库的SSLPeerUnverifiedException。有或没有CertificatePinner

      javax.net.ssl.SSLPeerUnverifiedException: Hostname domain.com not verified: 0         
       W/System.err﹕ certificate: sha1/theSha=
       W/System.err﹕ DN: 1.2.840.113549.1.9.1=#1610696e666f40626561646963742e636f6d,CN=http://domain.com,OU=development,O=domain,L=Valencia,ST=Valencia,C=ES
       W/System.err﹕ subjectAltNames: []
       W/System.err﹕ at com.squareup.okhttp.internal.http.SocketConnector.connectTls(SocketConnector.java:124)
       W/System.err﹕ at com.squareup.okhttp.Connection.connect(Connection.java:143)
       W/System.err﹕ at com.squareup.okhttp.Connection.connectAndSetOwner(Connection.java:185)
       W/System.err﹕ at com.squareup.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:128)
       W/System.err﹕ at com.squareup.okhttp.internal.http.HttpEngine.nextConnection(HttpEngine.java:341)
       W/System.err﹕ at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:330)
       W/System.err﹕ at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:248)
       W/System.err﹕ at com.squareup.okhttp.Call.getResponse(Call.java:273)
       W/System.err﹕ at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:230)
       W/System.err﹕ at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:201)
       W/System.err﹕ at com.squareup.okhttp.Call.execute(Call.java:81)
       ...
      
    3. 我做错了什么?

5 个答案:

答案 0 :(得分:21)

我遇到了同样的问题,但我需要我的应用程序才能在多个临时环境中工作,所有这些环境都有自签名证书。更糟糕的是,他们可以随时更改这些证书。

要解决此问题,仅在连接到分段时,我添加了一个信任所有证书的SSLSocketFactory。这修复了java错误,但它给我留下了此票证中提到的okhttp异常。

为了避免这个错误,我需要再向okHttpClient添加一个自定义。这为我解决了错误。

okHttpClient.setHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        });

答案 1 :(得分:10)

setHostNameVerifier设置为okHttpBuilder即可解决此问题。确保验证方法应该返回true。

样品:

okHttpClient.setHostnameVerifier(new HostnameVerifier() {
    @Override
    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
});

OkHttpClient.Builder builder = new OkHttpClient.Builder();
    builder.hostnameVerifier(new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    });
OkHttpClient client = builder.build();

答案 2 :(得分:2)

请检查客户端证书上的CN名称是否已添加到“主题”备用名称中。我有同样的问题

答案 3 :(得分:1)

在生成证书的过程中,如果uri是一个IP,则必须设置subjectAltName才能通过验证。

“在某些情况下,URI被指定为IP地址而不是主机名。在这种情况下,iPAddress subjectAltName必须存在于证书中,并且必须与URI中的IP完全匹配。” RFC(由巴斯在评论中提及)

不要通过HostnameVerifier来烦恼客户端,而是通过以下方式重新使用自签名证书(我们已对其进行控制):

openssl req \
-newkey rsa:2048 \
-nodes \
-x509 \
-days 36500 -nodes \
-addext "subjectAltName = IP.1:1.2.3.4" \
-keyout /etc/ssl/private/nginx-selfsigned2.key \
-out /etc/ssl/certs/nginx-selfsigned2.crt

插件,如果在Android上也需要信任证书:

the crt is pem format and can be imported into android via
<?xml version="1.0" encoding="utf-8"?>
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="@raw/nginx_selfsigned2" />
            <certificates src="system" />
        </trust-anchors>
    </base-config>
</network-security-config>

因此,我们验证证书来自受信任的源,并且以前通过主机名验证(通过SAN)来确保与之通信的服务器为他的ip提供正确的证书。

更多信息:https://developer.android.com/training/articles/security-config https://developer.android.com/training/articles/security-ssl.html#SelfSigned

答案 4 :(得分:0)

如果您在 body { background-color:#2a303d; text-align: center; font-family: 'Ubuntu', sans-serif; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; -o-user-select: none; user-select: none; } #rock { width: 80%; position: absolute; top: 45.8%; left: 39.75%; transform: translate(-50%, -50%); transform: rotate(360deg); height: 100px; width: 100px } #paper { width: 80%; position: absolute; top: 45.8%; left: 46.75%; transform: translate(-50%, -50%); transform: rotate(360deg); height: 100px; width: 100px; } #scissors { width: 80%; position: absolute; top: 52.05%; left: 57.25%; transform: translate(-50%, -50%); height: 100px; width: 100px; } #RPS { font-size: 65px; margin-top: 40px; } #botPick { font-size: 50px; margin-top: 350px; } #Score p { margin:0; padding:0; margin-left: 5px; font-size: 36px; display: inline-block; line-height: 80px; vertical-align: top; } #winOrLose { color: white; } 中使用 network_security_config 解决res/xml 并且您更改了域/IP 地址,请记住您应该将 Cleartext HTTP traffic not permitted 值更改为新地址。 这对我有用。

includeSubdomains