qibobo
qibobo

Reputation: 543

Java can not load "BEGIN TRUSTED CERTIFICATE" format certificate

I have a CA certificate which is generate by openssl with "trustout", so the it begins with "-----BEGIN TRUSTED CERTIFICATE-----", when I tried to read it with Java, exception throws. Does Java supports this format of certificate? If so, how to read it?

public class TestReadCerts {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
         String sslrootcertfile = "F:\\javaworkspace\\opensource\\certs\\ca.pem";

            FileInputStream fis=null;
            try {
              fis = new FileInputStream(sslrootcertfile); // NOSONAR
            } catch (FileNotFoundException ex) {
              ex.printStackTrace();
            }
            try {
              CertificateFactory cf = CertificateFactory.getInstance("X.509");

              Object[] certs = cf.generateCertificates(fis).toArray(new Certificate[]{});

            } catch (Exception e) {
              e.printStackTrace();
            } 
    }

}

The exception: java.security.cert.CertificateException: Unable to initialize,

java.io.IOException: extra data given to DerValue constructor
    at java.base/sun.security.x509.X509CertImpl.<init>(X509CertImpl.java:191)
    at java.base/sun.security.provider.X509Factory.parseX509orPKCS7Cert(X509Factory.java:476)
    at java.base/sun.security.provider.X509Factory.engineGenerateCertificates(X509Factory.java:361)
    at java.base/java.security.cert.CertificateFactory.generateCertificates(CertificateFactory.java:478)
    at TestReadCerts.main(TestReadCerts.java:21)
Caused by: java.io.IOException: extra data given to DerValue constructor
    at java.base/sun.security.util.DerValue.init(DerValue.java:409)
    at java.base/sun.security.util.DerValue.<init>(DerValue.java:294)
    at java.base/sun.security.util.DerValue.<init>(DerValue.java:305)
    at java.base/sun.security.x509.X509CertImpl.<init>(X509CertImpl.java:188)
    ... 4 more

And the certificate is as below:

-----BEGIN TRUSTED CERTIFICATE-----
MIICATCCAWoCCQDjKSwZBsrQwTANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMB4XDTE5MDQwMzE0NDcwMVoXDTIwMDQwMjE0NDcwMVowRTELMAkG
A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyRCB
rbB/FqN6e9IAJ86WUUGxM+8vEyfQ7cn2HWca220NB/Ns3Q+QtvztSe48PUzn9w6s
MNOwsDW4+8lenPLd78J32lG59x1P1R1jpjL3GcjNTwuewW1jIsex8jALzfU9hJzO
prraO/6X+UbKbazXt6GiB7mOlUvneKsWuoGpF5MCAwEAATANBgkqhkiG9w0BAQsF
AAOBgQCN1UJF/FdT84bzEn1kmg77b+LCCrU11DsFg/s/ABvo5TKV+OmilBPj1vML
dbZ4GDQSaXKZAOyJiAp0S5BzHXlXz5YfX9sM4mfhaqZt736WAnKVSnzd55CjMlEk
GxW3TkRFL5cVm5my1UQs3Mfg4MC5QPaoer5kc+0UhMHmTlgyvTAMMAoGCCsGAQUF
BwMB
-----END TRUSTED CERTIFICATE-----

Upvotes: 2

Views: 2763

Answers (2)

Misantorp
Misantorp

Reputation: 2821

Please also check out the answer by dave_thompson_085 as it points out some inaccuracies in my understanding and as a result this answer


Your problem is trying to read a PEM encoded certificate with CertificateFactory which expects a DER encoding as stated in the documentation

In the case of a certificate factory for X.509 certificates, the certificate provided in inStream must be DER-encoded and may be supplied in binary or printable (Base64) encoding.

I think the quickest way to read the certificate is to convert the certificate into the appropriate encoding. Since you already mentioned using openssl to generate the certificate you can encode your current certificate to DER

$ openssl x509 -in /F/javaworkspace/opensource/certs/ca.pem -outform DER -out /F/javaworkspace/opensource/certs/ca.der

(adjust the path to ca.pem and ca.der as needed)

Lastly, don't forget to update the sslrootcertfile variable

String sslrootcertfile = "F:\\javaworkspace\\opensource\\certs\\ca.der";

Upvotes: 2

dave_thompson_085
dave_thompson_085

Reputation: 38990

PEM type 'TRUSTED CERTIFICATE' is an OpenSSL-specific nonstandard format that Java can't handle out of the box. It actually contains the standard X.509 cert as one DER chunk plus another OpenSSL-defined DER chunk of trust information.

If you have OpenSSL, the simplest method is to convert to the standard 'CERTIFICATE' format with openssl x509 <in >out . You can add -outform DER as Misantorp did but it isn't needed; CertificateFactory can read the standard format in either DER or PEM as OpenSSL inexactly calls them.

If you have or can get and use bcpkix and bcprov from https://www.BouncyCastle.org , they include routines to handle this OpenSSL PEM format (and many others):

        // assumes filename in args[0], adjust as needed
        Object both = new PEMParser(new FileReader(args[0])).readObject();
        // should close the FileReader, maybe using try-resources
        byte[] cert = ((X509TrustedCertificateBlock)both).getCertificateHolder().getEncoded();
        X509Certificate good = (X509Certificate) CertificateFactory.getInstance("X.509")
                .generateCertificate(new ByteArrayInputStream(cert));
        System.out.println (good.getSubjectX500Principal().getName());

Otherwise, it is possible to decompose the DER by hand but clumsy and unrobust:

        String in1 = new String(Files.readAllBytes(new File(args[0]).toPath()));
        byte[] both = Base64.getMimeDecoder().decode(in1.replaceAll("-----[A-Z ]*-----\\r?\\n",""));
        if( both[0]!=0x30 || both[1]!=(byte)0x82 ) throw new Exception("wrong!"); // or other handling
        byte[] cert = Arrays.copyOf(both, (both[2]<<8 | both[3]&0xFF) + 4);
        X509Certificate good = (X509Certificate) CertificateFactory.getInstance("X.509")
                .generateCertificate(new ByteArrayInputStream(cert));
        System.out.println (good.getSubjectX500Principal().getName());

Upvotes: 4

Related Questions