001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Alexander V. Astapchuk
020: * @version $Revision$
021: */package org.apache.harmony.security.tests.support;
022:
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.io.ObjectInputStream;
026: import java.io.ObjectOutputStream;
027: import java.io.Serializable;
028: import java.io.StreamCorruptedException;
029: import java.math.BigInteger;
030:
031: import java.security.InvalidKeyException;
032: import java.security.NoSuchAlgorithmException;
033: import java.security.NoSuchProviderException;
034: import java.security.Principal;
035: import java.security.Provider;
036: import java.security.PublicKey;
037: import java.security.Security;
038: import java.security.SignatureException;
039:
040: import java.security.cert.*;
041: import java.util.*;
042:
043: import javax.security.auth.x500.X500Principal;
044:
045: /**
046: * The class contains various utility methods used during the java.security
047: * classes testing.
048: *
049: */
050:
051: public final class TestCertUtils {
052:
053: private TestCertUtils() {
054: throw new Error("statics only");
055: }
056:
057: /**
058: * Returns new instance of test certificate each time the method is called.
059: *
060: * @return test certificate
061: */
062: public static Certificate getCert() {
063: return new TestCertificate();
064: }
065:
066: /**
067: * Returns an array of 3 test certificates. IMP: The array returned is not
068: * real chain of certificates, it's just an array of 3 certs. The method
069: * returns new array each time it's called. The number of 3 was chosen
070: * arbitrarily and is subject to change.
071: *
072: * @return an array of 3 certificates
073: */
074: public static Certificate[] getCertChain() {
075: Certificate[] chain = { new TestCertificate(),
076: new TestCertificate(), new TestCertificate() };
077: return chain;
078: }
079:
080: /**
081: * Returns a test CertPath, which uses getCertChain() to obtain a list of
082: * certificates to store.
083: *
084: * @return test cert path
085: */
086: public static CertPath getCertPath() {
087: return new TestCertPath();
088: }
089:
090: /**
091: * Generates and returns an instance of TestCertPath.<br>
092: * TestCertificate-s included in the CertPath will be uniq (will have
093: * different numbers passed to their ctor-s).<br>
094: * The second arguments shows which number will have the first Certificate
095: * in the CertPath. The second certificate will have (startID+1) number
096: * and so on.
097: *
098: * @param howMany - shows how many TestCerts must contain the CertPath generated
099: * @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
106: .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,
151: 5, 6, 7, 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: } else {
217: buf.append(certs.length).append("\n");
218: for (int i = 0; i < certs.length; i++) {
219: buf.append("\t").append(i).append(" ");
220: buf.append(certs[i]).append("\n");
221: }
222: }
223: return buf.toString();
224: }
225:
226: /**
227: * Writes<br>
228: * (String) serializedData<br>
229: * (int) number of certificates in this CertPath<br>
230: * <array of certificates>
231: *
232: * @param out
233: * @throws IOException
234: */
235: private void writeObject(ObjectOutputStream out)
236: 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)
249: throws IOException, ClassNotFoundException {
250: String s = in.readUTF();
251: if (!serializedData.equals(s)) {
252: throw new StreamCorruptedException("expect ["
253: + serializedData + "] 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,
280: 5, 6, 7, 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,
306: 5, 6, 7, 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)
394: 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)
404: throws IOException, 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
421: TestX509Certificate {
422: public TestInvalidX509Certificate(X500Principal subj,
423: X500Principal issuer) {
424: super (subj, issuer);
425: }
426: }
427:
428: /**
429: *
430: * TestX509CErtificate.<br>
431: * Does nothing interesting, but<br>
432: * a) is not abstract, so it can be instantiated<br>
433: * b) returns Encoded form<br>
434: *
435: */
436: public static class TestX509Certificate extends X509Certificate {
437: private X500Principal subject;
438:
439: private X500Principal issuer;
440:
441: public TestX509Certificate(X500Principal subj,
442: X500Principal issuer) {
443: this .subject = subj;
444: this .issuer = issuer;
445: }
446:
447: public X500Principal getIssuerX500Principal() {
448: return issuer;
449: }
450:
451: public X500Principal getSubjectX500Principal() {
452: return subject;
453: }
454:
455: /**
456: * The encoded for of this X509Certificate is a byte array where
457: * first are bytes of encoded form of Subject (as X500Principal),
458: * followed by one zero byte
459: * and followed by the encoded form of Issuer (as X500Principal)
460: *
461: */
462: public byte[] getEncoded() throws CertificateEncodingException {
463: byte[] asubj = subject.getEncoded();
464: byte[] aissuer = issuer.getEncoded();
465: byte[] data = new byte[asubj.length + aissuer.length + 1];
466:
467: System.arraycopy(asubj, 0, data, 0, asubj.length);
468: //data[asubj.length] = 0;
469: System.arraycopy(aissuer, 0, data, asubj.length + 1,
470: aissuer.length);
471: return data;
472: }
473:
474: public void checkValidity() throws CertificateExpiredException,
475: CertificateNotYetValidException {
476: }
477:
478: public void checkValidity(Date date)
479: throws CertificateExpiredException,
480: CertificateNotYetValidException {
481: }
482:
483: public int getBasicConstraints() {
484: return 0;
485: }
486:
487: public Principal getIssuerDN() {
488: return null;
489: }
490:
491: public boolean[] getIssuerUniqueID() {
492: return null;
493: }
494:
495: public boolean[] getKeyUsage() {
496: return null;
497: }
498:
499: public Date getNotAfter() {
500: return null;
501: }
502:
503: public Date getNotBefore() {
504: return null;
505: }
506:
507: public BigInteger getSerialNumber() {
508: return null;
509: }
510:
511: public String getSigAlgName() {
512: return null;
513: }
514:
515: public String getSigAlgOID() {
516: return null;
517: }
518:
519: public byte[] getSigAlgParams() {
520: return null;
521: }
522:
523: public byte[] getSignature() {
524: return null;
525: }
526:
527: public Principal getSubjectDN() {
528: return null;
529: }
530:
531: public boolean[] getSubjectUniqueID() {
532: return null;
533: }
534:
535: public byte[] getTBSCertificate()
536: throws CertificateEncodingException {
537: return null;
538: }
539:
540: public int getVersion() {
541: return 0;
542: }
543:
544: public Set getCriticalExtensionOIDs() {
545: return null;
546: }
547:
548: public byte[] getExtensionValue(String oid) {
549: return null;
550: }
551:
552: public Set getNonCriticalExtensionOIDs() {
553: return null;
554: }
555:
556: public boolean hasUnsupportedCriticalExtension() {
557: return false;
558: }
559:
560: public PublicKey getPublicKey() {
561: return null;
562: }
563:
564: public String toString() {
565: return null;
566: }
567:
568: public void verify(PublicKey key, String sigProvider)
569: throws CertificateException, NoSuchAlgorithmException,
570: InvalidKeyException, NoSuchProviderException,
571: SignatureException {
572:
573: }
574:
575: public void verify(PublicKey key) throws CertificateException,
576: NoSuchAlgorithmException, InvalidKeyException,
577: NoSuchProviderException, SignatureException {
578:
579: }
580: }
581:
582: /**
583: * TestProvider. Does nothing, but pretends to
584: * implement X.509 CertificateFactory.
585: */
586: public static class TestProvider extends Provider {
587:
588: private Provider.Service serv;
589:
590: public TestProvider(String name, double version, String info) {
591: super (name, version, info);
592: serv = new Provider.Service(this , "CertificateFactory",
593: "X.509", TestFactorySpi.class.getName(),
594: new ArrayList(), null);
595: }
596:
597: public synchronized Set getServices() {
598: HashSet s = new HashSet();
599: s.add(serv);
600: return s;
601: }
602: }
603:
604: /**
605: * Some kind of Certificate Factory, used during unit testing.
606: *
607: *
608: */
609: public static class TestFactorySpi extends CertificateFactorySpi {
610:
611: /**
612: * Tries to create an instance of TestX509Certificate, basing
613: * on the presumption that its {@link TestX509Certificate#getEncoded()
614: * encoded} form is stored.<br>
615: * @throws CertificateException is the presumption is not met or if
616: * any IO problem occurs.
617: */
618: public Certificate engineGenerateCertificate(InputStream is)
619: throws CertificateException {
620: byte[] data = new byte[0];
621: byte[] chunk = new byte[1024];
622: int len;
623: try {
624: while ((len = is.read(chunk)) > 0) {
625: byte[] tmp = new byte[data.length + len];
626: System.arraycopy(data, 0, tmp, 0, data.length);
627: System.arraycopy(chunk, 0, tmp, data.length, len);
628: data = tmp;
629: }
630: } catch (IOException ex) {
631: throw new CertificateException("IO problem", ex);
632: }
633: int pos = Arrays.binarySearch(data, (byte) 0);
634: if (pos < 0) {
635: throw new CertificateException("invalid format");
636: }
637: byte[] subjNameData = new byte[pos];
638: System.arraycopy(data, 0, subjNameData, 0,
639: subjNameData.length);
640: byte[] issNameData = new byte[data.length - pos - 1];
641: System.arraycopy(data, pos + 1, issNameData, 0,
642: issNameData.length);
643: X500Principal subjName = new X500Principal(subjNameData);
644: X500Principal issName = new X500Principal(issNameData);
645: return new TestX509Certificate(subjName, issName);
646: }
647:
648: /**
649: * Not supported yet.
650: * @throws UnsupportedOperationException
651: */
652: public Collection engineGenerateCertificates(
653: InputStream inStream) throws CertificateException {
654: throw new UnsupportedOperationException("not yet.");
655: }
656:
657: /**
658: * Not supported yet.
659: * @throws UnsupportedOperationException
660: */
661: public CRL engineGenerateCRL(InputStream inStream)
662: throws CRLException {
663: throw new UnsupportedOperationException("not yet.");
664: }
665:
666: /**
667: * Not supported yet.
668: * @throws UnsupportedOperationException
669: */
670: public Collection engineGenerateCRLs(InputStream inStream)
671: throws CRLException {
672: throw new UnsupportedOperationException("not yet.");
673: }
674:
675: /**
676: * Returns an instance of TestCertPath.<br>
677: * @throws CertificateException if
678: * a) any of Certificates passed is not an instance of X509Certificate
679: * b) any of Certificates passed is an instance of TestInvalidX509Certificate
680: */
681: public CertPath engineGenerateCertPath(List certs)
682: throws CertificateException {
683: ArrayList validCerts = new ArrayList();
684: for (Iterator i = certs.iterator(); i.hasNext();) {
685: Certificate c = (Certificate) i.next();
686: if (!(c instanceof X509Certificate)) {
687: throw new CertificateException("Not X509: " + c);
688: }
689: if (c instanceof TestInvalidX509Certificate) {
690: throw new CertificateException(
691: "Invalid (test) X509: " + c);
692: }
693: validCerts.add(c);
694: }
695: Certificate[] acerts = new Certificate[validCerts.size()];
696: validCerts.toArray(acerts);
697: return new TestCertPath(acerts);
698: }
699: }
700:
701: /**
702: * Utility class used to generate some amount of uniq names.
703: */
704: public static class UniGen {
705: public static final String rootName = "CN=Alex Astapchuk, OU=SSG, O=Intel ZAO, C=RU";
706:
707: private static final String datasNames[] = { "CN", "OU", "O",
708: "C" };
709:
710: private static final String datas[][] = {
711: // Names database
712: { "Alex Astapchuk", null, null, null },
713: { "John Doe", null, null, null },
714: // 'organisation unit'-s
715: { null, "SSG", null, null },
716: { null, "SSG/DRL", null, null },
717: // organizations
718: { null, null, "Intel ZAO", null },
719: { null, null, "Intel Inc", null },
720: // countries
721: { null, null, null, "RU" }, { null, null, null, "US" },
722: { null, null, null, "GB" }, { null, null, null, "JA" },
723: { null, null, null, "KO" }, { null, null, null, "TW" }, };
724:
725: //
726: // Returns a string from <code>data</code> from a given column and
727: // position. The positions are looked for first non-null entry. If there
728: // are no non empty items left, then it scans column starting from the
729: // beginning.
730: //
731: // @param col
732: // @param startRow
733: // @return
734: //
735: private static String getData(int col, int startRow) {
736: startRow = startRow % datas.length;
737: for (int i = startRow; i < datas.length; i++) {
738: if (datas[i][col] != null) {
739: return datas[i][col];
740: }
741: }
742: // no non-null entries left, check from the beginning
743: for (int i = 0; i < datas.length; i++) {
744: if (datas[i][col] != null) {
745: return datas[i][col];
746: }
747: }
748: // can't be
749: throw new Error();
750: }
751:
752: //
753: // Increments a num.<br>
754: // <code>num</code> is interpreted as a number with a base of
755: // <code>base</code> and each digit of this number is stored as a
756: // separate num's element.
757: //
758: // @param num
759: // @param base
760: // @return <b>true</b> if overflow happened
761: //
762: private static boolean inc(int[] num, int base) {
763: for (int i = 0; i < num.length; i++) {
764: if ((++num[i]) >= base) {
765: num[i] = 0;
766: } else {
767: return false;
768: }
769: }
770: return true;
771: }
772:
773: /**
774: * Generates some amount of uniq names, none of which is equals to
775: * {@link #rootName}.
776: * @param howMany
777: * @return
778: */
779: public static String[] genNames(int howMany) {
780: int counts[] = new int[datasNames.length];
781: ArrayList al = new ArrayList();
782:
783: // not really the thrifty algorithm...
784: for (int i = 0; i < howMany;) {
785:
786: // System.out.print("#"+i+": ");
787: // for( int j=0; j<counts.length; j++) {
788: // System.out.print(""+counts[j]+"|");
789: // }
790: // System.out.println();
791:
792: StringBuffer buf = new StringBuffer();
793: int j = 0;
794: for (; j < datasNames.length - 1; j++) {
795: String name = datasNames[j];
796: String val = getData(j, counts[j]);
797: buf.append(name).append('=').append(val)
798: .append(",");
799: }
800: String name = datasNames[j];
801: String val = getData(j, counts[j]);
802: buf.append(name).append('=').append(val);
803:
804: name = buf.toString();
805:
806: if (!(rootName.equals(name) || al.contains(name))) {
807: ++i;
808: al.add(name);
809: // System.out.println("generated: "+name);
810: } else {
811: // System.out.println("rejected: "+name);
812: }
813:
814: if (inc(counts, datas.length)) {
815: // if this happened, then just add some data into 'datas'
816: throw new Error(
817: "cant generate so many uniq names. sorry. add some more data.");
818: }
819: }
820: return (String[]) al.toArray(new String[al.size()]);
821: }
822:
823: /**
824: * Generates some amount of uniq X500Principals, none of which is equals
825: * has a string equals to {@link #rootName}.
826: * @param howMany
827: * @return
828: */
829: public static X500Principal[] genX500s(int howMany) {
830: String names[] = genNames(howMany);
831: X500Principal[] ps = new X500Principal[howMany];
832: for (int i = 0; i < howMany; i++) {
833: ps[i] = new X500Principal(names[i]);
834: }
835: return ps;
836: }
837:
838: }
839:
840: }
|