How to identify if the certificate is A1 or A3?

Asked

Viewed 305 times

2

There is a safe way to identify from an object X509Certificate, if the certificate is A1 or A3?

I’m using the function below, but very prone to failures:

// Certificados podem ter descrições diferentes. Foram encontrados os casos abaixo
// "OU=Assinatura Tipo A3"
// "OU=RFB e-CPF A3"
private static boolean isA3(X509Certificate cert) {
    final String name = cert.getSubjectX500Principal().getName();
    final String[] issueValues = name.split(",");
    for (String value : issueValues) {
        final String[] field = value.split("=");
        if (field[0].equals("OU"))
            if (field[1].endsWith("A3"))
                return true;
    }

    return false;
}

1 answer

2


The ICP-Brazil certificates have the extension Certificate Policies, as stated in the IRS document (see section 1.2.4):

the policyIdentifier field contains the Certification Policy OID (PC) that AC certificate holder implements;

So just read this extension and see the value there:

import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.x509.extension.X509ExtensionUtil;

X509Certificate cert = ...
// obter a extensão CertificatePolicies
byte[] value = cert.getExtensionValue(X509Extension.certificatePolicies.getId());
DERSequence seq = (DERSequence) X509ExtensionUtil.fromExtensionValue(value);
DERObjectIdentifier oid = (DERObjectIdentifier) ((DERSequence) seq.getObjectAt(0)).getObjectAt(0);
System.out.println(oid.getId());
if (oid.getId().startsWith("2.16.76.1.2.3.")) {
    // A3
} else if (oid.getId().startsWith("2.16.76.1.2.1.")) {
    // A1
}

I used the library Bouncy Castle to read the value of the extension, because to interpret the bytes manually is to want very much to reinvent the wheel. For the code above, I used version 1.46.

Finally, the value of this extension is well defined among CA’s, and the important thing is that they all have the same initial digits, varying only the last digit, which indicates the specific AC. Some examples:

Serasa

  • A3: 2.16.76.1.2.3.10
  • A1: 2.16.76.1.2.1.13

Certisign:

  • A3: 2.16.76.1.2.3.6
  • A1: 2.16.76.1.2.1.12

These values can be found in the respective Certificate Policies of each CA:

  • Serasa A3 and A1 (the values are indicated in section 1.2.2, although the Serasa A3 document has the wrong number (it has an extra "2" at the beginning)
  • Certisign A3 and A1, in section 1.2

Anyway, notice that for A3, they both start with 2.16.76.1.2.3 and only the last digit varies because it indicates the respective AC. You can refer to the value 2.16.76.1.2.3 here, and see that it corresponds to "Certificate policy A3" (and also see that all Oids starting with 2.16.76.1 is because they are below the ICP-Brazil).

Already if you consult 2.16.76.1.2.3.10, will see that it corresponds to a A3 from Serasa-RFB, while 2.16.76.1.2.3.6 corresponds to a A3 from Certisign-RFB. Anyway, everyone who starts with 2.16.76.1.2.3. are A3.

The same logic goes for A1, just start with 2.16.76.1.2.1..

Note that the dot at the end of the string is important, as there are OID’s like 2.16.76.1.2.303 and 2.16.76.1.2.101, then it is important that the comparison is startsWith("2.16.76.1.2.1.") (if the last point is removed, you may mistakenly consider that 2.16.76.1.2.101 is an A1, for example).


If you want to know the URL of the respective Certificate Policy (the address of the document), it is also the same extension - in the example above, just go through the sequence seq, or print it directly, to see this URL. The code is not very pretty:

DERSequence subSeq = (DERSequence) ((DERSequence) seq.getObjectAt(0)).getObjectAt(1);
DEREncodable url = ((DERSequence) subSeq.getObjectAt(0)).getObjectAt(1);
System.out.println(url.toString());

If you want, you can also make several loops through DERSequence (using size() to know the size and getObjectAt to obtain the respective object). But since this structure is a sequence inside another one inside another, it is quite "annoying" to travel it.


In previous versions of Bouncy Castle it was possible to use the class CertificatePolicies, but your methods are deprecated (even though they still work):

import org.bouncycastle.asn1.x509.CertificatePolicies;

byte[] value = cert.getExtensionValue(X509Extension.certificatePolicies.getId());
CertificatePolicies cp = CertificatePolicies.getInstance(X509ExtensionUtil.fromExtensionValue(value));
String policy = cp.getPolicy(0);
if (policy.startsWith("2.16.76.1.2.3.")) {
    // A3
} else if (policy.startsWith("2.16.76.1.2.1.")) {
    // A1
}
  • 1

    I didn’t know Bouncy Castle. I even used it to read the certificate’s CPF or CNPJ in a much more user-friendly way than the previous implementation without it.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.