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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 * @author Alexander V. Astapchuk
23 package org.apache.harmony.security.tests.support;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.ObjectInputStream;
28 import java.io.ObjectOutputStream;
29 import java.io.Serializable;
30 import java.io.StreamCorruptedException;
31 import java.math.BigInteger;
33 import java.security.InvalidKeyException;
34 import java.security.NoSuchAlgorithmException;
35 import java.security.NoSuchProviderException;
36 import java.security.Principal;
37 import java.security.Provider;
38 import java.security.PublicKey;
39 import java.security.Security;
40 import java.security.SignatureException;
42 import java.security.cert.*;
45 import javax.security.auth.x500.X500Principal;
48 * The class contains various utility methods used during the java.security
53 public final class TestCertUtils {
55 private TestCertUtils() {
56 throw new Error("statics only");
60 * Returns new instance of test certificate each time the method is called.
62 * @return test certificate
64 public static Certificate getCert() {
65 return new TestCertificate();
69 * Returns an array of 3 test certificates. IMP: The array returned is not
70 * real chain of certificates, it's just an array of 3 certs. The method
71 * returns new array each time it's called. The number of 3 was chosen
72 * arbitrarily and is subject to change.
74 * @return an array of 3 certificates
76 public static Certificate[] getCertChain() {
77 Certificate[] chain = { new TestCertificate(), new TestCertificate(),
78 new TestCertificate() };
83 * Returns a test CertPath, which uses getCertChain() to obtain a list of
84 * certificates to store.
86 * @return test cert path
88 public static CertPath getCertPath() {
89 return new TestCertPath();
93 * Generates and returns an instance of TestCertPath.<br>
94 * TestCertificate-s included in the CertPath will be uniq (will have
95 * different numbers passed to their ctor-s).<br>
96 * The second arguments shows which number will have the first Certificate
97 * in the CertPath. The second certificate will have (startID+1) number
100 * @param howMany - shows how many TestCerts must contain the CertPath generated
101 * @param startID - specifies the starting ID which the first certificate will have
102 * @return TestCertPath
104 public static CertPath genCertPath(int howMany, int startID) {
105 Certificate[] certs = new Certificate[howMany];
106 for (int i = 0; i < howMany; i++) {
107 certs[i] = new TestCertificate(Integer.toString(startID + i));
109 return new TestCertPath(certs);
112 private static Provider provider = null;
114 private static final String providerName = "TstPrvdr";
117 * A Principal used to form rootCA's certificate
119 public static final X500Principal rootPrincipal = new X500Principal(
123 * Some fake rootCA's certificate.
125 public static final X509Certificate rootCA = new TestX509Certificate(
126 rootPrincipal, rootPrincipal);
128 public static void install_test_x509_factory() {
129 if (provider == null) {
130 provider = new TestProvider(providerName, 0.01,
131 "Test provider for serialization testing");
132 Security.insertProviderAt(provider, 1);
136 public static void uninstall_test_x509_factory() {
137 if (provider != null) {
138 Security.removeProvider(providerName);
144 * The class represents test certificate path.
148 public static final class TestCertPath extends CertPath implements
151 private static final byte[] encoded = new byte[] { 1, 2, 3, 4, 5, 6, 7,
152 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF };
154 private static final String serializedData = "Just a dummy string to be serialized instead of real data";
156 private Certificate[] certs;
159 * Default ctor for TestCertPath. Uses {@link TestCertUtils#getCertChain()}
160 * to obtain list of certificates.<br>
161 * All TestCertPath-s constructed via this ctor will be equals() to each
164 public TestCertPath() {
165 super("testCertPath");
166 certs = getCertChain();
170 * Constructs TestCertPath and keeps the given array of certificates.<br>
171 * The TestCertPaths constructed via this ctor may be different (if they
172 * have different set of certificates)<br>
173 * @see TestCertUtils#genCertPath(int, int)
176 public TestCertPath(Certificate[] certs) {
177 super("testCertPath");
182 * @see java.security.cert.CertPath#getCertificates()
184 public List<Certificate> getCertificates() {
185 return Arrays.asList(certs);
189 * @see java.security.cert.CertPath#getEncoded()
191 public byte[] getEncoded() throws CertificateEncodingException {
192 return encoded.clone();
196 * @see java.security.cert.CertPath#getEncoded(java.lang.String)
198 public byte[] getEncoded(String encoding)
199 throws CertificateEncodingException {
200 return encoded.clone();
204 * @see java.security.cert.CertPath#getEncodings()
206 public Iterator<String> getEncodings() {
207 Vector<String> v = new Vector<String>();
208 v.add("myTestEncoding");
212 public String toString() {
213 StringBuffer buf = new StringBuffer(200);
214 buf.append("TestCertPath. certs count=");
215 if( certs == null ) {
219 buf.append(certs.length).append("\n");
220 for( int i=0; i<certs.length; i++) {
221 buf.append("\t").append(i).append(" ");
222 buf.append(certs[i]).append("\n");
225 return buf.toString();
230 * (String) serializedData<br>
231 * (int) number of certificates in this CertPath<br>
232 * <array of certificates>
235 * @throws IOException
237 private void writeObject(ObjectOutputStream out) throws IOException {
238 out.writeUTF(serializedData);
242 out.writeInt(certs.length);
243 for (int i = 0; i < certs.length; i++) {
244 out.writeObject(certs[i]);
249 private void readObject(ObjectInputStream in) throws IOException,
250 ClassNotFoundException {
251 String s = in.readUTF();
252 if (!serializedData.equals(s)) {
253 throw new StreamCorruptedException("expect [" + serializedData
254 + "] got [" + s + "]");
256 int count = in.readInt();
257 certs = new Certificate[count];
258 for (int i = 0; i < count; i++) {
259 certs[i] = (Certificate) in.readObject();
263 protected Object writeReplace() {
267 protected Object readResolve() {
273 * The class represents empty PublicKey.
277 public static final class TestPublicKey implements PublicKey {
278 private static final String algo = "testPublicKeyAlgorithm";
280 private static final byte[] encoded = new byte[] { 1, 2, 3, 4, 5, 6, 7,
281 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF };
283 private static final String format = "testPublicKeyFormat";
285 public String getAlgorithm() {
289 public byte[] getEncoded() {
290 return encoded.clone();
293 public String getFormat() {
299 * The class represents test certificate.
303 public static class TestCertificate extends Certificate implements
306 private static final byte[] encoded = new byte[] { 1, 2, 3, 4, 5, 6, 7,
307 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF };
309 public static final String TYPE = "Test";
312 // A String that makes different TestCertificates to be different.
314 private String diff = null;
317 * Default ctor. All the TestCertificate-s created with this ctor are equals() to each other.
318 * Use TestCertificate(String) if you need non equal TestCertificate-s.
320 public TestCertificate() {
325 * A special purpose ctor. Pass different String-s to have different TestCertificates.
326 * TestCertificate-s with the same String passed to this ctor are considered equal.
328 public TestCertificate(String diff) {
334 * A ctor that allows to specify both the TYPE of certificate and the
335 * diff. Leave the <code>diff</code> null when no difference needed.
340 public TestCertificate(String diff, String type) {
345 public byte[] getEncoded() throws CertificateEncodingException {
346 return encoded.clone();
349 public void verify(PublicKey key) throws CertificateException,
350 NoSuchAlgorithmException, InvalidKeyException,
351 NoSuchProviderException, SignatureException {
355 public void verify(PublicKey key, String sigProvider)
356 throws CertificateException, NoSuchAlgorithmException,
357 InvalidKeyException, NoSuchProviderException,
363 public String toString() {
364 return "Test certificate - for unit testing only";
367 public boolean equals(Object obj) {
368 if (obj == null || !(obj instanceof TestCertificate)) {
371 TestCertificate that = (TestCertificate) obj;
375 if (this.diff == null) {
376 return that.diff == null;
378 return this.diff.equals(that.diff);
381 public PublicKey getPublicKey() {
382 return new TestPublicKey();
387 * boolean - true if this certificate has a diff string,
388 * false otherwise, followed by <br>
389 * writeUTF() of string (if presented)
392 * @throws IOException
394 private void writeObject(ObjectOutputStream out) throws IOException {
396 out.writeBoolean(false);
398 out.writeBoolean(false);
403 private void readObject(ObjectInputStream in) throws IOException,
404 ClassNotFoundException {
405 boolean hasDiffString = in.readBoolean();
411 protected Object writeReplace() {
415 protected Object readResolve() {
420 public static class TestInvalidX509Certificate extends TestX509Certificate {
421 public TestInvalidX509Certificate(X500Principal subj,
422 X500Principal issuer) {
429 * TestX509CErtificate.<br>
430 * Does nothing interesting, but<br>
431 * a) is not abstract, so it can be instantiated<br>
432 * b) returns Encoded form<br>
435 public static class TestX509Certificate extends X509Certificate {
436 private X500Principal subject;
438 private X500Principal issuer;
440 public TestX509Certificate(X500Principal subj, X500Principal issuer) {
442 this.issuer = issuer;
445 public X500Principal getIssuerX500Principal() {
449 public X500Principal getSubjectX500Principal() {
454 * The encoded for of this X509Certificate is a byte array where
455 * first are bytes of encoded form of Subject (as X500Principal),
456 * followed by one zero byte
457 * and followed by the encoded form of Issuer (as X500Principal)
460 public byte[] getEncoded() throws CertificateEncodingException {
461 byte[] asubj = subject.getEncoded();
462 byte[] aissuer = issuer.getEncoded();
463 byte[] data = new byte[asubj.length + aissuer.length + 1];
465 System.arraycopy(asubj, 0, data, 0, asubj.length);
466 //data[asubj.length] = 0;
468 .arraycopy(aissuer, 0, data, asubj.length + 1,
473 public void checkValidity() throws CertificateExpiredException,
474 CertificateNotYetValidException {
477 public void checkValidity(Date date)
478 throws CertificateExpiredException,
479 CertificateNotYetValidException {
482 public int getBasicConstraints() {
486 public Principal getIssuerDN() {
490 public boolean[] getIssuerUniqueID() {
494 public boolean[] getKeyUsage() {
498 public Date getNotAfter() {
502 public Date getNotBefore() {
506 public BigInteger getSerialNumber() {
510 public String getSigAlgName() {
514 public String getSigAlgOID() {
518 public byte[] getSigAlgParams() {
522 public byte[] getSignature() {
526 public Principal getSubjectDN() {
530 public boolean[] getSubjectUniqueID() {
534 public byte[] getTBSCertificate() throws CertificateEncodingException {
538 public int getVersion() {
542 public Set getCriticalExtensionOIDs() {
546 public byte[] getExtensionValue(String oid) {
550 public Set getNonCriticalExtensionOIDs() {
554 public boolean hasUnsupportedCriticalExtension() {
558 public PublicKey getPublicKey() {
562 public String toString() {
566 public void verify(PublicKey key, String sigProvider)
567 throws CertificateException, NoSuchAlgorithmException,
568 InvalidKeyException, NoSuchProviderException,
573 public void verify(PublicKey key) throws CertificateException,
574 NoSuchAlgorithmException, InvalidKeyException,
575 NoSuchProviderException, SignatureException {
581 * TestProvider. Does nothing, but pretends to
582 * implement X.509 CertificateFactory.
584 public static class TestProvider extends Provider {
586 private Provider.Service serv;
588 public TestProvider(String name, double version, String info) {
589 super(name, version, info);
590 serv = new Provider.Service(this, "CertificateFactory", "X.509",
591 TestFactorySpi.class.getName(), new ArrayList<String>(), null);
594 public synchronized Set<Provider.Service> getServices() {
595 HashSet<Provider.Service> s = new HashSet<Service>();
602 * Some kind of Certificate Factory, used during unit testing.
606 public static class TestFactorySpi extends CertificateFactorySpi {
609 * Tries to create an instance of TestX509Certificate, basing
610 * on the presumption that its {@link TestX509Certificate#getEncoded()
611 * encoded} form is stored.<br>
612 * @throws CertificateException is the presumption is not met or if
613 * any IO problem occurs.
615 public Certificate engineGenerateCertificate(InputStream is)
616 throws CertificateException {
617 byte[] data = new byte[0];
618 byte[] chunk = new byte[1024];
621 while ((len = is.read(chunk)) > 0) {
622 byte[] tmp = new byte[data.length + len];
623 System.arraycopy(data, 0, tmp, 0, data.length);
624 System.arraycopy(chunk, 0, tmp, data.length, len);
627 } catch (IOException ex) {
628 throw new CertificateException("IO problem", ex);
630 int pos = Arrays.binarySearch(data, (byte) 0);
632 throw new CertificateException("invalid format");
634 byte[] subjNameData = new byte[pos];
635 System.arraycopy(data, 0, subjNameData, 0, subjNameData.length);
636 byte[] issNameData = new byte[data.length - pos - 1];
637 System.arraycopy(data, pos + 1, issNameData, 0, issNameData.length);
638 X500Principal subjName = new X500Principal(subjNameData);
639 X500Principal issName = new X500Principal(issNameData);
640 return new TestX509Certificate(subjName, issName);
645 * @throws UnsupportedOperationException
647 public Collection engineGenerateCertificates(InputStream inStream)
648 throws CertificateException {
649 throw new UnsupportedOperationException("not yet.");
654 * @throws UnsupportedOperationException
656 public CRL engineGenerateCRL(InputStream inStream) throws CRLException {
657 throw new UnsupportedOperationException("not yet.");
662 * @throws UnsupportedOperationException
664 public Collection engineGenerateCRLs(InputStream inStream)
665 throws CRLException {
666 throw new UnsupportedOperationException("not yet.");
670 * Returns an instance of TestCertPath.<br>
671 * @throws CertificateException if
672 * a) any of Certificates passed is not an instance of X509Certificate
673 * b) any of Certificates passed is an instance of TestInvalidX509Certificate
675 public CertPath engineGenerateCertPath(List certs)
676 throws CertificateException {
677 ArrayList<Certificate> validCerts = new ArrayList<Certificate>();
678 for (Iterator i = certs.iterator(); i.hasNext();) {
679 Certificate c = (Certificate) i.next();
680 if (!(c instanceof X509Certificate)) {
681 throw new CertificateException("Not X509: " + c);
683 if (c instanceof TestInvalidX509Certificate) {
684 throw new CertificateException("Invalid (test) X509: " + c);
688 Certificate[] acerts = new Certificate[validCerts.size()];
689 validCerts.toArray(acerts);
690 return new TestCertPath(acerts);
695 * Utility class used to generate some amount of uniq names.
697 public static class UniGen {
698 public static final String rootName = "CN=Alex Astapchuk, OU=SSG, O=Intel ZAO, C=RU";
700 private static final String datasNames[] = { "CN", "OU", "O", "C" };
702 private static final String datas[][] = {
704 { "Alex Astapchuk", null, null, null },
705 { "John Doe", null, null, null },
706 // 'organisation unit'-s
707 { null, "SSG", null, null }, { null, "SSG/DRL", null, null },
709 { null, null, "Intel ZAO", null },
710 { null, null, "Intel Inc", null },
712 { null, null, null, "RU" }, { null, null, null, "US" },
713 { null, null, null, "GB" }, { null, null, null, "JA" },
714 { null, null, null, "KO" }, { null, null, null, "TW" }, };
717 // Returns a string from <code>data</code> from a given column and
718 // position. The positions are looked for first non-null entry. If there
719 // are no non empty items left, then it scans column starting from the
726 private static String getData(int col, int startRow) {
727 startRow = startRow % datas.length;
728 for (int i = startRow; i < datas.length; i++) {
729 if (datas[i][col] != null) {
730 return datas[i][col];
733 // no non-null entries left, check from the beginning
734 for (int i = 0; i < datas.length; i++) {
735 if (datas[i][col] != null) {
736 return datas[i][col];
744 // Increments a num.<br>
745 // <code>num</code> is interpreted as a number with a base of
746 // <code>base</code> and each digit of this number is stored as a
747 // separate num's element.
751 // @return <b>true</b> if overflow happened
753 private static boolean inc(int[] num, int base) {
754 for (int i = 0; i < num.length; i++) {
755 if ((++num[i]) >= base) {
765 * Generates some amount of uniq names, none of which is equals to
770 public static String[] genNames(int howMany) {
771 int counts[] = new int[datasNames.length];
772 ArrayList<String> al = new ArrayList<String>();
774 // not really the thrifty algorithm...
775 for (int i = 0; i < howMany;) {
777 // System.out.print("#"+i+": ");
778 // for( int j=0; j<counts.length; j++) {
779 // System.out.print(""+counts[j]+"|");
781 // System.out.println();
783 StringBuffer buf = new StringBuffer();
785 for (; j < datasNames.length - 1; j++) {
786 String name = datasNames[j];
787 String val = getData(j, counts[j]);
788 buf.append(name).append('=').append(val).append(",");
790 String name = datasNames[j];
791 String val = getData(j, counts[j]);
792 buf.append(name).append('=').append(val);
794 name = buf.toString();
796 if (!(rootName.equals(name) || al.contains(name))) {
799 // System.out.println("generated: "+name);
801 // System.out.println("rejected: "+name);
804 if (inc(counts, datas.length)) {
805 // if this happened, then just add some data into 'datas'
807 "cant generate so many uniq names. sorry. add some more data.");
810 return (String[]) al.toArray(new String[al.size()]);
814 * Generates some amount of uniq X500Principals, none of which is equals
815 * has a string equals to {@link #rootName}.
819 public static X500Principal[] genX500s(int howMany) {
820 String names[] = genNames(howMany);
821 X500Principal[] ps = new X500Principal[howMany];
822 for (int i = 0; i < howMany; i++) {
823 ps[i] = new X500Principal(names[i]);