3

我有一个通过 SSL 调用 Web 服务的 Android 应用程序。在生产中,我们将拥有由受信任的 CA 签名的普通 SSL 证书。但是,我们需要能够支持自签名证书(由我们自己的 CA 签名)。

我已经成功实施了建议的接受自签名证书的解决方案,但是由于中间人攻击的风险,这将不起作用。然后我创建了一个信任管理器来验证证书链实际上是由我们的 CA 签名的。

问题是我必须绕过正常的 SSL 验证——应用程序现在只会与安装了我们的自签名证书之一的服务器通信。

我有点迷路,我已经广泛搜索但找不到任何东西。我希望找到一种以编程方式将我们的 CA 添加到设备上的信任库的方法,因为这将是处理该问题的侵入性最小的方法。

我想要实现的目标: 1. 对普通 SSL 证书的完全标准支持。2. 对我们自己的 CA 签名的自签名证书的额外支持。

有什么建议吗?

4

1 回答 1

2

你还没有发布任何代码,所以我不能确定你到底做了什么。但是,我假设您SSLContext只使用自定义X509TrustManager子类来设置。这很好,但是您可以做的是让您的自定义信任管理器实现链接到内置的信任管理器。您可以在设置信任管理器时执行此操作;像这样的东西应该工作:

private List<X509TrustManager> trustManagers = new ArrayList<X509TrustManager>();

public MyCustomTrustManager() {
    TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmFactory.init((KeyStore)null);

    for (TrustManager tm : tmFactory.getTrustManagers()) {
        if (tm instanceof X509TrustManager)
            trustManagers.add((X509TrustManager)tm);
    }
}

因此,现在您的自定义信任管理器拥有所有内置信任管理器的列表。在您对 的覆盖中checkServerTrusted(),您需要遍历内置的信任管理器,并通过checkServerTrusted()依次调用每个信任管理器来检查每个信任管理器。如果他们都不信任证书,您可以应用自己的证书检查。如果通过,您可以正常返回。如果没有,只需像其他方式一样抛出 a CertificateException

编辑:添加以下有关执行主机名验证等操作的内容。

您还可以验证证书中的主机名是否符合您的预期。您需要在构造函数中为您的自定义信任管理器传递有效的主机名,并将其存储在类中。您的checkServerTrusted()方法将获得一个数组X509Certificate。许多“链”将仅包含一个证书,但其他“链”将包含多个,具体取决于 cA 如何签署您的证书。无论哪种方式,数组中的第一个证书应该是您想要比较的“您的”证书。

使用信任管理器检查基本证书有效性后,您需要执行以下操作:

Principal subjectDN = chain[0].getSubjectDN();
String subjectCN = parseDN(subjectDN.getName(), "CN");
if (this.allowedCN.equals(subjectCN)) {
    // certificate is good
}

的实施由parseDN()您决定。 subjectDN.getName()将返回以逗号分隔的键值对列表(由 分隔=),例如C=US,ST=California,L=Mountain View,O=Google Inc,CN=www.google.com. 您需要用于主机名比较的 CN(“通用名称”)值。请注意,如果您有通配符证书,它将被列为类似 的*.example.com内容,因此在这种情况下,您需要做的不仅仅是简单的等号匹配。

于 2012-10-31T06:36:24.923 回答