Home » Java » certificate – Can't pars subjectAlternativeNames in java-Exceptionshub

certificate – Can't pars subjectAlternativeNames in java-Exceptionshub

Posted by: admin February 25, 2020 Leave a comment

Questions:

I try to parse extra data of certificate in java. I interested in subjectAlternativeNames section. My code is:

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

X509CertInfo contains java.io.IOException: invalid URI name:DNS:*.cetrel.lu, DNS:mail.cetrel.lu, DNS:www.cetrel.lu.
and subjectAlternativeNames is empty.

How can I parse it?

I am interested only in extra_data, not in leaf_input. I can find the certificate here: http://transparencyreport.google.com/https/certificates/

The DNS names section is empty, seems Google can’t parse it either.

Full code to parce cert:


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;
    }
}

How to&Answers:

After you shared your code to access the certificate, I could safe and inspect it.

As was already clear from the question, the problematic extension is the subject alternative name. Its value is specified (RFC 5280) as an instance of GeneralNames which is a SEQUENCE of GeneralName which is a 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 }

This is a dump of the extension in question of your certificate:

    <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'
         : . . . . . . . }
         : . . . . . . }
         : . . . . . }

The value is tagged with a 6 ([6]). Thus, the uniformResourceIdentifier choice is used. RFC 5280 in the context of subject alternative names requires for the value of this choice:

The name
MUST NOT be a relative URI, and it MUST follow the URI syntax and
encoding rules specified in [RFC3986]. The name MUST include both a
scheme (e.g., “http” or “ftp”) and a scheme-specific-part.

Thus, the sun.security.x509.X509CertImpl attempts to parse the value as an URI. This obviously must fail as the value simply is no 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

If you want to access the value nonetheless, simply retrieve and parse it from the UnparseableExtensions map you have like this:

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());
    }
}

for sun.security.x509.Extension and sun.security.util.DerValue.

The output:

IA5String value from URI GeneralName value: DNS:*.cetrel.lu, DNS:mail.cetrel.lu, DNS:www.cetrel.lu