【问题标题】:How to import global CA certificate programatically in Android 4.X?如何在 Android 4.X 中以编程方式导入全球 CA 证书?
【发布时间】:2016-10-06 01:31:40
【问题描述】:

我们的应用程序使用 http/https 连接其 REST 服务器。在我们切换到 https 之前,一切正常。 Min SDK version = 14。 IOS版本运行没有任何问题。

这是openssl s_client -connect test_server.ru:443 的输出:

Certificate chain
 0 s:/CN=test_server.ru
   i:/C=US/O=thawte, Inc./OU=Domain Validated SSL/CN=thawte DV SSL CA - G2
 1 s:/C=US/O=thawte, Inc./OU=Domain Validated SSL/CN=thawte DV SSL CA - G2
   i:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA
 2 s:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA
   i:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA

Android 4-5 Chrome 将站点证书显示为无效(红色和交叉)。应用程序捕获异常:“未找到证书路径的信任锚”

所以我将两个键都添加到资产文件夹并制作了快速静态类来使用这些键:

public class CustomTrustCA {
    private static SSLContext mSSLContext = null;

    public static SSLSocketFactory getInstance() {
        if (mSSLContext == null && Init() == null) return null;
        return mSSLContext.getSocketFactory();
    }

    public static SSLContext Init() {
        Certificate ca = null;
        Certificate ca2 = null;
        InputStream caInput = null;
        InputStream caInput2 = null;
        KeyStore keyStore = null;
        TrustManagerFactory tmf = null;
        mSSLContext = null;

        //noinspection TryFinallyCanBeTryWithResources
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            caInput = Application.AppContext.getAssets().open("thawte.cer");
            ca = cf.generateCertificate(caInput);
            caInput2 = Application.AppContext.getAssets().open("thawte2.cer");
            ca2 = cf.generateCertificate(caInput2);
        } catch (Exception e) {
            Logger.Exception(e);
        } finally {
            try {
                if (caInput != null) caInput.close();
                if (caInput2 != null) caInput2.close();
            } catch (Exception ignored) {}
        }

        if (ca == null) return null;
        if (ca2 == null) return null;
        try {
            // Create a KeyStore containing our trusted CAs
            String keyStoreType = KeyStore.getDefaultType();
            keyStore = KeyStore.getInstance(keyStoreType);
            keyStore.load(null, null);
            keyStore.setCertificateEntry("ca", ca);
            keyStore.setCertificateEntry("ca2", ca2);
        } catch (Exception e) {
            Logger.Exception(e);
        }

        try {
            // Create a TrustManager that trusts the CAs in our KeyStore
            String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
            tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
            tmf.init(keyStore);
        } catch (Exception e) {
            Logger.Exception(e);
        }

        if (tmf == null) return null;
        try {
            // Create an SSLContext that uses our TrustManager
            mSSLContext = SSLContext.getInstance("TLS");
            mSSLContext.init(null, tmf.getTrustManagers(), null);
        } catch (Exception e) {
            Logger.Exception(e);
        }

        return mSSLContext;
    }
}

然后我将它添加到低级 http 代码中:

HttpsURLConnection urlConnection = (HttpsURLConnection) full_url.openConnection();
SSLSocketFactory instance = CustomTrustCA.getInstance();
if (instance != null) urlConnection.setSSLSocketFactory(instance);

这解决了问题,但通用图像加载器也坏了,所以我写了一些代码来修复它(自定义图像下载器类):

public class SecureImageDownloader extends BaseImageDownloader {
    public static final String TAG = SecureImageDownloader.class.getName();


    public SecureImageDownloader(Context context, int connectTimeout, int readTimeout) {
        super(context, connectTimeout, readTimeout);
    }

    @Override
    protected InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException {

        URL url = null;
        try {
            url = new URL(imageUri);
        } catch (MalformedURLException e) {
            Logger.Exception(e);
        }
        HttpURLConnection http = null;
        if (url == null) return null;

        if (Scheme.ofUri(imageUri) == Scheme.HTTPS) {
            HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
            https.setSSLSocketFactory(CustomTrustCA.getInstance());
            http = https;
            http.connect();
        } else {
            http = (HttpURLConnection) url.openConnection();
        }

        http.setConnectTimeout(connectTimeout);
        http.setReadTimeout(readTimeout);
        return new FlushedInputStream(new BufferedInputStream(http.getInputStream()));
    }
}

好的。主要代码现在可以工作了。 UIL 也可以...但是...

最后我发现附件的 DownloadManager 和 Intent.ACTION_VIEW 也停止工作(应用程序将它们传递 https url!),但不知道如何修复它们。这是我的代码:

if (isNotEmpty(documentUri)) {
    DownloadManager.Request downloadReq = new DownloadManager.Request(Uri.parse(documentUri));
    downloadReq.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, attachment.Name());
    downloadReq.allowScanningByMediaScanner();
    downloadReq.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
    DownloadManager dm = (DownloadManager) Application.AppContext.getSystemService(Context.DOWNLOAD_SERVICE);
    dm.enqueue(downloadReq);
}

有没有办法使用代码添加系统范围的 CA 证书(即启动一些设置意图或其他东西)?

【问题讨论】:

    标签: java android ssl certificate ssl-certificate


    【解决方案1】:

    我编写了简单的 lib ssl-utils-android 来从此类资产中加载特定证书:

    SSLContext sslContext = SslUtils.getSslContextForCertificateFile(context, "BPClass2RootCA-sha2.cer");
    

    您可以在my blog post 上阅读更多相关信息。

    编辑: 将您自己的 http 客户端实例加载到 Universal Image Loader,例如:

    ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
        ...
        .imageDownloader(new HttpClientImageDownloader(context, yourHttpClient))
        ...
        .build();
    ImageLoader.getInstance().init(config);
    

    我从未使用过 UIL。希望它会起作用。

    【讨论】:

    • 我的代码已经完成了,但我不知道如何更改 DownloadManager 和 Intent.ACTION_VIEW 的 sslcontext .....
    • 要使通用图像加载器正常工作,您还必须向它添加 CA,因为它使用另一个 HTTP 客户端实例。
    • 目前 UIL 已经修复(参见上面的代码)。我只有外部意图(下载器和 ACTION_VIEW)有问题,不知道如何解决它们......
    • 我回答了题主的问题。我看不出Intent 是如何破坏CA 的。它们完全不相关。
    • 好的。我只是想问如何在全球范围内导入CA证书...关于下载mansger和action_view - 我的应用程序使用它们来下载附件并显示附件图片。 DownloadManager 和 Intent.ACTION_VIEW 都使用应用程序提供的 HTTPS url 来下载数据。由于证书,他们未能做到这一点。所以下载附件/查看附件不起作用...
    猜你喜欢
    • 2012-02-19
    • 2011-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-13
    • 1970-01-01
    • 2014-02-08
    • 2018-12-23
    相关资源
    最近更新 更多