Как исправить небезопасную реализацию X509TrustManager в приложении для Android

Google сообщила, что у меня есть небезопасная реализация интерфейса X509TrustManager в приложении для Android и мне нужно изменить свой код следующим образом:

Чтобы правильно обрабатывать SSL-сертификат, измените код в checkServerTrusted метод вашего пользовательского интерфейса X509TrustManager для поднять либо CertificateException, либо IllegalArgumentException всякий раз сертификат, представленный сервером, не соответствует вашему ожидания. По техническим вопросам вы можете отправлять сообщения в Qaru и используйте теги "android-security" и "TrustManager".

Как можно изменить следующий код, чтобы исправить вышеупомянутую проблему?

public EasySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
    super(truststore);

    TrustManager tm = new X509TrustManager()  {
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    };

    mContext.init(null, new TrustManager[] { tm }, null);
}

Ответы

Ответ 1

Я решил это, используя следующий код:

public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                try {
                    chain[0].checkValidity();
                } catch (Exception e) {
                    throw new CertificateException("Certificate not valid or trusted.");
                }
            }

Ответ 2

Если вы столкнулись с этим из внешней библиотеки, которую вы используете, проверьте, является ли ее причиной личная библиотека приложений.

Для меня библиотека apache вызвала ошибку: я использовал устаревший класс - MultipartEntity. Этот класс использует SSLContextBuilder который использует TrustManagerDelegate. TrustManagerDelegate реализует X509TrustManager, что приводит к ошибке "небезопасной реализации TrustManager" при загрузке приложения в магазин воспроизведения Google.

Решение: вместо устаревшего MultipartEntity класса используйте MultipartEntityBuilder.

Например:

MultipartEntity httpMultipart = new MultipartEntity();
String contentType = httpMultipart.getContentType().getValue();

Будет заменено на:

MultipartEntityBuilder httpMultipart = new MultipartEntityBuilder();
String contentType = httpMultipart.build().getContentType().getValue();

Ответ 3

Я встречаюсь с этой проблемой. Если ваш код такой:

 TrustManager tm = new X509TrustManager()  {
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    }

    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    }

    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
};

он примет весь сертификат, и это плохая идея, поэтому Google отправит вам письмо. Мы также можем внести изменения, чтобы принять самозаверяющий сертификат. Я решил это, вот мой вопрос и мое решение

Ответ 4

Если вы используете HttpClient, то решение @Nabeel очень приятно, но если вы используете HttpsUrlConnection, то этот код очень хорош для этого

import android.util.Log;

import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

/**
 * TrustManager that accepts all certificates and hosts.
 * Useful when you want to use HTTPS but you have self-signed certificates.
 * Works with HttpsUrlConnection.
 * Use at your own risk and only for development.
 *
 * @author gotev (Aleksandar Gotev)
 */
public class AllCertificatesAndHostsTruster implements TrustManager, X509TrustManager {

    @Override
    public final void checkClientTrusted(final X509Certificate[] xcs, final String string)
            throws CertificateException {
    }

    @Override
    public final void checkServerTrusted(final X509Certificate[] xcs, final String string)
            throws CertificateException {
    }

    @Override
    public final X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
    }

    /**
     * Gets an {@link SSLContext} which trusts all certificates.
     * @return {@link SSLContext}
     */
    public static SSLContext getSSLContext() {
        final TrustManager[] trustAllCerts =
                new TrustManager[] {new AllCertificatesAndHostsTruster()};

        try {
            final SSLContext context = SSLContext.getInstance("SSL");
            context.init(null, trustAllCerts, new SecureRandom());
            return context;

        } catch (Exception exc) {
            Log.e("CertHostTruster", "Unable to initialize the Trust Manager to trust all the "
                    + "SSL certificates and HTTPS hosts.", exc);
            return null;
        }
    }

    /**
     * Creates an hostname verifier which accepts all hosts.
     * @return {@link HostnameVerifier}
     */
    public static HostnameVerifier getAllHostnamesVerifier() {
        return new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };
    }

    /**
     * Call this method once before all your network calls
     * to accept all the self-signed certificates in HTTPS connections.
     */
    public static void apply() {
        final TrustManager[] trustAllCerts =
                new TrustManager[] {new AllCertificatesAndHostsTruster()};

        try {
            final SSLContext context = SSLContext.getInstance("SSL");
            context.init(null, trustAllCerts, new SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
            HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {

                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });

        } catch (Exception exc) {
            Log.e("CertHostTruster", "Unable to initialize the Trust Manager to trust all the "
                    + "SSL certificates and HTTPS hosts.", exc);
        }
    }
}

Источник: https://gist.github.com/gotev/6784c1303793c6ee9e56

Затем, чтобы использовать самозаверяющие сертификаты, просто вызовите:

AllCertificatesAndHostsTruster.apply();

перед любыми сетевыми вызовами.