Search code examples
x509certificatebouncycastlelets-encryptjava-security

Extension unknown: DER encoded OCTET string error in generated certificate


I have created a SSL certificate using acme client acme4j: https://github.com/shred/acme4j.

But while I'm generating a self signed certificate I'm facing an exception while parsing it. Here is the my generated certificate:

{Version: V3
Subject: T=spid: yuz8xxz
Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11

Key:  Sun RSA public key, 2048 bits
modulus: 

public exponent: 65537
Validity: [From: Mon Apr 10 17:56:36 IST 2017,
           To: Mon Apr 17 17:56:36 IST 2017]
Issuer: T=spid: yuz8xxz
SerialNumber: [    015b57d4 807c]

Certificate Extensions: 1
[1]: ObjectId: 1.3.6.1.5.5.7.1.26 Criticality=false


**Extension unknown: DER encoded OCTET string =**
0000: 04 23 30 21 A0 12 16 07   79 75 7A 38 78 78 7A 16  .#0!....yuz8xxz.
0010: 07 79 75 7A 38 78 78 7A   A1 0B 16 06 31 32 33 34  .yuz8xxz....1234
0020: 35 36 02 01 01                                     56...


]
 Algorithm: [SHA256withRSA]
 Signature:
]
}

Solution

  • Acme4j code uses a java.security.cert.X509Certificate class. The toString() method of this class (when using Sun's default provider) is generating this "extension unknown" output (according to the corresponding code).

    So, in order to parse it correctly (get a formatted output), you'll probably have to change acme4j's code (or write your own), including the code to parse this extension.

    In my tests (Java 7 and BouncyCastle 1.56), I created a wrapper to X509Certificate and created a format method based on BouncyCastle's code (I copied the most part and just added code for TNAuthorizationList extension). The code below is not optimal (poor exception handling and some deprecated classes), but you can get an idea.

    import org.bouncycastle.asn1.*;
    import org.bouncycastle.asn1.misc.*;
    import org.bouncycastle.asn1.util.ASN1Dump;
    import org.bouncycastle.asn1.x509.*;
    import org.bouncycastle.util.encoders.Hex;
    
    public class CertificateWrapper {
    
        private X509Certificate cert;
    
        public CertificateWrapper(X509Certificate cert) {
            this.cert = cert;
        }
    
        public String format() throws Exception {
            StringBuffer buf = new StringBuffer();
            String nl = System.getProperty("line.separator");
    
            buf.append("  [0]         Version: ").append(this.cert.getVersion()).append(nl);
            buf.append("         SerialNumber: ").append(this.cert.getSerialNumber()).append(nl);
            buf.append("             IssuerDN: ").append(this.cert.getIssuerDN().toString()).append(nl);
            buf.append("           Start Date: ").append(this.cert.getNotBefore()).append(nl);
            buf.append("           Final Date: ").append(this.cert.getNotAfter()).append(nl);
            buf.append("            SubjectDN: ").append(this.cert.getSubjectDN().toString()).append(nl);
            buf.append("           Public Key: ").append(this.cert.getPublicKey()).append(nl);
            buf.append("  Signature Algorithm: ").append(this.cert.getSigAlgName()).append(nl);
    
            byte[] sig = this.cert.getSignature();
    
            buf.append("            Signature: ").append(new String(Hex.encode(sig, 0, 20))).append(nl);
            for (int i = 20; i < sig.length; i += 20) {
                if (i < sig.length - 20) {
                    buf.append("                       ").append(new String(Hex.encode(sig, i, 20))).append(nl);
                } else {
                    buf.append("                       ").append(new String(Hex.encode(sig, i, sig.length - i))).append(nl);
                }
            }
    
            TBSCertificateStructure tbs = TBSCertificateStructure.getInstance(ASN1Sequence.fromByteArray(cert.getTBSCertificate()));
            X509Extensions extensions = tbs.getExtensions();
    
            if (extensions != null) {
                Enumeration e = extensions.oids();
    
                if (e.hasMoreElements()) {
                    buf.append("       Extensions: \n");
                }
    
                while (e.hasMoreElements()) {
                    ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) e.nextElement();
                    X509Extension ext = extensions.getExtension(oid);
    
                    if (ext.getValue() != null) {
                        byte[] octs = ext.getValue().getOctets();
                        ASN1InputStream dIn = new ASN1InputStream(octs);
                        buf.append("                       critical(").append(ext.isCritical()).append(") ");
                        try {
                            if (oid.equals(Extension.basicConstraints)) {
                                buf.append(BasicConstraints.getInstance((ASN1Sequence) dIn.readObject())).append(nl);
                            } else if (oid.equals(Extension.keyUsage)) {
                                buf.append(KeyUsage.getInstance((DERBitString) dIn.readObject())).append(nl);
                            } else if (oid.equals(MiscObjectIdentifiers.netscapeCertType)) {
                                buf.append(new NetscapeCertType((DERBitString) dIn.readObject())).append(nl);
                            } else if (oid.equals(MiscObjectIdentifiers.netscapeRevocationURL)) {
                                buf.append(new NetscapeRevocationURL((DERIA5String) dIn.readObject())).append(nl);
                            } else if (oid.equals(MiscObjectIdentifiers.verisignCzagExtension)) {
                                buf.append(new VerisignCzagExtension((DERIA5String) dIn.readObject())).append(nl);
                            //*********************************************************
                            // *** HERE: code to handle TNAuthorizationList ***
                            //*********************************************************
                            } else if (oid.equals(TNAuthorizationList.TN_AUTH_LIST_OID)) {
                                buf.append(TNAuthorizationList.getInstance((ASN1Sequence) dIn.readObject())).append(nl);
                            } else {
                                buf.append(oid.getId());
                                buf.append(" value = ").append(ASN1Dump.dumpAsString(dIn.readObject())).append(nl);
                            }
                        } catch (Exception ex) {
                            buf.append(oid.getId());
                            buf.append(" value = ").append("*****").append(nl);
                        }
                    } else {
                        buf.append(nl);
                    }
                }
            }
    
            return buf.toString();
        }
    }
    

    Then, you just use this wrapper instead of X509Certificate:

    X509Certificate cert = ...
    System.out.println("Certificate " + new CertificateWrapper(cert).format());
    

    The output format is different from SUN's default provider, but you can customize it to get what you need.


    PS: This question is a complement (or "sequel") to this one. The OP's code was provided here and TNAuthorizationList class is in the accepted answer. But as this question is a different issue (related, but different), it was kept as a separate question.