Search code examples
androidsslbraintreeandroid-securityhostname-verifier

Unsafe implementation of the HostnameVerifier interface - Android


Reason for app rejection on Play Store:

Your app(s) are using an unsafe implementation of the HostnameVerifier interface. You can find more information about how to resolve the issue in this Google Help Center article.

Hello All,

I'm getting a HostnameVerifier issue by google play console when I upload the app to the play store. I've tried each and every solution that I've found on StackOverflow, but still, the issue is the same i.e Your app(s) are using an unsafe implementation of the HostnameVerifier interface.

Also, I've gone through the google documentation for this issue but didn't get any luck. Does anyone have a solution regarding this? Every help is appreciated

Below is my ServiceGenerator class

public class ServiceGenerator {

    private static final String KEY_AUTH_HEADER = "Authorization";
    private Context context;
    private Retrofit.Builder builder;
    private OkHttpClient.Builder httpClient;
    HandshakeCertificates certificates;

    ServiceGenerator(Context context) {
        this.context = context;
        final String dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'";

        httpClient = new OkHttpClient.Builder();

        certificates = new HandshakeCertificates.Builder()
                .addTrustedCertificate(AppConstants.SSL_CERTIFICATE_DEMO)
                .addTrustedCertificate(AppConstants.SSL_CERTIFICATE_LIVE)
                // Uncomment if standard certificates are also required.
                .addPlatformTrustedCertificates()
                .build();

        // Install the all-trusting trust manager
        final SSLContext sslContext;
        try {
            sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, new X509TrustManager[]{certificates.trustManager()}, new java.security.SecureRandom());

            final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
            httpClient.sslSocketFactory(sslSocketFactory, certificates.trustManager());
            httpClient.hostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
        }
       
        httpClient.connectTimeout(60, TimeUnit.SECONDS);
        httpClient.readTimeout(60, TimeUnit.SECONDS);
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();

        int cacheSize = 20 * 1024 * 1024; // 20 MB
        Cache cache = new Cache(context.getCacheDir(), cacheSize);

        logging.level(HttpLoggingInterceptor.Level.BASIC);

        httpClient.cache(cache);
        httpClient.addNetworkInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Response originalResponse = chain.proceed(chain.request());
                if (Functions.isConnected(context)) {
                    int maxAge = 60 * 2; // read from cache for 2 minute
                    return originalResponse.newBuilder()
                            .header("Cache-Control", "public, max-age=" + maxAge)
                            .build();
                } else {
                    int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
                    return originalResponse.newBuilder()
                            .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                            .build();
                }
            }
        });

        httpClient.addInterceptor(logging);
        httpClient.addInterceptor(new HeaderInterceptor());

        Gson gson = new GsonBuilder()
                .setDateFormat(dateFormat)
                .create();
        builder = new Retrofit.Builder()
                .baseUrl(Apis.HOST);

        builder.addConverterFactory(GsonConverterFactory.create(gson));
    }

    class HeaderInterceptor implements Interceptor {

        @Override
        public Response intercept(Chain chain) throws IOException {

            String authKey = "authKey";
            if (PrefUtils.isUserLoggedIn(context) && PrefUtils.getUserFullProfileDetails(context) != null) {
                authKey = PrefUtils.getUserFullProfileDetails(context).getAuthKey();
            }

            Request newRequest = chain.request().newBuilder()
                    .addHeader("auth-key", authKey)
                    .build();
            return chain.proceed(newRequest);
        }
    }

    public <S> S createService(Class<S> serviceClass) {
        Retrofit retrofit = builder.client(httpClient.build()).build();
        return retrofit.create(serviceClass);
    }
}

Solution

  • Actually, this issue was due to Braintree SDK which was using HostnameVerifier. I've gone through this GitHub issue channel for Braintree and came to know that they had resolved that issue and I just need to update the SDK version and upload the bundle to the Play Store. This resolved my issue and was able to upload my app to the Play Store.

    Whosoever is still looking for a better solution and isn't able to find it anywhere, then kindly go through this link and submit your issue details there, also Please ask them to mention the name of the file which is causing this issue. They will revert you in the mail with the name of the file.