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
23package org.apache.harmony.security.tests.support;
24
25import java.io.IOException;
26import java.io.InputStream;
27import java.io.ObjectInputStream;
28import java.io.ObjectOutputStream;
29import java.io.Serializable;
30import java.io.StreamCorruptedException;
31import java.math.BigInteger;
32
33import java.security.InvalidKeyException;
34import java.security.NoSuchAlgorithmException;
35import java.security.NoSuchProviderException;
36import java.security.Principal;
37import java.security.Provider;
38import java.security.PublicKey;
39import java.security.Security;
40import java.security.SignatureException;
41
42import java.security.cert.*;
43import java.util.*;
44
45import 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
53public 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
832