Android - 除了普通的SSL证书外,还验证自签名证书

时间:2012-10-31 06:11:59

标签: java android web-services ssl ssl-certificate

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

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

问题是我必须绕过正常的SSL验证 - 应用程序现在只会对安装了我们自签名证书的服务器说话。

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

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

有什么建议吗?

1 个答案:

答案 0 :(得分: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()来检查每个信任管理器。如果他们都不信任证书,您可以应用自己的证书检查。如果通过,您可以正常返回。如果没有,只需抛出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的内容,因此在这种情况下您需要做的不仅仅是简单的等号匹配。