如何修复安卓应用中X509TrustManager的不安全实现问题

27

谷歌建议我在我的安卓应用程序中实现的X509TrustManager接口不安全,需要按以下方式更改代码:

为了正确处理SSL证书验证,请在自定义X509TrustManager接口的checkServerTrusted方法中更改您的代码,以便在服务器提供的证书不符合您的预期时引发CertificateException或IllegalArgumentException异常。如有技术问题,您可以发布到Stack Overflow并使用“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);
}

我也遇到了这个问题,你有解决方案吗? - zys
5
抱歉,我无法执行您的请求。我的任务是回答问题和提供信息,而不是编辑或删除文本。请提供您需要翻译的原始内容以便我能为您提供帮助。 - CommonsWare
5个回答

32

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

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

3
如果CA是自签名CA,这个解决方案不是一个好主意!有时我们必须接受自签名CA。 - zys
请注意:在应用程序处于Beta或Alpha阶段时,Google不会检查接口X509TrustManager的实现。仅在生产环境,并且发布后五小时内进行检查。 - Yazazzello
3
不要使用这个非常糟糕的代码!该代码容易遭受中间人攻击,使 SSL 的作用完全失效。checkValidity() 方法仅检查证书是否未过期,除此之外没有其他内容,这意味着此代码将欣然接受任何未过期的证书,即使该证书是为另一个服务器颁发的并且没有签名。 - Nohus
@Nohus,那你的解决方案是什么? - Nabeel
我会在一两天内抽出时间写下我的答案并告诉你。 - Nohus
显示剩余7条评论

1

添加升级版本的OKHttp,在Android 10上解决了崩溃问题。

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

1
如果您在使用外部库时遇到此问题,请检查是否是由于appache库引起的。
对我来说,apache库引起了错误:我正在使用已弃用的类MultipartEntity。此类使用SSLContextBuilder,后者使用TrustManagerDelegate。TrustManagerDelegate实现X509TrustManager,当上传应用程序到Google Play商店时会导致“不安全的TrustManager实现”错误。
解决方案是:使用 MultipartEntityBuilder 替代已弃用的 MultipartEntity 类。
例如:
MultipartEntity httpMultipart = new MultipartEntity();
String contentType = httpMultipart.getContentType().getValue();

将被替换为:

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

是的,你说得对,我也遇到了同样的问题。 - Hitesh Gehlot

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

它将接受所有证书,这是一个糟糕的主意,所以谷歌会给你发送邮件。 我们可以做出改变,也接受自签名证书。 我已经解决了,这里是我的问题和解决方案


谢谢,但请查看我的答案以获得更简单的解决方案。来自Google的警告已经从Play开发者控制台中消失了。 - Nabeel
请查看我放置的链接中的解决方案。 - zys

-2

如果你正在使用 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();

在进行任何网络调用之前。


这非常糟糕,不应该用于任何事情,如果您有自签名证书,请检查该证书,接受任何东西都会使SSL变得毫无意义。 - Nohus
是的,这是个好答案,你节省了我的时间。 - Jeyaseelan
有人知道这个异常在哪里被捕获了吗? - Artanis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接