0

我尝试在 java中解析证书的额外数据。我对 subjectAlternativeNames 部分感兴趣。我的代码是:

CertificateFactory certFactory = CertificateFactory.getInstance("X.509")
certFactory.generateCertificate(ByteArrayInputStream(x509EntryChain.leafCertificate))

X509CertInfo包含java.io.IOException: invalid URI name:DNS:*.cetrel.lu, DNS:mail.cetrel.lu, DNS:www.cetrel.lu. 并且subjectAlternativeNames是空的。

我该如何解析它?

我只对 感兴趣extra_data,对 不感兴趣leaf_input。我可以在这里找到证书:http: //transparencyreport.google.com/https/certificates/

DNS names部分是空的,似乎谷歌也无法解析它。

提供证书的完整代码:


import org.apache.commons.codec.binary.Base64;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import sun.security.x509.CertificateExtensions;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;

import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;

public class CertParser {

    public static void main(String[] args) throws CertificateException, IOException {
        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        ByteArrayInputStream in = new ByteArrayInputStream(Base64.decodeBase64(
                "AAAAAAFXsGq8HwAAAAW+MIIFujCCBKKgAwIBAgIDAZZpMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkxVMRYwFAYDVQQKEw1MdXhUcnVzdCBTLkEuMR4wHAYDVQQDExVMdXhUcnVzdCBRdWFsaWZpZWQgQ0EwHhcNMTAwNDE1MDY0MzM0WhcNMTMwNDE1MDY0MzM0WjB8MQswCQYDVQQGEwJMVTERMA8GA1UEBxMITXVuc2JhY2gxFDASBgNVBAoTC0NFVFJFTCBTLkEuMRAwDgYDVQQLEwdJT1AtU1NTMRQwEgYDVQQDFAsqLmNldHJlbC5sdTEcMBoGCSqGSIb3DQEJARYNc3NzQGNldHJlbC5sdTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALhf+I7RshQlHMMWq/WPDLNxx+ODMd4Tn9ej14igvMEE+RYEagdMOZoeO8Bqz9qV2atzFHqz0D+Ad+cxuznBGKl4rhS9gUejgoAMox7car0+LSsv1NT4J0gAlnmH3BJlDMd9CihT0D/sRwMNfa8GYAvCuGDtWIvYb497RFy+2kmzk3cwCk3BgOO3MsT7iqhcn65Pd1Lq1vLjCCuQBoWLlcKk4uptPsyFKrHEh1/0ksY5evqBPxioVppoN+oay20RK36JzrzAl+vfpzq03WRlM2IgM0ItnesLqid9GqTUsOTq59i5aVX1EKlfgM5v7YCpYMLrJA+JBO3beR/4FSczfccCAwEAAaOCAnowggJ2MAwGA1UdEwEB/wQCMAAwYAYIKwYBBQUHAQEEVDBSMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5sdXh0cnVzdC5sdTArBggrBgEFBQcwAoYfaHR0cDovL2NhLmx1eHRydXN0Lmx1L0xUUUNBLmNydDBBBgNVHREEOjA4hjZETlM6Ki5jZXRyZWwubHUsIEROUzptYWlsLmNldHJlbC5sdSwgRE5TOnd3dy5jZXRyZWwubHUwggEABgNVHSAEgfgwgfUwgegGCCuBKwEBAgYBMIHbMIGtBggrBgEFBQcCAjCBoBqBnUx1eFRydXN0IFNlcnZlciBDZXJ0aWZpY2F0ZS4gTm90IHN1cHBvcnRlZCBieSBTU0NELCBLZXkgR2VuZXJhdGlvbiBieSBTdWJzY3JpYmVyLiBHVEMsIENQIGFuZCBDUFMgb24gaHR0cDovL3JlcG9zaXRvcnkubHV4dHJ1c3QubHUuIFNpZ25lZCBieSBhIFF1YWxpZmllZCBDQS4wKQYIKwYBBQUHAgEWHWh0dHA6Ly9yZXBvc2l0b3J5Lmx1eHRydXN0Lmx1MAgGBgQAj3oBAzARBglghkgBhvhCAQEEBAMCBeAwDgYDVR0PAQH/BAQDAgSwMCcGA1UdJQQgMB4GCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwQwHwYDVR0jBBgwFoAUjZCjB90aE3eZTJKrTUPeP80pZAUwMQYDVR0fBCowKDAmoCSgIoYgaHR0cDovL2NybC5sdXh0cnVzdC5sdS9MVFFDQS5jcmwwHQYDVR0OBBYEFLhOdzNJg4CSchxSsHbwKWMVm8xnMA0GCSqGSIb3DQEBBQUAA4IBAQBrQ9bYRA5O8a+4vIfVx5izH5x9yieKuQjpF4Etc4YUxfk3h5yZieWJuZGHygUjV5TCYDdhtVf6FFWkJ4FT5l6zDQeOLrEWqL12qcT4hGFN61mwjZO7kca8IHlqPPeqtYVg/Ssbpun+bjPOsGdvrvMulqNNTz5UeQuovc/VFaoHpYCQhezrQ6E6uQ684f6LFVIbsah6pT58wEnrf6xE1aRdSk27e3bF8wns3zOVsWE2wKck5pMS5DkGwjWli27Aqt6QQCyCKC7xqqxwL8GfnmXZNdn2iYYfSyr0I7rdqxa7FsuNFkEb8/PdZlyMxQP867YnucRCyLzzjCnRy1bOLnUeAAA="
        ));
        readNumber(in, 1); // version
        readNumber(in, 1); // type
        readNumber(in, 8); // timestamp
        readNumber(in, 2); // entry type
        int length = (int) readNumber(in, 3);
        byte[]  x509 = readFixedLength(in, length);
        X509CertImpl cert = (X509CertImpl) certFactory.generateCertificate(new ByteArrayInputStream(x509));
        ((CertificateExtensions)((X509CertInfo)cert.get("x509.info")).get("extensions")).getUnparseableExtensions(); // <-- want too be empty, but it's not!!!
    }

    static byte[] readFixedLength(InputStream inputStream, int dataLength) throws IOException {
        byte[] toReturn = new byte[dataLength];
        int bytesRead = inputStream.read(toReturn);
        if (bytesRead < dataLength) {
            throw new RuntimeException();
        }
        return toReturn;
    }

    static long readNumber(InputStream inputStream, int numBytes) throws IOException {
        long toReturn = 0;
        for (int i = 0; i < numBytes; i++) {
            int valRead = inputStream.read();
            if (valRead < 0) {
                throw new RuntimeException();
            }
            toReturn = (toReturn << 8) | valRead;
        }
        return toReturn;
    }
}

4

1 回答 1

1

在您共享代码以访问证书后,我可以对其进行安全检查。

正如问题中已经清楚的那样,有问题的扩展名是主题替代名称。它的值被指定(RFC 5280)作为一个实例,GeneralNames它是一个SEQUENCEGeneralName它是一个CHOICE

SubjectAltName ::= GeneralNames

GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName

GeneralName ::= CHOICE {
     otherName                 [0]  AnotherName,
     rfc822Name                [1]  IA5String,
     dNSName                   [2]  IA5String,
     x400Address               [3]  ORAddress,
     directoryName             [4]  Name,
     ediPartyName              [5]  EDIPartyName,
     uniformResourceIdentifier [6]  IA5String,
     iPAddress                 [7]  OCTET STRING,
     registeredID              [8]  OBJECT IDENTIFIER }

这是您的证书相关扩展的转储:

    <30 41>
 676   65: . . . . SEQUENCE {
    <06 03>
 678    3: . . . . . OBJECT IDENTIFIER subjectAltName (2 5 29 17)
         : . . . . . . (X.509 extension)
    <04 3A>
 683   58: . . . . . OCTET STRING, encapsulates {
    <30 38>
 685   56: . . . . . . SEQUENCE {
    <86 36>
 687   54: . . . . . . . [6]
         : . . . . . . . . 'DNS:*.cetrel.lu, DNS:mail.cetrel.lu, DNS:www.cet'
         : . . . . . . . . 'rel.lu'
         : . . . . . . . }
         : . . . . . . }
         : . . . . . }

该值标有 6 ( [6])。因此,uniformResourceIdentifier使用了选择。RFC 5280 在主题替代名称的上下文中要求此选项的值:

名称不能是相对 URI,它必须遵循 [RFC3986] 中指定的 URI 语法和编码规则。名称必须包括方案(例如,“http”或“ftp”)和方案特定部分。

因此,sun.security.x509.X509CertImpl尝试将值解析为 URI。这显然必须失败,因为该值根本不是 URI:

java.io.IOException: invalid URI name:DNS:*.cetrel.lu, DNS:mail.cetrel.lu, DNS:www.cetrel.lu
    at sun.security.x509.URIName.<init>(URIName.java:109)
    at sun.security.x509.URIName.<init>(URIName.java:96)
    at sun.security.x509.GeneralName.<init>(GeneralName.java:122)
    at sun.security.x509.GeneralName.<init>(GeneralName.java:76)
    at sun.security.x509.GeneralNames.<init>(GeneralNames.java:68)
    at sun.security.x509.SubjectAlternativeNameExtension.<init>(SubjectAlternativeNameExtension.java:141)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at sun.security.x509.CertificateExtensions.parseExtension(CertificateExtensions.java:113)
    at sun.security.x509.CertificateExtensions.init(CertificateExtensions.java:88)
    at sun.security.x509.CertificateExtensions.<init>(CertificateExtensions.java:78)
    at sun.security.x509.X509CertInfo.parse(X509CertInfo.java:702)
    at sun.security.x509.X509CertInfo.<init>(X509CertInfo.java:167)
    at sun.security.x509.X509CertImpl.parse(X509CertImpl.java:1804)
    at sun.security.x509.X509CertImpl.<init>(X509CertImpl.java:195)
    at sun.security.provider.X509Factory.engineGenerateCertificate(X509Factory.java:102)
    at java.security.cert.CertificateFactory.generateCertificate(CertificateFactory.java:339)
Caused by: java.net.URISyntaxException: Illegal character in opaque part at index 16: DNS:*.cetrel.lu, DNS:mail.cetrel.lu, DNS:www.cetrel.lu
    at java.net.URI$Parser.fail(URI.java:2848)
    at java.net.URI$Parser.checkChars(URI.java:3021)
    at java.net.URI$Parser.parse(URI.java:3058)
    at java.net.URI.<init>(URI.java:588)
    at sun.security.x509.URIName.<init>(URIName.java:107)
    ... 46 more

如果您仍然想访问该值,只需从UnparseableExtensions您拥有的地图中检索并解析它,如下所示:

Map<String, Extension> unparseables = ((CertificateExtensions)((X509CertInfo)cert.get("x509.info")).get("extensions")).getUnparseableExtensions();

Extension extension = unparseables.get("2.5.29.17");
byte[] value = extension.getValue();
DerValue derValue = new DerValue(value);
while (derValue.data.available() > 0) {
    DerValue encName = derValue.data.getDerValue();
    if ((encName.tag & 0x1f) == 6) {
        encName.resetTag(DerValue.tag_IA5String);
        System.out.printf("IA5String value from URI GeneralName value: %s\n", encName.getIA5String());
    }
}

对于sun.security.x509.Extensionsun.security.util.DerValue

输出:

IA5String value from URI GeneralName value: DNS:*.cetrel.lu, DNS:mail.cetrel.lu, DNS:www.cetrel.lu
于 2020-02-17T18:02:33.643 回答