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