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