Search code examples
androidssl-certificateretrofit2

android retrofit Hostname not verified


I got certificate issued for IP address (Not a common name) and I'm triyng to connect to the server with that certificate.

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);

and I got an error:

Hostname 11.8.222.333 not verified:

but when I use

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

then everything works.

How to solve that error without turning off hostname verifier?

P.S. My certificate issued for IP (11.8.222.333)


Solution

  • I redefined verify method like that ( just copied sources from DefaultHostnameVerifier.java ) and everything works now. I don't know why it didn't work but now it's fine.

    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;
        }