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