JDK-6322976 : Michelin_PKI_CA Certificate can not be parsed in 1.4.2, works with 5.0
  • Type: Bug
  • Component: security-libs
  • Sub-Component: javax.net.ssl
  • Affected Version: 1.4.2_11
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: solaris_9
  • CPU: generic
  • Submitted: 2005-09-13
  • Updated: 2010-04-03
  • Resolved: 2005-09-15
Related Reports
Relates :  
Description
Customer provided certificate isn't parseable with 1.4.2, but reads fine in 5.0.

Please extract Michelin_PKI_CA.cer from the attachment and run 1.4.2 keytool :

C:\j2sdk1.4.2\bin\keytool -printcert -file Michelin_PKI_CA.cer 

sun.security.pkcs.ParsingException: X509.ObjectIdentifier() -- data isn't an object ID (tag = 48)
	at sun.security.pkcs.PKCS7.parse(PKCS7.java:118)
	at sun.security.pkcs.PKCS7.<init>(PKCS7.java:68)
	at sun.security.provider.X509Factory.parseX509orPKCS7Cert(X509Factory.java:530)
	at sun.security.provider.X509Factory.engineGenerateCertificates(X509Factory.java:407)
	at java.security.cert.CertificateFactory.generateCertificates(CertificateFactory.java:511)
	at sun.security.tools.KeyTool.doPrintCert(KeyTool.java:1021)
	at sun.security.tools.KeyTool.doCommands(KeyTool.java:539)
	at sun.security.tools.KeyTool.run(KeyTool.java:124)
	at sun.security.tools.KeyTool.main(KeyTool.java:118)
Caused by: java.io.IOException: X509.ObjectIdentifier() -- data isn't an object ID (tag = 48)
	at sun.security.util.ObjectIdentifier.<init>(ObjectIdentifier.java:134)
	at sun.security.util.DerInputStream.getOID(DerInputStream.java:250)
	at sun.security.pkcs.ContentInfo.<init>(ContentInfo.java:120)
	at sun.security.pkcs.PKCS7.parse(PKCS7.java:136)
	at sun.security.pkcs.PKCS7.parse(PKCS7.java:115)
	... 8 more

In 1.5 everything works fine:

keytool -printcert -file Michelin_PKI_CA.cer

Owner: CN=Michelin_PKI_CA, O=MICHELIN, C=FR
Issuer: CN=Michelin_PKI_CA, O=MICHELIN, C=FR
Serial number: 0
Valid Wrom: TLBXFGGMEPYOQKEDOTWFAOBUZXUWLSZLKBRNVWWCUFPEGAUTFJMVRESKPNKMBIPB
Certificate fingerprints:
	 MD5:  8E:E6:5E:54:97:0E:DA:E9:12:65:7C:E1:C3:8A:5B:C6
	 SHA1: B4:C2:C5:17:91:3D:2F:32:10:AB:2D:5A:99:5A:08:5C:10:4F:3E:2B

Customer provided a test case to get to the root of the issue (test case based on internal Sun API's) - below, src and binary attached.

To run test case use 1.4.2 and execute:

java test1 


import java.io.*;
import sun.security.pkcs.*;
import sun.security.util.*;
import sun.security.x509.*;
import java.security.*;
import java.security.spec.*;
import sun.misc.*;

class test1
{
    public static void main ( String[] args ) throws Exception
    {
	FileInputStream input_stream =
		new FileInputStream ( "Michelin_PKI_CA.cer" );

	System.out.println ( "input_stream " + input_stream );

	int a = input_stream.available();

	System.out.println ( "available " + a );

	byte[] bytes = new byte[a];

	int n = input_stream.read ( bytes );

	System.out.println ( "n " + n );

	InputStream is1 = new ByteArrayInputStream(bytes);

	System.out.println ( "is1 " + is1 );

	bytes = base64_to_binary(is1);

	System.out.println ( "bytes " + bytes );

	int len = bytes.length;

	System.out.println ( "len " + len );
/*
	for ( int i = 0; i < len; i++ )
	{
	    System.out.printf ( "%02x ", bytes[i] );
	}
	System.out.println ();
*/
	InputStream is2 = new ByteArrayInputStream(bytes);

	System.out.println ( "is2 " + is2 );
/*
	PKCS7 p = new PKCS7 ( is2 );

	System.out.println ( "p " + p );

	DerInputStream derin = new DerInputStream ( bytes );

	System.out.println ( "derin " + derin );

	ContentInfo info = new ContentInfo ( derin, false );

	System.out.println ( "info " + info );
*/
	DerValue dv = new DerValue (is2 );

	System.out.println ( "dv " + dv );
/*
	X509CertImpl xci = new X509CertImpl ( dv );

	System.out.println ( "xci " + xci );
*/
	DerValue seq = dv.data.getDerValue();

	System.out.println ( "seq " + seq );
/*
	X509CertInfo certInfo = new X509CertInfo ( seq );

	System.out.println ( "certInfo " + certInfo );
*/
	DerInputStream in = seq.data;

	System.out.println ( "in " + in );

	DerValue tmp = in.getDerValue();
	System.out.println ( "tmp " + tmp );
	if ( tmp.isContextSpecific((byte)0)) {
	    tmp = in.getDerValue();
	    System.out.println ( "tmp " + tmp );
	}
	CertificateSerialNumber serialNum = new CertificateSerialNumber(tmp);
	System.out.println ( "serialNum " + serialNum );

	CertificateAlgorithmId algId = new CertificateAlgorithmId(in);
	System.out.println ( "algId " + algId );

	CertificateIssuerName issuer = new CertificateIssuerName(in);
	System.out.println ( "issuer " + issuer );

	CertificateValidity interval = new CertificateValidity(in);
	System.out.println ( "interval " + interval );

	CertificateSubjectName subject = new CertificateSubjectName(in);
	System.out.println ( "subject " + subject );
/*
	CertificateX509Key pubkey = new CertificateX509Key ( in );
	System.out.println ( "pubkey " + pubkey );
*/
	DerValue val = in.getDerValue();
	System.out.println ( "val " + val );
/*
	PublicKey key = X509Key.parse(val);
	System.out.println ( "key " + key );
*/
	DerValue tmp_dv = val.data.getDerValue();
	System.out.println ( "tmp_dv " + tmp_dv );

	AlgorithmId algorithm = AlgorithmId.parse(tmp_dv);
	System.out.println ( "algorithm " + algorithm );

	BitArray ba = val.data.getUnalignedBitString();
	//System.out.println ( "ba " + ba );

	DerOutputStream x509EncodedKeyStream = new DerOutputStream();
	System.out.println ( "dos " + x509EncodedKeyStream );

	encode ( x509EncodedKeyStream, algorithm, ba );

	bytes = x509EncodedKeyStream.toByteArray();
	System.out.println ( "bytes " + bytes );

	X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec ( bytes );
	System.out.println ( "x509KeySpec " + x509KeySpec );

	String name = algorithm.getName();
	System.out.println ( "name " + name );

	KeyFactory keyFac = KeyFactory.getInstance ( name );
	System.out.println ( "keyFac " + keyFac );

	PublicKey key = keyFac.generatePublic ( x509KeySpec );
	System.out.println ( "key " + key );
    }

    /*
     * Produce SubjectPublicKey encoding from algorithm id and key material.
     */
    static void encode(DerOutputStream out, AlgorithmId algid, BitArray key)
        throws IOException {
            DerOutputStream tmp = new DerOutputStream();
            algid.encode(tmp);
            tmp.putUnalignedBitString(key);
            out.write(DerValue.tag_Sequence, tmp);
    }

    /*
     * Converts a Base64-encoded X.509 certificate or X.509 CRL or PKCS#7 data
     * to binary encoding.
     * In all cases, the data must be bounded at the beginning by
     * "-----BEGIN", and must be bounded at the end by "-----END".
     */
    private static byte[] base64_to_binary(InputStream is)
        throws IOException
    {
        long len = 0; // total length of base64 encoding, including boundaries

        is.mark(is.available());

        BufferedInputStream bufin = new BufferedInputStream(is);
        BufferedReader br = new BufferedReader(new InputStreamReader(bufin));

        // First read all of the data that is found between
        // the "-----BEGIN" and "-----END" boundaries into a buffer.
        String temp;
        if ((temp=readLine(br))==null || !temp.startsWith("-----BEGIN")) {
            throw new IOException("Unsupported encoding");
        } else {
            len += temp.length();
        }
        StringBuffer strBuf = new StringBuffer();
        while ((temp=readLine(br))!=null && !temp.startsWith("-----END")) {
            strBuf.append(temp);
        }
        if (temp == null) {
            throw new IOException("Unsupported encoding");
        } else {
            len += temp.length();
        }

        // consume only as much as was needed
        len += strBuf.length();
        is.reset();
        is.skip(len);

        // Now, that data is supposed to be a single X.509 certificate or
        // X.509 CRL or PKCS#7 formatted data... Base64 encoded.
        // Decode into binary and return the result.
        BASE64Decoder decoder = new BASE64Decoder();
        return decoder.decodeBuffer(strBuf.toString());
    }

    private static final int defaultExpectedLineLength = 80;
    private static final char[] endBoundary = "-----END".toCharArray();

    /*
     * Read a line of text.  A line is considered to be terminated by any one
     * of a line feed ('\n'), a carriage return ('\r'), a carriage return
     * followed immediately by a linefeed, or an end-of-certificate marker.
     *
     * @return     A String containing the contents of the line, including
     *             any line-termination characters, or null if the end of the
     *             stream has been reached.
     */
    private static String readLine(BufferedReader br) throws IOException {
        int c;
        int i = 0;
        boolean isMatch = true;
        boolean matched = false;
        StringBuffer sb = new StringBuffer(defaultExpectedLineLength);
        do {
            c = br.read();
            if (isMatch && (i < endBoundary.length)) {
                isMatch = ((char)c != endBoundary[i++]) ? false : true;
            }
            if (!matched)
                matched = (isMatch && (i == endBoundary.length));
            sb.append((char)c);
        } while ((c != -1) && (c != '\n') && (c != '\r'));

        if (!matched && c == -1) {
            return null;
        }
        if (c == '\r') {
            br.mark(1);
            int c2 = br.read();
            if (c2 == '\n') {
                sb.append((char)c);
            } else {
                br.reset();
            }
        }
        return sb.toString();
    }
}
Kirill added privately that:

I just realized that I in fact failed to enclose the stack trace for the customer's test case which comes out of jsse.jar :

Exception in thread "main" java.security.spec.InvalidKeySpecException:

> Unknown key spec: Invalid RSA modulus size.
>        at
> com.sun.net.ssl.internal.ssl.JS_KeyFactory.engineGeneratePublic(DashoA
> 12
> 275)
>        at java.security.KeyFactory.generatePublic(KeyFactory.java:221)
>        at test1.main(test1.java:138)

So, the root cause seems to be in jsse.

Comments
EVALUATION This is the same issue as 4524097 / 4853305, the 2048 bit key length limitation in the JSafe RSA code.
15-09-2005