| /* |
| * 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 |
| * @version $Revision$ |
| */ |
| |
| 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.<br> |
| * TestCertificate-s included in the CertPath will be uniq (will have |
| * different numbers passed to their ctor-s).<br> |
| * 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.<br> |
| * 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.<br> |
| * The TestCertPaths constructed via this ctor may be different (if they |
| * have different set of certificates)<br> |
| * @see TestCertUtils#genCertPath(int, int) |
| * @param certs |
| */ |
| public TestCertPath(Certificate[] certs) { |
| super("testCertPath"); |
| this.certs = certs; |
| } |
| |
| /** |
| * @see java.security.cert.CertPath#getCertificates() |
| */ |
| public List<Certificate> 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<String> getEncodings() { |
| Vector<String> v = new Vector<String>(); |
| 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<br> |
| * (String) serializedData<br> |
| * (int) number of certificates in this CertPath<br> |
| * <array of certificates> |
| * |
| * @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 <code>diff</code> 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:<br> |
| * boolean - true if this certificate has a diff string, |
| * false otherwise, followed by <br> |
| * 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.<br> |
| * Does nothing interesting, but<br> |
| * a) is not abstract, so it can be instantiated<br> |
| * b) returns Encoded form<br> |
| * |
| */ |
| 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<String>(), null); |
| } |
| |
| public synchronized Set<Provider.Service> getServices() { |
| HashSet<Provider.Service> s = new HashSet<Service>(); |
| 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.<br> |
| * @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.<br> |
| * @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<Certificate> validCerts = new ArrayList<Certificate>(); |
| 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 <code>data</code> 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.<br> |
| // <code>num</code> is interpreted as a number with a base of |
| // <code>base</code> and each digit of this number is stored as a |
| // separate num's element. |
| // |
| // @param num |
| // @param base |
| // @return <b>true</b> 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<String> al = new ArrayList<String>(); |
| |
| // not really the thrifty algorithm... |
| for (int i = 0; i < howMany;) { |
| |
| // System.out.print("#"+i+": "); |
| // for( int j=0; j<counts.length; j++) { |
| // System.out.print(""+counts[j]+"|"); |
| // } |
| // System.out.println(); |
| |
| StringBuffer buf = new StringBuffer(); |
| int j = 0; |
| for (; j < datasNames.length - 1; j++) { |
| String name = datasNames[j]; |
| String val = getData(j, counts[j]); |
| buf.append(name).append('=').append(val).append(","); |
| } |
| String name = datasNames[j]; |
| String val = getData(j, counts[j]); |
| buf.append(name).append('=').append(val); |
| |
| name = buf.toString(); |
| |
| if (!(rootName.equals(name) || al.contains(name))) { |
| ++i; |
| al.add(name); |
| // System.out.println("generated: "+name); |
| } else { |
| // System.out.println("rejected: "+name); |
| } |
| |
| if (inc(counts, datas.length)) { |
| // if this happened, then just add some data into 'datas' |
| throw new Error( |
| "cant generate so many uniq names. sorry. add some more data."); |
| } |
| } |
| return (String[]) al.toArray(new String[al.size()]); |
| } |
| |
| /** |
| * Generates some amount of uniq X500Principals, none of which is equals |
| * has a string equals to {@link #rootName}. |
| * @param howMany |
| * @return |
| */ |
| public static X500Principal[] genX500s(int howMany) { |
| String names[] = genNames(howMany); |
| X500Principal[] ps = new X500Principal[howMany]; |
| for (int i = 0; i < howMany; i++) { |
| ps[i] = new X500Principal(names[i]); |
| } |
| return ps; |
| } |
| |
| } |
| |
| } |
| |