Java Certificate Parsing

(...or why keytool is ruining your life)

Chris W. Johnson
February 14, 2011

Suppose you want to load a certificate into a keystore using the Java "keytool" utility, but it won’t work. Perhaps you’re even trying to load a certificate chain and you’ve followed the helpful documentation for "OPTION 1" in Keytool and the "Failed to establish chain from reply" Error, and that won’t work. Maybe you’re trying to do this programmatically, and are calling the generateCertificate method of java.security.cert.CertificateFactory. This document may be able to help you.

This document deals with the problems I encountered while trying to load a chain of Base64 encoded X.509 certificates using keytool and Java 2 Standard Edition, version 1.4.2_03, though I expect these problems are present in other releases. The problems all turn out to be in the certificate parsing code of Java’s sun.security.provider.X509Factory class, specifically the methods parseX509orPKCS7Cert and isBase64 (you are likely to be seeing the former in your stack traces, if the problems discussed below are relevant to you). As a result, any code, including keytool, that calls those methods directly, or indirectly, will suffer from these problems.

I hope this document will spare you the protracted suffering that led me to write it.

Example Certificate Chain

Here’s an example chain of Base64 encoded X.509 certificates, formatted exactly as Sun’s document Keytool and the "Failed to establish chain from reply" Error specifies in it’s "OPTION 1" section:

ROOT CA:

-----BEGIN CERTIFICATE-----
MIICxDCCAi0CBECcV/wwDQYJKoZIhvcNAQEEBQAwgagxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIEwVU
ZXhhczEPMA0GA1UEBxMGQXVzdGluMSowKAYDVQQKEyFUaGUgVW5pdmVyc2l0eSBvZiBUZXhhcyBh
dCBBdXN0aW4xKDAmBgNVBAsTH0luZm9ybWF0aW9uIFRlY2hub2xvZ3kgU2VydmljZXMxIjAgBgNV
BAMTGXhtbGdhdGV3YXkuaXRzLnV0ZXhhcy5lZHUwHhcNMDQwNTA4MDM0NjA0WhcNMDQwODA2MDM0
NjA0WjCBqDELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMQ8wDQYDVQQHEwZBdXN0aW4xKjAo
BgNVBAoTIVRoZSBVbml2ZXJzaXR5IG9mIFRleGFzIGF0IEF1c3RpbjEoMCYGA1UECxMfSW5mb3Jt
YXRpb24gVGVjaG5vbG9neSBTZXJ2aWNlczEiMCAGA1UEAxMZeG1sZ2F0ZXdheS5pdHMudXRleGFz
LmVkdTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsmc+6+NjLmanvh+FvBziYdBwTiz+d/DZ
Uy2jyvij6f8Xly6zkhHLSsuBzw08wPzr2K+F359bf9T3uiZMuao//FBGtDrTYpvQwkn4PFZwSeY2
Ynw4edxp1JEWT2zfOY+QJDfNgpsYQ9hrHDwqnpbMVVqjdBq5RgTKGhFBj9kxEq0CAwEAATANBgkq
hkiG9w0BAQQFAAOBgQCPYGXF6oRbnjti3CPtjfwORoO7ab1QzNS9Z2rLMuPnt6POlm1A3UPEwCS8
6flTlAqg19Sh47H7+Iq/LuzotKvUE5ugK52QRNMa4c0OSaO5UEM5EfVox1pT9tZV1Z3whYYMhThg
oC4y/On0NUVMN5xfF/GpSACga/bVjoNvd8HWEg==
-----END CERTIFICATE-----

ISSUER CA SIGNED CERTIFICATE:

-----BEGIN CERTIFICATE-----
MIIC/TCCAmagAwIBAgIBKjANBgkqhkiG9w0BAQQFADCBqDEiMCAGA1UEAxMZeG1sZ2F0ZXdheS5p
dHMudXRleGFzLmVkdTEoMCYGA1UECxMfSW5mb3JtYXRpb24gVGVjaG5vbG9neSBTZXJ2aWNlczEq
MCgGA1UEChMhVGhlIFVuaXZlcnNpdHkgb2YgVGV4YXMgYXQgQXVzdGluMQ8wDQYDVQQHEwZBdXN0
aW4xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVUzAeFw0wNDA1MDkwNTMwMTBaFw0wNTA1MDQw
NTMwMTBaMIGAMQswCQYDVQQGEwJVUzEOMAwGA1UECBMFVGV4YXMxDzANBgNVBAcTBkF1c3RpbjEq
MCgGA1UEChMhVGhlIFVuaXZlcnNpdHkgb2YgVGV4YXMgYXQgQXVzdGluMRMwEQYDVQQLEwpUb29s
cyBUZWFtMQ8wDQYDVQQDEwZDbGllbnQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ6PABjb
zXUkgo29S4uv1Qz9reo1/tP4pkQTGAldSbtA4hVtA/3sjw2+u3kgxYruAi2cXV2k0RPZhsUZjlDk
jMPb/dlY81bD8gqe3lu3ezugJrlArlpfWN6PlufbTjxHSqIA0XD9R5/ZECaUV9dD43K5KdWUCy99
YKDiSwVPO9F5AgMBAAGjXTBbMB0GA1UdDgQWBBRkCCpscEXxXu8Ba67p6zdh13ypjzAfBgNVHSME
GDAWgBR2RsZH2kSY782kBROo92FAWS6sADAJBgNVHRMEAjAAMA4GA1UdDwQHAwUBEiRIkDANBgkq
hkiG9w0BAQQFAAOBgQCtV1NzpdVBs5vyb8yLXNA3hA1LsmE/2QanXG4T3UN93BI4HQzx0idnkN1Y
0RAQ1rjGeQ1pk3l2DWsPi9mTkCGmYs/EMLkKOBee9ad3BIG6sKwXgbgLyNLgda+Y1bo+SIomq/a7
yP92UHMFEegfS/ssECA+Q3hHuU6in3AqLfWH1w==
-----END CERTIFICATE-----

Understand up front that the way the sun.security.provider.X509Factory class is implemented as of J2SE 1.4.2_03, you will never get such a file to load, and you’ll be lucky to get any single certificate to load, because the X509Factory class employs a parser that only its mother could love.

To make a long story short, the file should actually be formatted as shown below. Note that there can be no blank lines at all; not at the top, not between certificates, and not after the final certificate. None. (Also, the order of the certificates makes no difference to keytool, because it sorts them appropriately before attempting to place them in a keystore.)

-----BEGIN CERTIFICATE-----
MIICxDCCAi0CBECcV/wwDQYJKoZIhvcNAQEEBQAwgagxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIEwVU
ZXhhczEPMA0GA1UEBxMGQXVzdGluMSowKAYDVQQKEyFUaGUgVW5pdmVyc2l0eSBvZiBUZXhhcyBh
dCBBdXN0aW4xKDAmBgNVBAsTH0luZm9ybWF0aW9uIFRlY2hub2xvZ3kgU2VydmljZXMxIjAgBgNV
BAMTGXhtbGdhdGV3YXkuaXRzLnV0ZXhhcy5lZHUwHhcNMDQwNTA4MDM0NjA0WhcNMDQwODA2MDM0
NjA0WjCBqDELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMQ8wDQYDVQQHEwZBdXN0aW4xKjAo
BgNVBAoTIVRoZSBVbml2ZXJzaXR5IG9mIFRleGFzIGF0IEF1c3RpbjEoMCYGA1UECxMfSW5mb3Jt
YXRpb24gVGVjaG5vbG9neSBTZXJ2aWNlczEiMCAGA1UEAxMZeG1sZ2F0ZXdheS5pdHMudXRleGFz
LmVkdTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsmc+6+NjLmanvh+FvBziYdBwTiz+d/DZ
Uy2jyvij6f8Xly6zkhHLSsuBzw08wPzr2K+F359bf9T3uiZMuao//FBGtDrTYpvQwkn4PFZwSeY2
Ynw4edxp1JEWT2zfOY+QJDfNgpsYQ9hrHDwqnpbMVVqjdBq5RgTKGhFBj9kxEq0CAwEAATANBgkq
hkiG9w0BAQQFAAOBgQCPYGXF6oRbnjti3CPtjfwORoO7ab1QzNS9Z2rLMuPnt6POlm1A3UPEwCS8
6flTlAqg19Sh47H7+Iq/LuzotKvUE5ugK52QRNMa4c0OSaO5UEM5EfVox1pT9tZV1Z3whYYMhThg
oC4y/On0NUVMN5xfF/GpSACga/bVjoNvd8HWEg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIC/TCCAmagAwIBAgIBKjANBgkqhkiG9w0BAQQFADCBqDEiMCAGA1UEAxMZeG1sZ2F0ZXdheS5p
dHMudXRleGFzLmVkdTEoMCYGA1UECxMfSW5mb3JtYXRpb24gVGVjaG5vbG9neSBTZXJ2aWNlczEq
MCgGA1UEChMhVGhlIFVuaXZlcnNpdHkgb2YgVGV4YXMgYXQgQXVzdGluMQ8wDQYDVQQHEwZBdXN0
aW4xDjAMBgNVBAgTBVRleGFzMQswCQYDVQQGEwJVUzAeFw0wNDA1MDkwNTMwMTBaFw0wNTA1MDQw
NTMwMTBaMIGAMQswCQYDVQQGEwJVUzEOMAwGA1UECBMFVGV4YXMxDzANBgNVBAcTBkF1c3RpbjEq
MCgGA1UEChMhVGhlIFVuaXZlcnNpdHkgb2YgVGV4YXMgYXQgQXVzdGluMRMwEQYDVQQLEwpUb29s
cyBUZWFtMQ8wDQYDVQQDEwZDbGllbnQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ6PABjb
zXUkgo29S4uv1Qz9reo1/tP4pkQTGAldSbtA4hVtA/3sjw2+u3kgxYruAi2cXV2k0RPZhsUZjlDk
jMPb/dlY81bD8gqe3lu3ezugJrlArlpfWN6PlufbTjxHSqIA0XD9R5/ZECaUV9dD43K5KdWUCy99
YKDiSwVPO9F5AgMBAAGjXTBbMB0GA1UdDgQWBBRkCCpscEXxXu8Ba67p6zdh13ypjzAfBgNVHSME
GDAWgBR2RsZH2kSY782kBROo92FAWS6sADAJBgNVHRMEAjAAMA4GA1UdDwQHAwUBEiRIkDANBgkq
hkiG9w0BAQQFAAOBgQCtV1NzpdVBs5vyb8yLXNA3hA1LsmE/2QanXG4T3UN93BI4HQzx0idnkN1Y
0RAQ1rjGeQ1pk3l2DWsPi9mTkCGmYs/EMLkKOBee9ad3BIG6sKwXgbgLyNLgda+Y1bo+SIomq/a7
yP92UHMFEegfS/ssECA+Q3hHuU6in3AqLfWH1w==
-----END CERTIFICATE-----

Specific Exceptions and Their Causes

Because the certificate "parser" is hopelessly inadequate to its job, the exceptions thrown when it can’t handle its input do not describe the actual problem with the input. Indeed, by the time it throws an exception it has gone so far down the wrong path that the exception thrown is actively misleading. Here are the exceptions you are most likely to encounter, along with their actual meanings in this context.

sun.security.pkcs.ParsingException: Sequence tag error

The "parser" expects the first ten bytes of the file to be "-----BEGIN" (and it imposes the same test on the ten bytes following each "-----END" line). Anything, including blank lines, will run afoul of this expectation. Remove all blank lines from the file, and all text other than the "-----BEGIN" and "-----END" lines, and the Base64-encoded text between them.

Unable to initialize, java.io.IOException: insufficient data

When reading a file containing a certificate chain, like the example at the beginning of this document, this exception is thrown if the first ten bytes following an "-----END" line are anything other than "-----BEGIN". So, eliminate everything between the certificates, including blank lines.

java.io.IOException: DerInputStream.getLength(): lengthTag=127, too big.

There are extra characters at the end of the certificate file which the "parser" is attempting to interpret as the start of another certificate. The most common way to encounter this error is to have one, or more, blank lines at the end of the certificate file. A line termination sequence is permitted (but not required) at the end of the final "-----END" line, but there can be no more than one. So, delete everything after the final "-----END" line.

Uncovering the Causes of Other Exceptions

The causes of the exceptions dicussed above were uncovered by donwloading the Java source code, using the exception’s stack trace to identify the class that threw it, and working backward from there down the stack trace until the original wrong turn in the code was found. I suggest this approach to anyone attempting to deal with similarly incomprehensible or incorrect exceptions. If you can setup the source code in an IDE and run keytool in its debugger, so much the better. At present, the Java source code can be downloaded from the Java SE Downloads page.

Relevant Documentation

Document Change History

Valid XHTML 1.0 Strict Valid CSS!