OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / libcore / support / src / test / java / org / apache / harmony / security / tests / support / TestCertUtils.java
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 * @version $Revision$
21 */
22
23 package org.apache.harmony.security.tests.support;
24
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;
32
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;
41
42 import java.security.cert.*;
43 import java.util.*;
44
45 import javax.security.auth.x500.X500Principal;
46
47 /**
48  * The class contains various utility methods used during the java.security
49  * classes testing.
50  *
51  */
52
53 public final class TestCertUtils {
54
55     private TestCertUtils() {
56         throw new Error("statics only");
57     }
58
59     /**
60      * Returns new instance of test certificate each time the method is called.
61      *
62      * @return test certificate
63      */
64     public static Certificate getCert() {
65         return new TestCertificate();
66     }
67
68     /**
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.
73      *
74      * @return an array of 3 certificates
75      */
76     public static Certificate[] getCertChain() {
77         Certificate[] chain = { new TestCertificate(), new TestCertificate(),
78                 new TestCertificate() };
79         return chain;
80     }
81
82     /**
83      * Returns a test CertPath, which uses getCertChain() to obtain a list of
84      * certificates to store.
85      *
86      * @return test cert path
87      */
88     public static CertPath getCertPath() {
89         return new TestCertPath();
90     }
91
92     /**
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
98      * and so on.
99      *
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
103      */
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));
108         }
109         return new TestCertPath(certs);
110     }
111
112     private static Provider provider = null;
113
114     private static final String providerName = "TstPrvdr";
115
116     /**
117      * A Principal used to form rootCA's certificate
118      */
119     public static final X500Principal rootPrincipal = new X500Principal(
120             UniGen.rootName);
121
122     /**
123      * Some fake rootCA's certificate.
124      */
125     public static final X509Certificate rootCA = new TestX509Certificate(
126             rootPrincipal, rootPrincipal);
127
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);
133         }
134     }
135
136     public static void uninstall_test_x509_factory() {
137         if (provider != null) {
138             Security.removeProvider(providerName);
139             provider = null;
140         }
141     }
142
143     /**
144      * The class represents test certificate path.
145      *
146      */
147
148     public static final class TestCertPath extends CertPath implements
149             Serializable {
150
151         private static final byte[] encoded = new byte[] { 1, 2, 3, 4, 5, 6, 7,
152                 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF };
153
154         private static final String serializedData = "Just a dummy string to be serialized instead of real data";
155
156         private Certificate[] certs;
157
158         /**
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
162          * other.
163          */
164         public TestCertPath() {
165             super("testCertPath");
166             certs = getCertChain();
167         }
168
169         /**
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)
174          * @param certs
175          */
176         public TestCertPath(Certificate[] certs) {
177             super("testCertPath");
178             this.certs = certs;
179         }
180
181         /**
182          * @see java.security.cert.CertPath#getCertificates()
183          */
184         public List<Certificate> getCertificates() {
185             return Arrays.asList(certs);
186         }
187
188         /**
189          * @see java.security.cert.CertPath#getEncoded()
190          */
191         public byte[] getEncoded() throws CertificateEncodingException {
192             return encoded.clone();
193         }
194
195         /**
196          * @see java.security.cert.CertPath#getEncoded(java.lang.String)
197          */
198         public byte[] getEncoded(String encoding)
199                 throws CertificateEncodingException {
200             return encoded.clone();
201         }
202
203         /**
204          * @see java.security.cert.CertPath#getEncodings()
205          */
206         public Iterator<String> getEncodings() {
207             Vector<String> v = new Vector<String>();
208             v.add("myTestEncoding");
209             return v.iterator();
210         }
211
212         public String toString() {
213             StringBuffer buf = new StringBuffer(200);
214             buf.append("TestCertPath. certs count=");
215             if( certs == null ) {
216                 buf.append("0\n");
217             }
218             else {
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");
223                 }
224             }
225             return buf.toString();
226         }
227
228         /**
229          * Writes<br>
230          * (String) serializedData<br>
231          * (int) number of certificates in this CertPath<br>
232          * <array of certificates>
233          *
234          * @param out
235          * @throws IOException
236          */
237         private void writeObject(ObjectOutputStream out) throws IOException {
238             out.writeUTF(serializedData);
239             if (certs == null) {
240                 out.writeInt(0);
241             } else {
242                 out.writeInt(certs.length);
243                 for (int i = 0; i < certs.length; i++) {
244                     out.writeObject(certs[i]);
245                 }
246             }
247         }
248
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 + "]");
255             }
256             int count = in.readInt();
257             certs = new Certificate[count];
258             for (int i = 0; i < count; i++) {
259                 certs[i] = (Certificate) in.readObject();
260             }
261         }
262
263         protected Object writeReplace() {
264             return this;
265         }
266
267         protected Object readResolve() {
268             return this;
269         }
270     }
271
272     /**
273      * The class represents empty PublicKey.
274      *
275      */
276
277     public static final class TestPublicKey implements PublicKey {
278         private static final String algo = "testPublicKeyAlgorithm";
279
280         private static final byte[] encoded = new byte[] { 1, 2, 3, 4, 5, 6, 7,
281                 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF };
282
283         private static final String format = "testPublicKeyFormat";
284
285         public String getAlgorithm() {
286             return algo;
287         }
288
289         public byte[] getEncoded() {
290             return encoded.clone();
291         }
292
293         public String getFormat() {
294             return format;
295         }
296     }
297
298     /**
299      * The class represents test certificate.
300      *
301      */
302
303     public static class TestCertificate extends Certificate implements
304             Serializable {
305
306         private static final byte[] encoded = new byte[] { 1, 2, 3, 4, 5, 6, 7,
307                 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF };
308
309         public static final String TYPE = "Test";
310
311         //
312         // A String that makes different TestCertificates to be different.
313         //
314         private String diff = null;
315
316         /**
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.
319          */
320         public TestCertificate() {
321             super(TYPE);
322         }
323
324         /**
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.
327          */
328         public TestCertificate(String diff) {
329             super(TYPE);
330             this.diff = diff;
331         }
332
333         /**
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.
336          *
337          * @param diff
338          * @param type
339          */
340         public TestCertificate(String diff, String type) {
341             super(type);
342             this.diff = diff;
343         }
344
345         public byte[] getEncoded() throws CertificateEncodingException {
346             return encoded.clone();
347         }
348
349         public void verify(PublicKey key) throws CertificateException,
350                 NoSuchAlgorithmException, InvalidKeyException,
351                 NoSuchProviderException, SignatureException {
352             // do nothing
353         }
354
355         public void verify(PublicKey key, String sigProvider)
356                 throws CertificateException, NoSuchAlgorithmException,
357                 InvalidKeyException, NoSuchProviderException,
358                 SignatureException {
359             // do nothing
360
361         }
362
363         public String toString() {
364             return "Test certificate - for unit testing only";
365         }
366
367         public boolean equals(Object obj) {
368             if (obj == null || !(obj instanceof TestCertificate)) {
369                 return false;
370             }
371             TestCertificate that = (TestCertificate) obj;
372             if (this == that) {
373                 return true;
374             }
375             if (this.diff == null) {
376                 return that.diff == null;
377             }
378             return this.diff.equals(that.diff);
379         }
380
381         public PublicKey getPublicKey() {
382             return new TestPublicKey();
383         }
384
385         /**
386          * Writes:<br>
387          * boolean - true if this certificate has a diff string,
388          * false otherwise, followed by <br>
389          * writeUTF() of string (if presented)
390          *
391          * @param out
392          * @throws IOException
393          */
394         private void writeObject(ObjectOutputStream out) throws IOException {
395             if (diff == null) {
396                 out.writeBoolean(false);
397             } else {
398                 out.writeBoolean(false);
399                 out.writeUTF(diff);
400             }
401         }
402
403         private void readObject(ObjectInputStream in) throws IOException,
404                 ClassNotFoundException {
405             boolean hasDiffString = in.readBoolean();
406             if (hasDiffString) {
407                 diff = in.readUTF();
408             }
409         }
410
411         protected Object writeReplace() {
412             return this;
413         }
414
415         protected Object readResolve() {
416             return this;
417         }
418     }
419
420     public static class TestInvalidX509Certificate extends TestX509Certificate {
421         public TestInvalidX509Certificate(X500Principal subj,
422                 X500Principal issuer) {
423             super(subj, issuer);
424         }
425     }
426
427     /**
428      *
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>
433      *
434      */
435     public static class TestX509Certificate extends X509Certificate {
436         private X500Principal subject;
437
438         private X500Principal issuer;
439
440         public TestX509Certificate(X500Principal subj, X500Principal issuer) {
441             this.subject = subj;
442             this.issuer = issuer;
443         }
444
445         public X500Principal getIssuerX500Principal() {
446             return issuer;
447         }
448
449         public X500Principal getSubjectX500Principal() {
450             return subject;
451         }
452
453         /**
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)
458          *
459          */
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];
464
465             System.arraycopy(asubj, 0, data, 0, asubj.length);
466             //data[asubj.length] = 0;
467             System
468                     .arraycopy(aissuer, 0, data, asubj.length + 1,
469                             aissuer.length);
470             return data;
471         }
472
473         public void checkValidity() throws CertificateExpiredException,
474                 CertificateNotYetValidException {
475         }
476
477         public void checkValidity(Date date)
478                 throws CertificateExpiredException,
479                 CertificateNotYetValidException {
480         }
481
482         public int getBasicConstraints() {
483             return 0;
484         }
485
486         public Principal getIssuerDN() {
487             return null;
488         }
489
490         public boolean[] getIssuerUniqueID() {
491             return null;
492         }
493
494         public boolean[] getKeyUsage() {
495             return null;
496         }
497
498         public Date getNotAfter() {
499             return null;
500         }
501
502         public Date getNotBefore() {
503             return null;
504         }
505
506         public BigInteger getSerialNumber() {
507             return null;
508         }
509
510         public String getSigAlgName() {
511             return null;
512         }
513
514         public String getSigAlgOID() {
515             return null;
516         }
517
518         public byte[] getSigAlgParams() {
519             return null;
520         }
521
522         public byte[] getSignature() {
523             return null;
524         }
525
526         public Principal getSubjectDN() {
527             return null;
528         }
529
530         public boolean[] getSubjectUniqueID() {
531             return null;
532         }
533
534         public byte[] getTBSCertificate() throws CertificateEncodingException {
535             return null;
536         }
537
538         public int getVersion() {
539             return 0;
540         }
541
542         public Set getCriticalExtensionOIDs() {
543             return null;
544         }
545
546         public byte[] getExtensionValue(String oid) {
547             return null;
548         }
549
550         public Set getNonCriticalExtensionOIDs() {
551             return null;
552         }
553
554         public boolean hasUnsupportedCriticalExtension() {
555             return false;
556         }
557
558         public PublicKey getPublicKey() {
559             return null;
560         }
561
562         public String toString() {
563             return null;
564         }
565
566         public void verify(PublicKey key, String sigProvider)
567                 throws CertificateException, NoSuchAlgorithmException,
568                 InvalidKeyException, NoSuchProviderException,
569                 SignatureException {
570
571         }
572
573         public void verify(PublicKey key) throws CertificateException,
574                 NoSuchAlgorithmException, InvalidKeyException,
575                 NoSuchProviderException, SignatureException {
576
577         }
578     }
579
580     /**
581      * TestProvider. Does nothing, but pretends to
582      * implement X.509 CertificateFactory.
583      */
584     public static class TestProvider extends Provider {
585
586         private Provider.Service serv;
587
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);
592         }
593
594         public synchronized Set<Provider.Service> getServices() {
595             HashSet<Provider.Service> s = new HashSet<Service>();
596             s.add(serv);
597             return s;
598         }
599     }
600
601     /**
602      * Some kind of Certificate Factory, used during unit testing.
603      *
604      *
605      */
606     public static class TestFactorySpi extends CertificateFactorySpi {
607
608         /**
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.
614          */
615         public Certificate engineGenerateCertificate(InputStream is)
616                 throws CertificateException {
617             byte[] data = new byte[0];
618             byte[] chunk = new byte[1024];
619             int len;
620             try {
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);
625                     data = tmp;
626                 }
627             } catch (IOException ex) {
628                 throw new CertificateException("IO problem", ex);
629             }
630             int pos = Arrays.binarySearch(data, (byte) 0);
631             if (pos < 0) {
632                 throw new CertificateException("invalid format");
633             }
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);
641         }
642
643         /**
644          * Not supported yet.
645          * @throws UnsupportedOperationException
646          */
647         public Collection engineGenerateCertificates(InputStream inStream)
648                 throws CertificateException {
649             throw new UnsupportedOperationException("not yet.");
650         }
651
652         /**
653          * Not supported yet.
654          * @throws UnsupportedOperationException
655          */
656         public CRL engineGenerateCRL(InputStream inStream) throws CRLException {
657             throw new UnsupportedOperationException("not yet.");
658         }
659
660         /**
661          * Not supported yet.
662          * @throws UnsupportedOperationException
663          */
664         public Collection engineGenerateCRLs(InputStream inStream)
665                 throws CRLException {
666             throw new UnsupportedOperationException("not yet.");
667         }
668
669         /**
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
674          */
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);
682                 }
683                 if (c instanceof TestInvalidX509Certificate) {
684                     throw new CertificateException("Invalid (test) X509: " + c);
685                 }
686                 validCerts.add(c);
687             }
688             Certificate[] acerts = new Certificate[validCerts.size()];
689             validCerts.toArray(acerts);
690             return new TestCertPath(acerts);
691         }
692     }
693
694     /**
695      * Utility class used to generate some amount of uniq names.
696      */
697     public static class UniGen {
698         public static final String rootName = "CN=Alex Astapchuk, OU=SSG, O=Intel ZAO, C=RU";
699
700         private static final String datasNames[] = { "CN", "OU", "O", "C" };
701
702         private static final String datas[][] = {
703         // Names database
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 },
708                 // organizations
709                 { null, null, "Intel ZAO", null },
710                 { null, null, "Intel Inc", null },
711                 // countries
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" }, };
715
716         //
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
720         // beginning.
721         //
722         // @param col
723         // @param startRow
724         // @return
725         //
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];
731                 }
732             }
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];
737                 }
738             }
739             // can't be
740             throw new Error();
741         }
742
743         //
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.
748         //
749         // @param num
750         // @param base
751         // @return <b>true</b> if overflow happened
752         //
753         private static boolean inc(int[] num, int base) {
754             for (int i = 0; i < num.length; i++) {
755                 if ((++num[i]) >= base) {
756                     num[i] = 0;
757                 } else {
758                     return false;
759                 }
760             }
761             return true;
762         }
763
764         /**
765          * Generates some amount of uniq names, none of which is equals to
766          * {@link #rootName}.
767          * @param howMany
768          * @return
769          */
770         public static String[] genNames(int howMany) {
771             int counts[] = new int[datasNames.length];
772             ArrayList<String> al = new ArrayList<String>();
773
774             // not really the thrifty algorithm...
775             for (int i = 0; i < howMany;) {
776
777                 //                System.out.print("#"+i+": ");
778                 //                for( int j=0; j<counts.length; j++) {
779                 //                    System.out.print(""+counts[j]+"|");
780                 //                }
781                 //                System.out.println();
782
783                 StringBuffer buf = new StringBuffer();
784                 int j = 0;
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(",");
789                 }
790                 String name = datasNames[j];
791                 String val = getData(j, counts[j]);
792                 buf.append(name).append('=').append(val);
793
794                 name = buf.toString();
795
796                 if (!(rootName.equals(name) || al.contains(name))) {
797                     ++i;
798                     al.add(name);
799                     //                    System.out.println("generated: "+name);
800                 } else {
801                     //                    System.out.println("rejected: "+name);
802                 }
803
804                 if (inc(counts, datas.length)) {
805                     // if this happened, then just add some data into 'datas'
806                     throw new Error(
807                             "cant generate so many uniq names. sorry. add some more data.");
808                 }
809             }
810             return (String[]) al.toArray(new String[al.size()]);
811         }
812
813         /**
814          * Generates some amount of uniq X500Principals, none of which is equals
815          * has a string equals to {@link #rootName}.
816          * @param howMany
817          * @return
818          */
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]);
824             }
825             return ps;
826         }
827
828     }
829
830 }
831