/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @author Alexander V. Astapchuk */ package org.apache.harmony.security.tests.support; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.io.StreamCorruptedException; import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Principal; import java.security.Provider; import java.security.PublicKey; import java.security.Security; import java.security.SignatureException; import java.security.cert.*; import java.util.*; import javax.security.auth.x500.X500Principal; /** * The class contains various utility methods used during the java.security * classes testing. */ public final class TestCertUtils { private TestCertUtils() { throw new Error("statics only"); } /** * Returns new instance of test certificate each time the method is called. * * @return test certificate */ public static Certificate getCert() { return new TestCertificate(); } /** * Returns an array of 3 test certificates. IMP: The array returned is not * real chain of certificates, it's just an array of 3 certs. The method * returns new array each time it's called. The number of 3 was chosen * arbitrarily and is subject to change. * * @return an array of 3 certificates */ public static Certificate[] getCertChain() { Certificate[] chain = { new TestCertificate(), new TestCertificate(), new TestCertificate() }; return chain; } /** * Returns a test CertPath, which uses getCertChain() to obtain a list of * certificates to store. * * @return test cert path */ public static CertPath getCertPath() { return new TestCertPath(); } /** * Generates and returns an instance of TestCertPath.
* TestCertificate-s included in the CertPath will be uniq (will have * different numbers passed to their ctor-s).
* The second arguments shows which number will have the first Certificate * in the CertPath. The second certificate will have (startID+1) number * and so on. * * @param howMany - shows how many TestCerts must contain the CertPath generated * @param startID - specifies the starting ID which the first certificate will have * @return TestCertPath */ public static CertPath genCertPath(int howMany, int startID) { Certificate[] certs = new Certificate[howMany]; for (int i = 0; i < howMany; i++) { certs[i] = new TestCertificate(Integer.toString(startID + i)); } return new TestCertPath(certs); } private static Provider provider = null; private static final String providerName = "TstPrvdr"; /** * A Principal used to form rootCA's certificate */ public static final X500Principal rootPrincipal = new X500Principal( UniGen.rootName); /** * Some fake rootCA's certificate. */ public static final X509Certificate rootCA = new TestX509Certificate( rootPrincipal, rootPrincipal); public static void install_test_x509_factory() { if (provider == null) { provider = new TestProvider(providerName, 0.01, "Test provider for serialization testing"); Security.insertProviderAt(provider, 1); } } public static void uninstall_test_x509_factory() { if (provider != null) { Security.removeProvider(providerName); provider = null; } } /** * The class represents test certificate path. */ public static final class TestCertPath extends CertPath implements Serializable { private static final byte[] encoded = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF }; private static final String serializedData = "Just a dummy string to be serialized instead of real data"; private Certificate[] certs; /** * Default ctor for TestCertPath. Uses {@link TestCertUtils#getCertChain()} * to obtain list of certificates.
* All TestCertPath-s constructed via this ctor will be equals() to each * other. */ public TestCertPath() { super("testCertPath"); certs = getCertChain(); } /** * Constructs TestCertPath and keeps the given array of certificates.
* The TestCertPaths constructed via this ctor may be different (if they * have different set of certificates)
* * @param certs * @see TestCertUtils#genCertPath(int, int) */ public TestCertPath(Certificate[] certs) { super("testCertPath"); this.certs = certs; } /** * @see java.security.cert.CertPath#getCertificates() */ public List getCertificates() { return Arrays.asList(certs); } /** * @see java.security.cert.CertPath#getEncoded() */ public byte[] getEncoded() throws CertificateEncodingException { return encoded.clone(); } /** * @see java.security.cert.CertPath#getEncoded(java.lang.String) */ public byte[] getEncoded(String encoding) throws CertificateEncodingException { return encoded.clone(); } /** * @see java.security.cert.CertPath#getEncodings() */ public Iterator getEncodings() { Vector v = new Vector(); v.add("myTestEncoding"); return v.iterator(); } public String toString() { StringBuffer buf = new StringBuffer(200); buf.append("TestCertPath. certs count="); if (certs == null) { buf.append("0\n"); } else { buf.append(certs.length).append("\n"); for (int i = 0; i < certs.length; i++) { buf.append("\t").append(i).append(" "); buf.append(certs[i]).append("\n"); } } return buf.toString(); } /** * Writes
* (String) serializedData
* (int) number of certificates in this CertPath
* * * @param out * @throws IOException */ private void writeObject(ObjectOutputStream out) throws IOException { out.writeUTF(serializedData); if (certs == null) { out.writeInt(0); } else { out.writeInt(certs.length); for (int i = 0; i < certs.length; i++) { out.writeObject(certs[i]); } } } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { String s = in.readUTF(); if (!serializedData.equals(s)) { throw new StreamCorruptedException("expect [" + serializedData + "] got [" + s + "]"); } int count = in.readInt(); certs = new Certificate[count]; for (int i = 0; i < count; i++) { certs[i] = (Certificate) in.readObject(); } } protected Object writeReplace() { return this; } protected Object readResolve() { return this; } } /** * The class represents empty PublicKey. */ public static final class TestPublicKey implements PublicKey { private static final String algo = "testPublicKeyAlgorithm"; private static final byte[] encoded = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF }; private static final String format = "testPublicKeyFormat"; public String getAlgorithm() { return algo; } public byte[] getEncoded() { return encoded.clone(); } public String getFormat() { return format; } } /** * The class represents test certificate. */ public static class TestCertificate extends Certificate implements Serializable { private static final byte[] encoded = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF }; public static final String TYPE = "Test"; // // A String that makes different TestCertificates to be different. // private String diff = null; /** * Default ctor. All the TestCertificate-s created with this ctor are equals() to each other. * Use TestCertificate(String) if you need non equal TestCertificate-s. */ public TestCertificate() { super(TYPE); } /** * A special purpose ctor. Pass different String-s to have different TestCertificates. * TestCertificate-s with the same String passed to this ctor are considered equal. */ public TestCertificate(String diff) { super(TYPE); this.diff = diff; } /** * A ctor that allows to specify both the TYPE of certificate and the * diff. Leave the diff null when no difference needed. * * @param diff * @param type */ public TestCertificate(String diff, String type) { super(type); this.diff = diff; } public byte[] getEncoded() throws CertificateEncodingException { return encoded.clone(); } public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { // do nothing } public void verify(PublicKey key, String sigProvider) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { // do nothing } public String toString() { return "Test certificate - for unit testing only"; } public boolean equals(Object obj) { if (obj == null || !(obj instanceof TestCertificate)) { return false; } TestCertificate that = (TestCertificate) obj; if (this == that) { return true; } if (this.diff == null) { return that.diff == null; } return this.diff.equals(that.diff); } public PublicKey getPublicKey() { return new TestPublicKey(); } /** * Writes:
* boolean - true if this certificate has a diff string, * false otherwise, followed by
* writeUTF() of string (if presented) * * @param out * @throws IOException */ private void writeObject(ObjectOutputStream out) throws IOException { if (diff == null) { out.writeBoolean(false); } else { out.writeBoolean(false); out.writeUTF(diff); } } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { boolean hasDiffString = in.readBoolean(); if (hasDiffString) { diff = in.readUTF(); } } protected Object writeReplace() { return this; } protected Object readResolve() { return this; } } public static class TestInvalidX509Certificate extends TestX509Certificate { public TestInvalidX509Certificate(X500Principal subj, X500Principal issuer) { super(subj, issuer); } } /** * TestX509CErtificate.
* Does nothing interesting, but
* a) is not abstract, so it can be instantiated
* b) returns Encoded form
*/ public static class TestX509Certificate extends X509Certificate { private X500Principal subject; private X500Principal issuer; public TestX509Certificate(X500Principal subj, X500Principal issuer) { this.subject = subj; this.issuer = issuer; } public X500Principal getIssuerX500Principal() { return issuer; } public X500Principal getSubjectX500Principal() { return subject; } /** * The encoded for of this X509Certificate is a byte array where * first are bytes of encoded form of Subject (as X500Principal), * followed by one zero byte * and followed by the encoded form of Issuer (as X500Principal) */ public byte[] getEncoded() throws CertificateEncodingException { byte[] asubj = subject.getEncoded(); byte[] aissuer = issuer.getEncoded(); byte[] data = new byte[asubj.length + aissuer.length + 1]; System.arraycopy(asubj, 0, data, 0, asubj.length); //data[asubj.length] = 0; System .arraycopy(aissuer, 0, data, asubj.length + 1, aissuer.length); return data; } public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException { } public void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException { } public int getBasicConstraints() { return 0; } public Principal getIssuerDN() { return null; } public boolean[] getIssuerUniqueID() { return null; } public boolean[] getKeyUsage() { return null; } public Date getNotAfter() { return null; } public Date getNotBefore() { return null; } public BigInteger getSerialNumber() { return null; } public String getSigAlgName() { return null; } public String getSigAlgOID() { return null; } public byte[] getSigAlgParams() { return null; } public byte[] getSignature() { return null; } public Principal getSubjectDN() { return null; } public boolean[] getSubjectUniqueID() { return null; } public byte[] getTBSCertificate() throws CertificateEncodingException { return null; } public int getVersion() { return 0; } public Set getCriticalExtensionOIDs() { return null; } public byte[] getExtensionValue(String oid) { return null; } public Set getNonCriticalExtensionOIDs() { return null; } public boolean hasUnsupportedCriticalExtension() { return false; } public PublicKey getPublicKey() { return null; } public String toString() { return null; } public void verify(PublicKey key, String sigProvider) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { } public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { } } /** * TestProvider. Does nothing, but pretends to * implement X.509 CertificateFactory. */ public static class TestProvider extends Provider { private Provider.Service serv; public TestProvider(String name, double version, String info) { super(name, version, info); serv = new Provider.Service(this, "CertificateFactory", "X.509", TestFactorySpi.class.getName(), new ArrayList(), null); } public synchronized Set getServices() { HashSet s = new HashSet(); s.add(serv); return s; } } /** * Some kind of Certificate Factory, used during unit testing. */ public static class TestFactorySpi extends CertificateFactorySpi { /** * Tries to create an instance of TestX509Certificate, basing * on the presumption that its {@link TestX509Certificate#getEncoded() * encoded} form is stored.
* * @throws CertificateException is the presumption is not met or if * any IO problem occurs. */ public Certificate engineGenerateCertificate(InputStream is) throws CertificateException { byte[] data = new byte[0]; byte[] chunk = new byte[1024]; int len; try { while ((len = is.read(chunk)) > 0) { byte[] tmp = new byte[data.length + len]; System.arraycopy(data, 0, tmp, 0, data.length); System.arraycopy(chunk, 0, tmp, data.length, len); data = tmp; } } catch (IOException ex) { throw new CertificateException("IO problem", ex); } int pos = Arrays.binarySearch(data, (byte) 0); if (pos < 0) { throw new CertificateException("invalid format"); } byte[] subjNameData = new byte[pos]; System.arraycopy(data, 0, subjNameData, 0, subjNameData.length); byte[] issNameData = new byte[data.length - pos - 1]; System.arraycopy(data, pos + 1, issNameData, 0, issNameData.length); X500Principal subjName = new X500Principal(subjNameData); X500Principal issName = new X500Principal(issNameData); return new TestX509Certificate(subjName, issName); } /** * Not supported yet. * * @throws UnsupportedOperationException */ public Collection engineGenerateCertificates(InputStream inStream) throws CertificateException { throw new UnsupportedOperationException("not yet."); } /** * Not supported yet. * * @throws UnsupportedOperationException */ public CRL engineGenerateCRL(InputStream inStream) throws CRLException { throw new UnsupportedOperationException("not yet."); } /** * Not supported yet. * * @throws UnsupportedOperationException */ public Collection engineGenerateCRLs(InputStream inStream) throws CRLException { throw new UnsupportedOperationException("not yet."); } /** * Returns an instance of TestCertPath.
* * @throws CertificateException if * a) any of Certificates passed is not an instance of X509Certificate * b) any of Certificates passed is an instance of TestInvalidX509Certificate */ public CertPath engineGenerateCertPath(List certs) throws CertificateException { ArrayList validCerts = new ArrayList(); for (Iterator i = certs.iterator(); i.hasNext(); ) { Certificate c = (Certificate) i.next(); if (!(c instanceof X509Certificate)) { throw new CertificateException("Not X509: " + c); } if (c instanceof TestInvalidX509Certificate) { throw new CertificateException("Invalid (test) X509: " + c); } validCerts.add(c); } Certificate[] acerts = new Certificate[validCerts.size()]; validCerts.toArray(acerts); return new TestCertPath(acerts); } } /** * Utility class used to generate some amount of uniq names. */ public static class UniGen { public static final String rootName = "CN=Alex Astapchuk, OU=SSG, O=Intel ZAO, C=RU"; private static final String datasNames[] = { "CN", "OU", "O", "C" }; private static final String datas[][] = { // Names database { "Alex Astapchuk", null, null, null }, { "John Doe", null, null, null }, // 'organisation unit'-s { null, "SSG", null, null }, { null, "SSG/DRL", null, null }, // organizations { null, null, "Intel ZAO", null }, { null, null, "Intel Inc", null }, // countries { null, null, null, "RU" }, { null, null, null, "US" }, { null, null, null, "GB" }, { null, null, null, "JA" }, { null, null, null, "KO" }, { null, null, null, "TW" }, }; // // Returns a string from data from a given column and // position. The positions are looked for first non-null entry. If there // are no non empty items left, then it scans column starting from the // beginning. // // @param col // @param startRow // @return // private static String getData(int col, int startRow) { startRow = startRow % datas.length; for (int i = startRow; i < datas.length; i++) { if (datas[i][col] != null) { return datas[i][col]; } } // no non-null entries left, check from the beginning for (int i = 0; i < datas.length; i++) { if (datas[i][col] != null) { return datas[i][col]; } } // can't be throw new Error(); } // // Increments a num.
// num is interpreted as a number with a base of // base and each digit of this number is stored as a // separate num's element. // // @param num // @param base // @return true if overflow happened // private static boolean inc(int[] num, int base) { for (int i = 0; i < num.length; i++) { if ((++num[i]) >= base) { num[i] = 0; } else { return false; } } return true; } /** * Generates some amount of uniq names, none of which is equals to * {@link #rootName}. * * @param howMany * @return */ public static String[] genNames(int howMany) { int counts[] = new int[datasNames.length]; ArrayList al = new ArrayList(); // not really the thrifty algorithm... for (int i = 0; i < howMany; ) { // System.out.print("#"+i+": "); // for( int j=0; j