1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18/** 19 * @author Alexander V. Astapchuk 20 */ 21 22package org.apache.harmony.security.tests.support; 23 24import java.io.IOException; 25import java.io.InputStream; 26import java.io.ObjectInputStream; 27import java.io.ObjectOutputStream; 28import java.io.Serializable; 29import java.io.StreamCorruptedException; 30import java.math.BigInteger; 31 32import java.security.InvalidKeyException; 33import java.security.NoSuchAlgorithmException; 34import java.security.NoSuchProviderException; 35import java.security.Principal; 36import java.security.Provider; 37import java.security.PublicKey; 38import java.security.Security; 39import java.security.SignatureException; 40 41import java.security.cert.*; 42import java.util.*; 43 44import javax.security.auth.x500.X500Principal; 45 46/** 47 * The class contains various utility methods used during the java.security 48 * classes testing. 49 */ 50 51public final class TestCertUtils { 52 53 private TestCertUtils() { 54 throw new Error("statics only"); 55 } 56 57 /** 58 * Returns new instance of test certificate each time the method is called. 59 * 60 * @return test certificate 61 */ 62 public static Certificate getCert() { 63 return new TestCertificate(); 64 } 65 66 /** 67 * Returns an array of 3 test certificates. IMP: The array returned is not 68 * real chain of certificates, it's just an array of 3 certs. The method 69 * returns new array each time it's called. The number of 3 was chosen 70 * arbitrarily and is subject to change. 71 * 72 * @return an array of 3 certificates 73 */ 74 public static Certificate[] getCertChain() { 75 Certificate[] chain = { new TestCertificate(), new TestCertificate(), 76 new TestCertificate() }; 77 return chain; 78 } 79 80 /** 81 * Returns a test CertPath, which uses getCertChain() to obtain a list of 82 * certificates to store. 83 * 84 * @return test cert path 85 */ 86 public static CertPath getCertPath() { 87 return new TestCertPath(); 88 } 89 90 /** 91 * Generates and returns an instance of TestCertPath.<br> 92 * TestCertificate-s included in the CertPath will be uniq (will have 93 * different numbers passed to their ctor-s).<br> 94 * The second arguments shows which number will have the first Certificate 95 * in the CertPath. The second certificate will have (startID+1) number 96 * and so on. 97 * 98 * @param howMany - shows how many TestCerts must contain the CertPath generated 99 * @param startID - specifies the starting ID which the first certificate will have 100 * @return TestCertPath 101 */ 102 public static CertPath genCertPath(int howMany, int startID) { 103 Certificate[] certs = new Certificate[howMany]; 104 for (int i = 0; i < howMany; i++) { 105 certs[i] = new TestCertificate(Integer.toString(startID + i)); 106 } 107 return new TestCertPath(certs); 108 } 109 110 private static Provider provider = null; 111 112 private static final String providerName = "TstPrvdr"; 113 114 /** 115 * A Principal used to form rootCA's certificate 116 */ 117 public static final X500Principal rootPrincipal = new X500Principal( 118 UniGen.rootName); 119 120 /** 121 * Some fake rootCA's certificate. 122 */ 123 public static final X509Certificate rootCA = new TestX509Certificate( 124 rootPrincipal, rootPrincipal); 125 126 public static void install_test_x509_factory() { 127 if (provider == null) { 128 provider = new TestProvider(providerName, 0.01, 129 "Test provider for serialization testing"); 130 Security.insertProviderAt(provider, 1); 131 } 132 } 133 134 public static void uninstall_test_x509_factory() { 135 if (provider != null) { 136 Security.removeProvider(providerName); 137 provider = null; 138 } 139 } 140 141 /** 142 * The class represents test certificate path. 143 */ 144 145 public static final class TestCertPath extends CertPath implements 146 Serializable { 147 148 private static final byte[] encoded = new byte[] { 1, 2, 3, 4, 5, 6, 7, 149 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF }; 150 151 private static final String serializedData = "Just a dummy string to be serialized instead of real data"; 152 153 private Certificate[] certs; 154 155 /** 156 * Default ctor for TestCertPath. Uses {@link TestCertUtils#getCertChain()} 157 * to obtain list of certificates.<br> 158 * All TestCertPath-s constructed via this ctor will be equals() to each 159 * other. 160 */ 161 public TestCertPath() { 162 super("testCertPath"); 163 certs = getCertChain(); 164 } 165 166 /** 167 * Constructs TestCertPath and keeps the given array of certificates.<br> 168 * The TestCertPaths constructed via this ctor may be different (if they 169 * have different set of certificates)<br> 170 * 171 * @param certs 172 * @see TestCertUtils#genCertPath(int, int) 173 */ 174 public TestCertPath(Certificate[] certs) { 175 super("testCertPath"); 176 this.certs = certs; 177 } 178 179 /** 180 * @see java.security.cert.CertPath#getCertificates() 181 */ 182 public List getCertificates() { 183 return Arrays.asList(certs); 184 } 185 186 /** 187 * @see java.security.cert.CertPath#getEncoded() 188 */ 189 public byte[] getEncoded() throws CertificateEncodingException { 190 return encoded.clone(); 191 } 192 193 /** 194 * @see java.security.cert.CertPath#getEncoded(java.lang.String) 195 */ 196 public byte[] getEncoded(String encoding) 197 throws CertificateEncodingException { 198 return encoded.clone(); 199 } 200 201 /** 202 * @see java.security.cert.CertPath#getEncodings() 203 */ 204 public Iterator getEncodings() { 205 Vector v = new Vector(); 206 v.add("myTestEncoding"); 207 return v.iterator(); 208 } 209 210 public String toString() { 211 StringBuffer buf = new StringBuffer(200); 212 buf.append("TestCertPath. certs count="); 213 if (certs == null) { 214 buf.append("0\n"); 215 } else { 216 buf.append(certs.length).append("\n"); 217 for (int i = 0; i < certs.length; i++) { 218 buf.append("\t").append(i).append(" "); 219 buf.append(certs[i]).append("\n"); 220 } 221 } 222 return buf.toString(); 223 } 224 225 /** 226 * Writes<br> 227 * (String) serializedData<br> 228 * (int) number of certificates in this CertPath<br> 229 * <array of certificates> 230 * 231 * @param out 232 * @throws IOException 233 */ 234 private void writeObject(ObjectOutputStream out) throws IOException { 235 out.writeUTF(serializedData); 236 if (certs == null) { 237 out.writeInt(0); 238 } else { 239 out.writeInt(certs.length); 240 for (int i = 0; i < certs.length; i++) { 241 out.writeObject(certs[i]); 242 } 243 } 244 } 245 246 private void readObject(ObjectInputStream in) throws IOException, 247 ClassNotFoundException { 248 String s = in.readUTF(); 249 if (!serializedData.equals(s)) { 250 throw new StreamCorruptedException("expect [" + serializedData 251 + "] got [" + s + "]"); 252 } 253 int count = in.readInt(); 254 certs = new Certificate[count]; 255 for (int i = 0; i < count; i++) { 256 certs[i] = (Certificate) in.readObject(); 257 } 258 } 259 260 protected Object writeReplace() { 261 return this; 262 } 263 264 protected Object readResolve() { 265 return this; 266 } 267 } 268 269 /** 270 * The class represents empty PublicKey. 271 */ 272 273 public static final class TestPublicKey implements PublicKey { 274 private static final String algo = "testPublicKeyAlgorithm"; 275 276 private static final byte[] encoded = new byte[] { 1, 2, 3, 4, 5, 6, 7, 277 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF }; 278 279 private static final String format = "testPublicKeyFormat"; 280 281 public String getAlgorithm() { 282 return algo; 283 } 284 285 public byte[] getEncoded() { 286 return encoded.clone(); 287 } 288 289 public String getFormat() { 290 return format; 291 } 292 } 293 294 /** 295 * The class represents test certificate. 296 */ 297 298 public static class TestCertificate extends Certificate implements 299 Serializable { 300 301 private static final byte[] encoded = new byte[] { 1, 2, 3, 4, 5, 6, 7, 302 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF }; 303 304 public static final String TYPE = "Test"; 305 306 // 307 // A String that makes different TestCertificates to be different. 308 // 309 private String diff = null; 310 311 /** 312 * Default ctor. All the TestCertificate-s created with this ctor are equals() to each other. 313 * Use TestCertificate(String) if you need non equal TestCertificate-s. 314 */ 315 public TestCertificate() { 316 super(TYPE); 317 } 318 319 /** 320 * A special purpose ctor. Pass different String-s to have different TestCertificates. 321 * TestCertificate-s with the same String passed to this ctor are considered equal. 322 */ 323 public TestCertificate(String diff) { 324 super(TYPE); 325 this.diff = diff; 326 } 327 328 /** 329 * A ctor that allows to specify both the TYPE of certificate and the 330 * diff. Leave the <code>diff</code> null when no difference needed. 331 * 332 * @param diff 333 * @param type 334 */ 335 public TestCertificate(String diff, String type) { 336 super(type); 337 this.diff = diff; 338 } 339 340 public byte[] getEncoded() throws CertificateEncodingException { 341 return encoded.clone(); 342 } 343 344 public void verify(PublicKey key) throws CertificateException, 345 NoSuchAlgorithmException, InvalidKeyException, 346 NoSuchProviderException, SignatureException { 347 // do nothing 348 } 349 350 public void verify(PublicKey key, String sigProvider) 351 throws CertificateException, NoSuchAlgorithmException, 352 InvalidKeyException, NoSuchProviderException, 353 SignatureException { 354 // do nothing 355 356 } 357 358 public String toString() { 359 return "Test certificate - for unit testing only"; 360 } 361 362 public boolean equals(Object obj) { 363 if (obj == null || !(obj instanceof TestCertificate)) { 364 return false; 365 } 366 TestCertificate that = (TestCertificate) obj; 367 if (this == that) { 368 return true; 369 } 370 if (this.diff == null) { 371 return that.diff == null; 372 } 373 return this.diff.equals(that.diff); 374 } 375 376 public PublicKey getPublicKey() { 377 return new TestPublicKey(); 378 } 379 380 /** 381 * Writes:<br> 382 * boolean - true if this certificate has a diff string, 383 * false otherwise, followed by <br> 384 * writeUTF() of string (if presented) 385 * 386 * @param out 387 * @throws IOException 388 */ 389 private void writeObject(ObjectOutputStream out) throws IOException { 390 if (diff == null) { 391 out.writeBoolean(false); 392 } else { 393 out.writeBoolean(false); 394 out.writeUTF(diff); 395 } 396 } 397 398 private void readObject(ObjectInputStream in) throws IOException, 399 ClassNotFoundException { 400 boolean hasDiffString = in.readBoolean(); 401 if (hasDiffString) { 402 diff = in.readUTF(); 403 } 404 } 405 406 protected Object writeReplace() { 407 return this; 408 } 409 410 protected Object readResolve() { 411 return this; 412 } 413 } 414 415 public static class TestInvalidX509Certificate extends TestX509Certificate { 416 public TestInvalidX509Certificate(X500Principal subj, 417 X500Principal issuer) { 418 super(subj, issuer); 419 } 420 } 421 422 /** 423 * TestX509CErtificate.<br> 424 * Does nothing interesting, but<br> 425 * a) is not abstract, so it can be instantiated<br> 426 * b) returns Encoded form<br> 427 */ 428 public static class TestX509Certificate extends X509Certificate { 429 private X500Principal subject; 430 431 private X500Principal issuer; 432 433 public TestX509Certificate(X500Principal subj, X500Principal issuer) { 434 this.subject = subj; 435 this.issuer = issuer; 436 } 437 438 public X500Principal getIssuerX500Principal() { 439 return issuer; 440 } 441 442 public X500Principal getSubjectX500Principal() { 443 return subject; 444 } 445 446 /** 447 * The encoded for of this X509Certificate is a byte array where 448 * first are bytes of encoded form of Subject (as X500Principal), 449 * followed by one zero byte 450 * and followed by the encoded form of Issuer (as X500Principal) 451 */ 452 public byte[] getEncoded() throws CertificateEncodingException { 453 byte[] asubj = subject.getEncoded(); 454 byte[] aissuer = issuer.getEncoded(); 455 byte[] data = new byte[asubj.length + aissuer.length + 1]; 456 457 System.arraycopy(asubj, 0, data, 0, asubj.length); 458 //data[asubj.length] = 0; 459 System 460 .arraycopy(aissuer, 0, data, asubj.length + 1, 461 aissuer.length); 462 return data; 463 } 464 465 public void checkValidity() throws CertificateExpiredException, 466 CertificateNotYetValidException { 467 } 468 469 public void checkValidity(Date date) 470 throws CertificateExpiredException, 471 CertificateNotYetValidException { 472 } 473 474 public int getBasicConstraints() { 475 return 0; 476 } 477 478 public Principal getIssuerDN() { 479 return null; 480 } 481 482 public boolean[] getIssuerUniqueID() { 483 return null; 484 } 485 486 public boolean[] getKeyUsage() { 487 return null; 488 } 489 490 public Date getNotAfter() { 491 return null; 492 } 493 494 public Date getNotBefore() { 495 return null; 496 } 497 498 public BigInteger getSerialNumber() { 499 return null; 500 } 501 502 public String getSigAlgName() { 503 return null; 504 } 505 506 public String getSigAlgOID() { 507 return null; 508 } 509 510 public byte[] getSigAlgParams() { 511 return null; 512 } 513 514 public byte[] getSignature() { 515 return null; 516 } 517 518 public Principal getSubjectDN() { 519 return null; 520 } 521 522 public boolean[] getSubjectUniqueID() { 523 return null; 524 } 525 526 public byte[] getTBSCertificate() throws CertificateEncodingException { 527 return null; 528 } 529 530 public int getVersion() { 531 return 0; 532 } 533 534 public Set getCriticalExtensionOIDs() { 535 return null; 536 } 537 538 public byte[] getExtensionValue(String oid) { 539 return null; 540 } 541 542 public Set getNonCriticalExtensionOIDs() { 543 return null; 544 } 545 546 public boolean hasUnsupportedCriticalExtension() { 547 return false; 548 } 549 550 public PublicKey getPublicKey() { 551 return null; 552 } 553 554 public String toString() { 555 return null; 556 } 557 558 public void verify(PublicKey key, String sigProvider) 559 throws CertificateException, NoSuchAlgorithmException, 560 InvalidKeyException, NoSuchProviderException, 561 SignatureException { 562 563 } 564 565 public void verify(PublicKey key) throws CertificateException, 566 NoSuchAlgorithmException, InvalidKeyException, 567 NoSuchProviderException, SignatureException { 568 569 } 570 } 571 572 /** 573 * TestProvider. Does nothing, but pretends to 574 * implement X.509 CertificateFactory. 575 */ 576 public static class TestProvider extends Provider { 577 578 private Provider.Service serv; 579 580 public TestProvider(String name, double version, String info) { 581 super(name, version, info); 582 serv = new Provider.Service(this, "CertificateFactory", "X.509", 583 TestFactorySpi.class.getName(), new ArrayList(), null); 584 } 585 586 public synchronized Set getServices() { 587 HashSet s = new HashSet(); 588 s.add(serv); 589 return s; 590 } 591 } 592 593 /** 594 * Some kind of Certificate Factory, used during unit testing. 595 */ 596 public static class TestFactorySpi extends CertificateFactorySpi { 597 598 /** 599 * Tries to create an instance of TestX509Certificate, basing 600 * on the presumption that its {@link TestX509Certificate#getEncoded() 601 * encoded} form is stored.<br> 602 * 603 * @throws CertificateException is the presumption is not met or if 604 * any IO problem occurs. 605 */ 606 public Certificate engineGenerateCertificate(InputStream is) 607 throws CertificateException { 608 byte[] data = new byte[0]; 609 byte[] chunk = new byte[1024]; 610 int len; 611 try { 612 while ((len = is.read(chunk)) > 0) { 613 byte[] tmp = new byte[data.length + len]; 614 System.arraycopy(data, 0, tmp, 0, data.length); 615 System.arraycopy(chunk, 0, tmp, data.length, len); 616 data = tmp; 617 } 618 } catch (IOException ex) { 619 throw new CertificateException("IO problem", ex); 620 } 621 int pos = Arrays.binarySearch(data, (byte) 0); 622 if (pos < 0) { 623 throw new CertificateException("invalid format"); 624 } 625 byte[] subjNameData = new byte[pos]; 626 System.arraycopy(data, 0, subjNameData, 0, subjNameData.length); 627 byte[] issNameData = new byte[data.length - pos - 1]; 628 System.arraycopy(data, pos + 1, issNameData, 0, issNameData.length); 629 X500Principal subjName = new X500Principal(subjNameData); 630 X500Principal issName = new X500Principal(issNameData); 631 return new TestX509Certificate(subjName, issName); 632 } 633 634 /** 635 * Not supported yet. 636 * 637 * @throws UnsupportedOperationException 638 */ 639 public Collection engineGenerateCertificates(InputStream inStream) 640 throws CertificateException { 641 throw new UnsupportedOperationException("not yet."); 642 } 643 644 /** 645 * Not supported yet. 646 * 647 * @throws UnsupportedOperationException 648 */ 649 public CRL engineGenerateCRL(InputStream inStream) throws CRLException { 650 throw new UnsupportedOperationException("not yet."); 651 } 652 653 /** 654 * Not supported yet. 655 * 656 * @throws UnsupportedOperationException 657 */ 658 public Collection engineGenerateCRLs(InputStream inStream) 659 throws CRLException { 660 throw new UnsupportedOperationException("not yet."); 661 } 662 663 /** 664 * Returns an instance of TestCertPath.<br> 665 * 666 * @throws CertificateException if 667 * a) any of Certificates passed is not an instance of X509Certificate 668 * b) any of Certificates passed is an instance of TestInvalidX509Certificate 669 */ 670 public CertPath engineGenerateCertPath(List certs) 671 throws CertificateException { 672 ArrayList validCerts = new ArrayList(); 673 for (Iterator i = certs.iterator(); i.hasNext(); ) { 674 Certificate c = (Certificate) i.next(); 675 if (!(c instanceof X509Certificate)) { 676 throw new CertificateException("Not X509: " + c); 677 } 678 if (c instanceof TestInvalidX509Certificate) { 679 throw new CertificateException("Invalid (test) X509: " + c); 680 } 681 validCerts.add(c); 682 } 683 Certificate[] acerts = new Certificate[validCerts.size()]; 684 validCerts.toArray(acerts); 685 return new TestCertPath(acerts); 686 } 687 } 688 689 /** 690 * Utility class used to generate some amount of uniq names. 691 */ 692 public static class UniGen { 693 public static final String rootName = "CN=Alex Astapchuk, OU=SSG, O=Intel ZAO, C=RU"; 694 695 private static final String datasNames[] = { "CN", "OU", "O", "C" }; 696 697 private static final String datas[][] = { 698 // Names database 699 { "Alex Astapchuk", null, null, null }, 700 { "John Doe", null, null, null }, 701 // 'organisation unit'-s 702 { null, "SSG", null, null }, { null, "SSG/DRL", null, null }, 703 // organizations 704 { null, null, "Intel ZAO", null }, 705 { null, null, "Intel Inc", null }, 706 // countries 707 { null, null, null, "RU" }, { null, null, null, "US" }, 708 { null, null, null, "GB" }, { null, null, null, "JA" }, 709 { null, null, null, "KO" }, { null, null, null, "TW" }, }; 710 711 // 712 // Returns a string from <code>data</code> from a given column and 713 // position. The positions are looked for first non-null entry. If there 714 // are no non empty items left, then it scans column starting from the 715 // beginning. 716 // 717 // @param col 718 // @param startRow 719 // @return 720 // 721 private static String getData(int col, int startRow) { 722 startRow = startRow % datas.length; 723 for (int i = startRow; i < datas.length; i++) { 724 if (datas[i][col] != null) { 725 return datas[i][col]; 726 } 727 } 728 // no non-null entries left, check from the beginning 729 for (int i = 0; i < datas.length; i++) { 730 if (datas[i][col] != null) { 731 return datas[i][col]; 732 } 733 } 734 // can't be 735 throw new Error(); 736 } 737 738 // 739 // Increments a num.<br> 740 // <code>num</code> is interpreted as a number with a base of 741 // <code>base</code> and each digit of this number is stored as a 742 // separate num's element. 743 // 744 // @param num 745 // @param base 746 // @return <b>true</b> if overflow happened 747 // 748 private static boolean inc(int[] num, int base) { 749 for (int i = 0; i < num.length; i++) { 750 if ((++num[i]) >= base) { 751 num[i] = 0; 752 } else { 753 return false; 754 } 755 } 756 return true; 757 } 758 759 /** 760 * Generates some amount of uniq names, none of which is equals to 761 * {@link #rootName}. 762 * 763 * @param howMany 764 * @return 765 */ 766 public static String[] genNames(int howMany) { 767 int counts[] = new int[datasNames.length]; 768 ArrayList al = new ArrayList(); 769 770 // not really the thrifty algorithm... 771 for (int i = 0; i < howMany; ) { 772 773 // System.out.print("#"+i+": "); 774 // for( int j=0; j<counts.length; j++) { 775 // System.out.print(""+counts[j]+"|"); 776 // } 777 // System.out.println(); 778 779 StringBuffer buf = new StringBuffer(); 780 int j = 0; 781 for (; j < datasNames.length - 1; j++) { 782 String name = datasNames[j]; 783 String val = getData(j, counts[j]); 784 buf.append(name).append('=').append(val).append(","); 785 } 786 String name = datasNames[j]; 787 String val = getData(j, counts[j]); 788 buf.append(name).append('=').append(val); 789 790 name = buf.toString(); 791 792 if (!(rootName.equals(name) || al.contains(name))) { 793 ++i; 794 al.add(name); 795 // System.out.println("generated: "+name); 796 } else { 797 // System.out.println("rejected: "+name); 798 } 799 800 if (inc(counts, datas.length)) { 801 // if this happened, then just add some data into 'datas' 802 throw new Error( 803 "cant generate so many uniq names. sorry. add some more data."); 804 } 805 } 806 return (String[]) al.toArray(new String[al.size()]); 807 } 808 809 /** 810 * Generates some amount of uniq X500Principals, none of which is equals 811 * has a string equals to {@link #rootName}. 812 * 813 * @param howMany 814 * @return 815 */ 816 public static X500Principal[] genX500s(int howMany) { 817 String names[] = genNames(howMany); 818 X500Principal[] ps = new X500Principal[howMany]; 819 for (int i = 0; i < howMany; i++) { 820 ps[i] = new X500Principal(names[i]); 821 } 822 return ps; 823 } 824 825 } 826 827} 828 829