从Android中的x.509证书中获取CA详细信息

时间:2015-12-22 14:24:43

标签: java android openssl ssl-certificate x509certificate

我使用

之类的东西启动了HTTPS连接
httpsCert.connect();

其中httpsCert为HttpsURLConnection httpsCert

现在我执行类似Certificate[] certs = httpsCert.getServerCertificates();的操作来检索服务器x.509证书。

  

我想检索哪个根CA或中间CA签署了上面收到的证书。

我的方法是查看上面收到的证书中的issuer字段,但这是一个很好的方法。我的意思是有更好的方法吗?

其次,从developer.android.com/HttpsUrlConnection开始,getServerCertificates()似乎会返回标识对等方的证书列表,其中包含对等方的身份证书,后跟CA. 在此上下文中如果是某些证书,例如Google应用程序提取的证书,则证书的一部分会显示授权信息访问权限:                                                                               CA Issuers - URI:http://pki.google.com/GIAG2.crt 但是其他应用程序获取其他权限签署的证书的情况并非如此。

  

第二个问题是Android如何验证由中间CA签名的证书的证书路径?

2 个答案:

答案 0 :(得分:3)

  

我想检索哪个根CA或中间CA签署了上面收到的证书。

如果您正在谈论缺少证书,那么您就无法做到。这是PKI中一个众所周知的问题,称为"哪个目录"问题。问题是你不知道在哪里找到丢失的证书。服务器发送构建链以执行验证所需的所有证书,解决了该问题。

你仍然必须在某处信任;否则坏人会发给你他想要你信任的连锁店,你就不会更聪明了。这就是浏览器和cURL等用户代理带有cacert.pem等列表的原因。

事实上,一些配置不当的服务器将发送所需的中间证书。在这种情况下,浏览器会携带一系列中间体来填补缺失的部分。

另见Peter Gutmann的Engineering Security

  

我的方法是查看上面收到的证书中的issuer字段,但这是一个很好的方法。我的意思是有更好的方法吗?

大致如何建立链条。当您使用颁发者的名称时,您使用的是可分辨名称("目录中的颁发者DN")。还有一个主题DN。发行人是权力机构,主体是其发行的实体。

验证链时,其名为"路径构建"。这会引导您RFC 4158, Internet X.509 Public Key Infrastructure: Certification Path Building

单凭DN通常是不够的,因为一个坏人可以重复使用相同的名称而你也不会更聪明。因此,您经常使用授权密钥标识符("目录中的AKI"),这是发行者公钥的指纹或摘要。这个坏人不能通过伪造AKI来做有用的事情,因为他没有私钥可以随身携带。

用于使元组唯一的其他内容是序列号。当CA使用相同的DN和相同的公钥重新颁发证书时,这很重要 - 只有序列号不同。当哈希值从SHA-1更改为SHA-256时,您会看到这种情况发生。

过去仅重新发布和更改哈希。由于表面上的一切看起来都很好,因此其中一个更难以追踪的路径验证失败。您需要一段时间才能意识到 DN AKI 都可以,但 SN 不匹配。

  

第二个问题是Android如何验证由中间CA签名的证书的证书路径?

Android是Java,Java遵循RFC。以下是您需要咨询的三个RFC。这不是一个小题目,你可以写一本提供全面处理的书:

"互联网X.509公钥基础设施"也被称为PKIX。它是互联网的PKI简介。其他组织的PKI可以并且有时会有所不同。这只是意味着其他组织可能有不同于PKIX使用的规则和RFC中的文档。

请注意:a" CA root"将是自签名的。主题DN将与发行者DN相同,主题的密钥标识符("目录中的SKI")将是发行者的权限密钥标识符(&#34中的AKI) ;目录说")等。此外,基本约束将CA=true,它可能会被标记为关键。

中间CA证书将针对(或链接到)不同的证书颁发,因此主题DN将与颁发者DN相同。但是,与自签名根一样,基本约束将具有CA=true,并且可能会被标记为关键。

作为依赖方,您完全可以接受信任中间人而不是根,即使中间人是由根证明的。这是你的特权。

答案 1 :(得分:0)

授权信息访问:CA颁发者存储在CA Extensions中。 此代码打印来自Authority Information Access Extension的URL列表。这就是你需要的东西。

import sun.security.util.ObjectIdentifier;
import sun.security.x509.X509CertImpl;

import java.util.regex.Matcher;  
import java.util.regex.Pattern;

class readCert{

    public boolean isExtAuthorityInfoAccess(Extension ext){
        Pattern re = Pattern.compile("\\bcaIssuers\\b",Pattern.CASE_INSENSITIVE);
        Matcher m = re.matcher(ext.toString());
        if (m.find()) {
            return true;
        } else {
            return false;
        }
    };

    public static List<String> getAuthorityInfoAccesssUrls(String text)
    {
        List<String> containedUrls = new ArrayList<String>();
        Pattern pattern = Pattern.compile(
                "(?:^|[\\W])((ht|f)tp(s?):\\/\\/|www\\.)"
                        + "(([\\w\\-]+\\.){1,}?([\\w\\-.~]+\\/?)*"
                        + "[\\p{Alnum}.,%_=?&#\\-+()\\[\\]\\*$~@!:/{};']*)",
                Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        Matcher urlMatcher = pattern.matcher(text);
        while (urlMatcher.find())
        {
            containedUrls.add(text.substring(urlMatcher.start(0),
                    urlMatcher.end(0)));
        }
        return containedUrls;
    };

    public static void main(String[] args) {

        readCert rc = new readCert();

        try {
            File file = new File("yourcert.crt");
            byte[] encCert = new byte[(int) file.length()];
            FileInputStream fis = new FileInputStream(file);
            fis.read(encCert);
            fis.close();

            InputStream in = new ByteArrayInputStream(encCert);
            CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate)certFactory.generateCertificate(in);

            X509CertImpl impl = (X509CertImpl)cert;
            int extnum = 0;
            if (cert.getNonCriticalExtensionOIDs() != null) {
                for (String extOID : cert.getNonCriticalExtensionOIDs()) {
                    Extension ext = impl.getExtension(new ObjectIdentifier(extOID));
                    if (ext != null) {
                        if (rc.isExtAuthorityInfoAccess(ext)) {
                            System.out.println(rc.getAuthorityInfoAccesssUrls(ext.toString()));
                            // System.out.println("#"+(++extnum)+": "+ ext.toString());
                            // CA ISSUERS ARE HERE
                        }
                    }
                }
            }
        } catch (  Exception e) {
            e.printStackTrace();
          };
    }
}