我获得了为IP地址颁发的证书(不是通用名称),并且我已经使用该证书连接到服务器。
OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
OkHttpClient okHttpClient = builder.build();
Gson gson = new GsonBuilder()
.setLenient()
.create();
retrofit = new Retrofit.Builder()
.baseUrl(url)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
ServerRouts service = retrofit.create(ServerRouts.class);
Resp_json> call = service.login(param, user, pw);
我收到了一个错误:
Hostname 11.8.222.333 not verified:
但是当我使用
时builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
然后一切正常。
如何在不关闭主机名验证程序的情况下解决该错误?
P.S。我的IP证书(11.8.222.333)
答案 0 :(得分:2)
我重新定义了类似的验证方法(只是从DefaultHostnameVerifier.java复制了源代码),现在一切正常。我不知道为什么它不起作用,但现在没事。
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
Certificate[] certs;
try {
certs = session.getPeerCertificates();
} catch (SSLException e) {
return false;
}
X509Certificate x509 = (X509Certificate) certs[0];
// We can be case-insensitive when comparing the host we used to
// establish the socket to the hostname in the certificate.
String hostName = hostname.trim().toLowerCase(Locale.ENGLISH);
// Verify the first CN provided. Other CNs are ignored. Firefox, wget,
// curl, and Sun Java work this way.
String firstCn = getFirstCn(x509);
if (matches(hostName, firstCn)) {
return true;
}
for (String cn : getDNSSubjectAlts(x509)) {
if (matches(hostName, cn)) {
return true;
}
}
return false;
}
});
private String getFirstCn(X509Certificate cert) {
String subjectPrincipal = cert.getSubjectX500Principal().toString();
for (String token : subjectPrincipal.split(",")) {
int x = token.indexOf("CN=");
if (x >= 0) {
return token.substring(x + 3);
}
}
return null;
}
答案 1 :(得分:0)
HostNameVerifier
的默认OkHttpClient.Builder
是okhttp3.internal.tls.OkHostnameVerifier
。看来此验证程序与对等方证书中的通用名称不匹配主机名,而仅与主题备用名称匹配。此行为是设计使然,而非错误-请查看以下问题:https://github.com/square/okhttp/issues/4966
有两种解决方法:
如果要继续使用OkHostnameVerifier
,则应确保服务器证书中包含使用者备用名称。至少在SAN中重复通用名称。我发现该要点有助于这样做:https://gist.github.com/croxton/ebfb5f3ac143cd86542788f972434c96
如果无法控制服务器证书,则可以使用备用HostNameVerifier实现,该实现也与证书的CN匹配。 @Rainmaker的答案(https://stackoverflow.com/a/41543384/978164)中的一个将起作用。我还发现Apache org.apache.http.conn.ssl.DefaultHostnameVerifier
包中的httpclient
可以正常工作,并且可能是更彻底的实现。
// instantiate and configure your OkHttpClient.Builder and then:
builder.hostnameVerifier(new org.apache.http.conn.ssl.DefaultHostnameVerifier());