CertificateFactoryTest.java revision e21b3caf3fb4e3e3d9244a000669a547621c16bd
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package libcore.java.security.cert;
18
19import com.android.org.bouncycastle.asn1.x509.BasicConstraints;
20import com.android.org.bouncycastle.asn1.x509.X509Extensions;
21import com.android.org.bouncycastle.x509.X509V3CertificateGenerator;
22import com.android.org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
23import com.android.org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
24
25import java.io.ByteArrayInputStream;
26import java.io.ByteArrayOutputStream;
27import java.io.IOException;
28import java.io.InputStream;
29import java.io.ObjectInputStream;
30import java.io.ObjectOutputStream;
31import java.io.OptionalDataException;
32import java.io.StreamCorruptedException;
33import java.math.BigInteger;
34import java.security.KeyPair;
35import java.security.KeyPairGenerator;
36import java.security.PrivateKey;
37import java.security.Provider;
38import java.security.Security;
39import java.security.cert.CertPath;
40import java.security.cert.Certificate;
41import java.security.cert.CertificateEncodingException;
42import java.security.cert.CertificateException;
43import java.security.cert.CertificateFactory;
44import java.security.cert.X509Certificate;
45import java.util.ArrayList;
46import java.util.Arrays;
47import java.util.Date;
48import java.util.GregorianCalendar;
49import java.util.Iterator;
50import java.util.List;
51import java.util.TimeZone;
52
53import javax.security.auth.x500.X500Principal;
54
55import junit.framework.TestCase;
56import libcore.java.security.StandardNames;
57
58public class CertificateFactoryTest extends TestCase {
59
60    private static final String VALID_CERTIFICATE_PEM =
61            "-----BEGIN CERTIFICATE-----\n"
62            + "MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBM\n"
63            + "MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg\n"
64            + "THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0x\n"
65            + "MTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh\n"
66            + "MRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcw\n"
67            + "FQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC\n"
68            + "gYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jN\n"
69            + "gtXj9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L\n"
70            + "05vuuWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAM\n"
71            + "BgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3Rl\n"
72            + "LmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUF\n"
73            + "BwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRw\n"
74            + "Oi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0\n"
75            + "ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUF\n"
76            + "AAOBgQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5\n"
77            + "u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6\n"
78            + "z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw==\n"
79            + "-----END CERTIFICATE-----\n";
80
81    private static final String VALID_CERTIFICATE_PEM_CRLF =
82            "-----BEGIN CERTIFICATE-----\r\n"
83            + "MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBM\r\n"
84            + "MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg\r\n"
85            + "THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0x\r\n"
86            + "MTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh\r\n"
87            + "MRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcw\r\n"
88            + "FQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC\r\n"
89            + "gYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jN\r\n"
90            + "gtXj9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L\r\n"
91            + "05vuuWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAM\r\n"
92            + "BgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3Rl\r\n"
93            + "LmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUF\r\n"
94            + "BwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRw\r\n"
95            + "Oi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0\r\n"
96            + "ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUF\r\n"
97            + "AAOBgQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5\r\n"
98            + "u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6\r\n"
99            + "z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw==\r\n"
100            + "-----END CERTIFICATE-----\r\n";
101
102    private static final byte[] VALID_CERTIFICATE_PEM_HEADER = "-----BEGIN CERTIFICATE-----\n"
103            .getBytes();
104
105    private static final byte[] VALID_CERTIFICATE_PEM_DATA =
106             ("MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBM"
107            + "MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg"
108            + "THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0x"
109            + "MTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh"
110            + "MRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcw"
111            + "FQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC"
112            + "gYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jN"
113            + "gtXj9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L"
114            + "05vuuWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAM"
115            + "BgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3Rl"
116            + "LmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUF"
117            + "BwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRw"
118            + "Oi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0"
119            + "ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUF"
120            + "AAOBgQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5"
121            + "u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6"
122            + "z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw==").getBytes();
123
124    private static final byte[] VALID_CERTIFICATE_PEM_FOOTER = "\n-----END CERTIFICATE-----\n"
125            .getBytes();
126
127    private static final String INVALID_CERTIFICATE_PEM =
128            "-----BEGIN CERTIFICATE-----\n"
129            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
130            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
131            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
132            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
133            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
134            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
135            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
136            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
137            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
138            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
139            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
140            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
141            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
142            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
143            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
144            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
145            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
146            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
147            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
148            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
149            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
150            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
151            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
152            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
153            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
154            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
155            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
156            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
157            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
158            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
159            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
160            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
161            + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
162            + "AAAAAAAA\n"
163            + "-----END CERTIFICATE-----";
164
165    public void test_generateCertificate() throws Exception {
166        Provider[] providers = Security.getProviders("CertificateFactory.X509");
167        for (Provider p : providers) {
168            CertificateFactory cf = CertificateFactory.getInstance("X509", p);
169            try {
170                test_generateCertificate(cf);
171                test_generateCertificate_InputStream_Offset_Correct(cf);
172                test_generateCertificate_InputStream_Empty(cf);
173                test_generateCertificate_InputStream_InvalidStart_Failure(cf);
174                test_generateCertificate_AnyLineLength_Success(cf);
175            } catch (Throwable e) {
176                throw new Exception("Problem testing " + p.getName(), e);
177            }
178        }
179    }
180
181    private void test_generateCertificate(CertificateFactory cf) throws Exception {
182        {
183            byte[] valid = VALID_CERTIFICATE_PEM.getBytes();
184            Certificate c = cf.generateCertificate(new ByteArrayInputStream(valid));
185            assertNotNull(c);
186        }
187
188        {
189            byte[] valid = VALID_CERTIFICATE_PEM_CRLF.getBytes();
190            Certificate c = cf.generateCertificate(new ByteArrayInputStream(valid));
191            assertNotNull(c);
192        }
193
194        try {
195            byte[] invalid = INVALID_CERTIFICATE_PEM.getBytes();
196            cf.generateCertificate(new ByteArrayInputStream(invalid));
197            fail();
198        } catch (CertificateException expected) {
199        }
200    }
201
202    /*
203     * Checks all possible line lengths for PEM input data.
204     */
205    private void test_generateCertificate_AnyLineLength_Success(CertificateFactory cf)
206            throws Exception {
207        // RI barfs on this
208        if (StandardNames.IS_RI) {
209            return;
210        }
211
212        int lineLength = 1;
213        int maxLineLength = VALID_CERTIFICATE_PEM_DATA.length;
214
215        ByteArrayOutputStream baos = new ByteArrayOutputStream();
216        baos.write(VALID_CERTIFICATE_PEM_HEADER);
217        int offset = 0;
218        while (lineLength < (maxLineLength - 4)) {
219            int end = offset + lineLength;
220            if (end > VALID_CERTIFICATE_PEM_DATA.length) {
221                end = VALID_CERTIFICATE_PEM_DATA.length;
222            }
223            baos.write(Arrays.copyOfRange(VALID_CERTIFICATE_PEM_DATA, offset, end));
224            baos.write('\n');
225            offset += lineLength;
226            if (offset >= maxLineLength) {
227                baos.write(VALID_CERTIFICATE_PEM_FOOTER);
228                try {
229                    Certificate c =
230                            cf.generateCertificate(new ByteArrayInputStream(baos.toByteArray()));
231                    assertNotNull(c);
232                } catch (Exception e) {
233                    throw new Exception("Fail at line length " + lineLength, e);
234                }
235                baos.reset();
236                baos.write(VALID_CERTIFICATE_PEM_HEADER);
237                offset = 0;
238            } else {
239                lineLength++;
240            }
241        }
242
243    }
244
245    private void test_generateCertificate_InputStream_Empty(CertificateFactory cf) throws Exception {
246        try {
247            Certificate c = cf.generateCertificate(new ByteArrayInputStream(new byte[0]));
248            if (!"BC".equals(cf.getProvider().getName())) {
249                fail("should throw CertificateException: " + cf.getProvider().getName());
250            }
251            assertNull(c);
252        } catch (CertificateException e) {
253            if ("BC".equals(cf.getProvider().getName())) {
254                fail("should return null: " + cf.getProvider().getName());
255            }
256        }
257    }
258
259    private void test_generateCertificate_InputStream_InvalidStart_Failure(CertificateFactory cf)
260            throws Exception {
261        try {
262            Certificate c = cf.generateCertificate(new ByteArrayInputStream(
263                    "-----BEGIN CERTIFICATE-----".getBytes()));
264            if (!"BC".equals(cf.getProvider().getName())) {
265                fail("should throw CertificateException: " + cf.getProvider().getName());
266            }
267            assertNull(c);
268        } catch (CertificateException expected) {
269            if ("BC".equals(cf.getProvider().getName())) {
270                fail("should return null: " + cf.getProvider().getName());
271            }
272        }
273    }
274
275    private void test_generateCertificate_InputStream_Offset_Correct(CertificateFactory cf)
276            throws Exception {
277        byte[] valid = VALID_CERTIFICATE_PEM.getBytes();
278
279        byte[] doubleCertificateData = new byte[valid.length * 2];
280        System.arraycopy(valid, 0, doubleCertificateData, 0, valid.length);
281        System.arraycopy(valid, 0, doubleCertificateData, valid.length, valid.length);
282        MeasuredInputStream certStream = new MeasuredInputStream(new ByteArrayInputStream(
283                doubleCertificateData));
284        Certificate certificate = cf.generateCertificate(certStream);
285        assertNotNull(certificate);
286        assertEquals(valid.length, certStream.getCount());
287    }
288
289    /**
290     * Proxy that counts the number of bytes read from an InputStream.
291     */
292    private static class MeasuredInputStream extends InputStream {
293        private long mCount = 0;
294
295        private long mMarked = 0;
296
297        private InputStream mStream;
298
299        public MeasuredInputStream(InputStream is) {
300            mStream = is;
301        }
302
303        public long getCount() {
304            return mCount;
305        }
306
307        @Override
308        public int read() throws IOException {
309            int nextByte = mStream.read();
310            mCount++;
311            return nextByte;
312        }
313
314        @Override
315        public int read(byte[] buffer) throws IOException {
316            int count = mStream.read(buffer);
317            mCount += count;
318            return count;
319        }
320
321        @Override
322        public int read(byte[] buffer, int offset, int length) throws IOException {
323            int count = mStream.read(buffer, offset, length);
324            mCount += count;
325            return count;
326        }
327
328        @Override
329        public long skip(long byteCount) throws IOException {
330            long count = mStream.skip(byteCount);
331            mCount += count;
332            return count;
333        }
334
335        @Override
336        public int available() throws IOException {
337            return mStream.available();
338        }
339
340        @Override
341        public void close() throws IOException {
342            mStream.close();
343        }
344
345        @Override
346        public void mark(int readlimit) {
347            mMarked = mCount;
348            mStream.mark(readlimit);
349        }
350
351        @Override
352        public boolean markSupported() {
353            return mStream.markSupported();
354        }
355
356        @Override
357        public synchronized void reset() throws IOException {
358            mCount = mMarked;
359            mStream.reset();
360        }
361    }
362
363    /* CertPath tests */
364    public void testGenerateCertPath() throws Exception {
365        KeyHolder ca = generateCertificate(true, null);
366        KeyHolder cert1 = generateCertificate(true, ca);
367        KeyHolder cert2 = generateCertificate(false, cert1);
368        KeyHolder cert3 = generateCertificate(false, cert2);
369
370        List<X509Certificate> certs = new ArrayList<X509Certificate>();
371        certs.add(cert3.certificate);
372        certs.add(cert2.certificate);
373        certs.add(cert1.certificate);
374
375        List<X509Certificate> duplicatedCerts = new ArrayList<X509Certificate>(certs);
376        duplicatedCerts.add(cert2.certificate);
377
378        Provider[] providers = Security.getProviders("CertificateFactory.X509");
379        for (Provider p : providers) {
380            final CertificateFactory cf = CertificateFactory.getInstance("X.509", p);
381
382            // Duplicate certificates can cause an exception.
383            {
384                final CertPath duplicatedPath = cf.generateCertPath(duplicatedCerts);
385                try {
386                    duplicatedPath.getEncoded();
387                    if (StandardNames.IS_RI) {
388                        fail("duplicate certificates should cause failure: " + p.getName());
389                    }
390                } catch (CertificateEncodingException expected) {
391                    if (!StandardNames.IS_RI) {
392                        fail("duplicate certificates should pass: " + p.getName());
393                    }
394                }
395            }
396
397            testCertPathEncoding(cf, certs, null);
398
399            /* Make sure all encoding entries are the same. */
400            final Iterator<String> it1 = cf.getCertPathEncodings();
401            final Iterator<String> it2 = cf.generateCertPath(certs).getEncodings();
402            for (;;) {
403                assertEquals(p.getName(), it1.hasNext(), it2.hasNext());
404                if (!it1.hasNext()) {
405                    break;
406                }
407
408                String encoding = it1.next();
409                assertEquals(p.getName(), encoding, it2.next());
410
411                try {
412                    it1.remove();
413                    fail("Should not be able to remove from iterator");
414                } catch (UnsupportedOperationException expected) {
415                }
416
417                try {
418                    it2.remove();
419                    fail("Should not be able to remove from iterator");
420                } catch (UnsupportedOperationException expected) {
421                }
422
423                /* Now test using this encoding. */
424                testCertPathEncoding(cf, certs, encoding);
425            }
426        }
427    }
428
429    private void testCertPathEncoding(CertificateFactory cf, List<X509Certificate> expectedCerts,
430            String encoding) throws Exception {
431        final String providerName = cf.getProvider().getName() + "[" + encoding + "]";
432
433        final CertPath pathFromList = cf.generateCertPath(expectedCerts);
434
435        // Create a copy we can modify and discard.
436        final byte[] encodedCopy;
437        if (encoding == null) {
438            encodedCopy = pathFromList.getEncoded();
439            assertNotNull(providerName, encodedCopy);
440
441            // check idempotence
442            assertEquals(providerName, Arrays.toString(pathFromList.getEncoded()),
443                    Arrays.toString(encodedCopy));
444        } else {
445            encodedCopy = pathFromList.getEncoded(encoding);
446            assertNotNull(providerName, encodedCopy);
447
448            // check idempotence
449            assertEquals(providerName, Arrays.toString(pathFromList.getEncoded(encoding)),
450                    Arrays.toString(encodedCopy));
451        }
452
453        // Try to modify byte array.
454        encodedCopy[0] ^= (byte) 0xFF;
455
456        // Get a real copy we will use if the test proceeds.
457        final byte[] encoded;
458        if (encoding == null) {
459            encoded = pathFromList.getEncoded();
460            assertNotNull(providerName, encodedCopy);
461
462            // check idempotence
463            assertEquals(providerName, Arrays.toString(pathFromList.getEncoded()),
464                    Arrays.toString(encoded));
465        } else {
466            encoded = pathFromList.getEncoded(encoding);
467            assertNotNull(providerName, encodedCopy);
468
469            // check idempotence
470            assertEquals(providerName, Arrays.toString(pathFromList.getEncoded(encoding)),
471                    Arrays.toString(encoded));
472        }
473        assertFalse(providerName, Arrays.toString(encoded).equals(Arrays.toString(encodedCopy)));
474
475        encodedCopy[0] ^= (byte) 0xFF;
476        assertEquals(providerName, Arrays.toString(encoded), Arrays.toString(encodedCopy));
477
478        final CertPath actualPath;
479        if (encoding == null) {
480            actualPath = cf.generateCertPath(new ByteArrayInputStream(encoded));
481        } else {
482            actualPath = cf.generateCertPath(new ByteArrayInputStream(encoded), encoding);
483        }
484
485        // PKCS7 certificate bags are not guaranteed to be in order.
486        final List<? extends Certificate> actualCerts;
487        if (!"PKCS7".equals(encoding)) {
488            actualCerts = actualPath.getCertificates();
489            assertEquals(providerName, expectedCerts, actualCerts);
490        } else {
491            actualCerts = pathFromList.getCertificates();
492        }
493
494        try {
495            actualCerts.remove(0);
496            fail("List of certificate should be immutable");
497        } catch (UnsupportedOperationException expected) {
498        }
499
500        ByteArrayOutputStream baos = new ByteArrayOutputStream();
501        ObjectOutputStream oos = new ObjectOutputStream(baos);
502        oos.writeObject(actualPath);
503        oos.close();
504
505        byte[] serialized = baos.toByteArray();
506        ByteArrayInputStream bais = new ByteArrayInputStream(serialized);
507        ObjectInputStream ois = new ObjectInputStream(bais);
508        Object output = ois.readObject();
509        assertTrue(providerName, output instanceof CertPath);
510
511        assertEquals(providerName, actualPath, (CertPath) output);
512    }
513
514    public static class KeyHolder {
515        public X509Certificate certificate;
516
517        public PrivateKey privateKey;
518    }
519
520    @SuppressWarnings("deprecation")
521    private static KeyHolder generateCertificate(boolean isCa, KeyHolder issuer) throws Exception {
522        Date startDate = new Date();
523
524        GregorianCalendar cal = new GregorianCalendar();
525        cal.setTimeZone(TimeZone.getTimeZone("UTC"));
526        cal.set(2100, 0, 1, 0, 0, 0); // Jan 1, 2100 UTC
527        Date expiryDate = cal.getTime();
528
529        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
530        KeyPair keyPair = kpg.generateKeyPair();
531
532        BigInteger serial;
533        X500Principal issuerPrincipal;
534        X500Principal subjectPrincipal;
535        PrivateKey caKey;
536        if (issuer != null) {
537            serial = issuer.certificate.getSerialNumber().add(BigInteger.ONE);
538            subjectPrincipal = new X500Principal("CN=Test Certificate Serial #" + serial.toString());
539            issuerPrincipal = issuer.certificate.getSubjectX500Principal();
540            caKey = issuer.privateKey;
541        } else {
542            serial = BigInteger.ONE;
543            subjectPrincipal = new X500Principal("CN=Test CA, O=Tests, C=US");
544            issuerPrincipal = subjectPrincipal;
545            caKey = keyPair.getPrivate();
546        }
547
548        BasicConstraints basicConstraints;
549        if (isCa) {
550            basicConstraints = new BasicConstraints(10 - serial.intValue());
551        } else {
552            basicConstraints = new BasicConstraints(false);
553        }
554
555        X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
556
557        certGen.setSerialNumber(serial);
558        certGen.setIssuerDN(issuerPrincipal);
559        certGen.setNotBefore(startDate);
560        certGen.setNotAfter(expiryDate);
561        certGen.setSubjectDN(subjectPrincipal);
562        certGen.setPublicKey(keyPair.getPublic());
563        certGen.setSignatureAlgorithm("SHA1withRSA");
564
565        if (issuer != null) {
566            certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
567                    new AuthorityKeyIdentifierStructure(issuer.certificate));
568        } else {
569            certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
570                    new AuthorityKeyIdentifierStructure(keyPair.getPublic()));
571        }
572
573        certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false,
574                new SubjectKeyIdentifierStructure(keyPair.getPublic()));
575        certGen.addExtension(X509Extensions.BasicConstraints, true, basicConstraints);
576
577        X509Certificate cert = certGen.generate(caKey);
578
579        KeyHolder holder = new KeyHolder();
580        holder.certificate = cert;
581        holder.privateKey = keyPair.getPrivate();
582
583        return holder;
584    }
585}
586