如何修复 X509TrustManager 在 Android 应用程序中的不安全实现



Google建议我的Android应用程序中有一个不安全的接口X509TrustManager实现,需要更改我的代码,如下所示:

要正确处理 SSL 证书验证,请在 检查服务器自定义 X509TrustManager 接口的受信任方法 每当何时引发 CertificateException 或 IllegalArgumentException 服务器提供的证书不符合您的要求 期望值。对于技术问题,您可以发布到 堆栈溢出 并使用标签"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);
}

我已经使用以下代码解决了这个问题:

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

如果您从正在使用的外部库中遇到此问题,请检查 appache libraray 是否是导致它的原因。

对我来说,apache库导致了错误:我正在使用已弃用的类 - 多部分实体。此类使用 SSLContextBuilder它使用 TrustManagerDelegate。TrustManagerDelegate实现了X509TrustManager,在将应用程序上传到Google Play商店时会导致"TrustManager的不安全实现"错误。

解决方案是:不要弃用MultipartEntity类,而是使用MultipartEntityBuilder。

例如:

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

将替换为:

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

添加 OKttps 的升级版本为我在 Android 10 中崩溃

implementation 'com.squareup.okhttp3:okhttp:4.8.0'

我遇到了这个问题。如果你的代码是这样的:

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

它将接受所有证书,这是一个坏主意,所以谷歌向您发送邮件。我们也可以进行更改以接受自签名证书。我解决了,这是我的问题和我的解决方案

如果您使用的是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();

在任何网络调用之前。

最新更新