package java.security.cert;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.Security;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.DEROutputStream;
import org.bouncycastle.asn1.OIDTokenizer;
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.util.Strings;
class CertUtil
{
static class Implementation
{
Object engine;
Provider provider;
Implementation(
Object engine,
Provider provider)
{
this.engine = engine;
this.provider = provider;
}
Object getEngine()
{
return engine;
}
Provider getProvider()
{
return provider;
}
}
/**
* see if we can find an algorithm (or its alias and what it represents) in
* the property table for the given provider.
*
* @return null if no algorithm found, an Implementation if it is.
*/
static Implementation getImplementation(
String baseName,
String algorithm,
Provider prov)
{
if (prov == null)
{
Provider[] provider = Security.getProviders();
//
// search every provider looking for the algorithm we want.
//
for (int i = 0; i != provider.length; i++)
{
Implementation imp = getImplementation(baseName, algorithm, provider[i]);
if (imp != null)
{
return imp;
}
}
return null;
}
String alias;
while ((alias = prov.getProperty("Alg.Alias." + baseName + "." + algorithm)) != null)
{
algorithm = alias;
}
String className = prov.getProperty(baseName + "." + algorithm);
if (className != null)
{
try
{
return new Implementation(Class.forName(className).newInstance(), prov);
}
catch (ClassNotFoundException e)
{
throw new IllegalStateException(
"algorithm " + algorithm + " in provider " + prov.getName() + " but no class found!");
}
catch (Exception e)
{
throw new IllegalStateException(
"algorithm " + algorithm + " in provider " + prov.getName() + " but class inaccessible: " + e.toString());
}
}
return null;
}
/**
* return an implementation for a given algorithm/provider.
* If the provider is null, we grab the first avalaible who has the required algorithm.
*
* @return null if no algorithm found, an Implementation if it is.
* @exception NoSuchProviderException if a provider is specified and not found.
*/
static Implementation getImplementation(
String baseName,
String algorithm,
String provider)
throws NoSuchProviderException
{
if (provider == null)
{
Provider[] prov = Security.getProviders();
//
// search every provider looking for the algorithm we want.
//
for (int i = 0; i != prov.length; i++)
{
Implementation imp = getImplementation(baseName, algorithm, prov[i]);
if (imp != null)
{
return imp;
}
}
}
else
{
Provider prov = Security.getProvider(provider);
if (prov == null)
{
throw new NoSuchProviderException("Provider " + provider + " not found");
}
return getImplementation(baseName, algorithm, prov);
}
return null;
}
/**
* see if we can find an algorithm (or its alias and what it represents) in
* the property table for the given provider.
*
* @return null if no algorithm found, an Implementation if it is.
*/
static Implementation getImplementation(String baseName, String algorithm,
Provider prov, Class[] ctorparamtype, Object[] ctorparam)
throws InvalidAlgorithmParameterException
{
String alias;
while ((alias = prov.getProperty("Alg.Alias." + baseName + "."
+ algorithm)) != null)
{
algorithm = alias;
}
String className = prov.getProperty(baseName + "." + algorithm);
if (className != null)
{
try
{
return new Implementation(Class.forName(className)
.getConstructor(ctorparamtype).newInstance(ctorparam),
prov);
}
catch (ClassNotFoundException e)
{
throw new IllegalStateException("algorithm " + algorithm
+ " in provider " + prov.getName()
+ " but no class found!");
}
catch (Exception e)
{
if (e instanceof InvalidAlgorithmParameterException)
{
throw (InvalidAlgorithmParameterException)e;
}
throw new IllegalStateException("algorithm " + algorithm
+ " in provider " + prov.getName()
+ " but class inaccessible!");
}
}
return null;
}
/**
* return an implementation for a given algorithm/provider. If the provider
* is null, we grab the first avalaible who has the required algorithm.
*
* @return null if no algorithm found, an Implementation if it is.
*
* @exception NoSuchProviderException
* if a provider is specified and not found.
*/
static Implementation getImplementation(String baseName, String algorithm,
String provider, Class[] ctorparamtype, Object[] ctorparam)
throws NoSuchProviderException, InvalidAlgorithmParameterException
{
if (provider == null)
{
Provider[] prov = Security.getProviders();
//
// search every provider looking for the algorithm we want.
//
for (int i = 0; i != prov.length; i++)
{
Implementation imp = getImplementation(baseName, algorithm,
prov[i], ctorparamtype, ctorparam);
if (imp != null)
{
return imp;
}
}
}
else
{
Provider prov = Security.getProvider(provider);
if (prov == null)
{
throw new NoSuchProviderException("Provider " + provider
+ " not found");
}
return getImplementation(baseName, algorithm, prov, ctorparamtype,
ctorparam);
}
return null;
}
static byte[] parseGeneralName(int type, String data) throws IOException
{
byte[] encoded = null;
switch (type)
{
case 0:
throw new IOException(
"unable to parse OtherName String representation");
case 1:
encoded = parseRfc822(data.trim());
break;
case 2:
encoded = parseDNSName(data.trim());
break;
case 3:
throw new IOException(
"unable to parse ORAddress String representation");
case 4:
encoded = parseX509Name(data.trim());
break;
case 5:
throw new IOException(
"unable to parse EDIPartyName String representation");
case 6:
encoded = parseURI(data.trim());
break;
case 7:
encoded = parseIP(data.trim());
break;
case 8:
encoded = parseOID(data.trim());
break;
default:
throw new IOException(
"unable to parse unkown type String representation");
}
return encoded;
}
/**
* Check the format of an OID.<br />
* Throw an IOException if the first component is not 0, 1 or 2 or the
* second component is greater than 39.<br />
* <br />
* User {@link org.bouncycastle.asn1.OIDTokenizer OIDTokenizer}
*
* @param the
* OID to be checked.
*
* @exception IOException
* if the first component is not 0, 1 or 2 or the second
* component is greater than 39.
*/
static byte[] parseOID(String oid) throws IOException
{
OIDTokenizer tokenizer = new OIDTokenizer(oid);
String token;
if (!tokenizer.hasMoreTokens())
{
throw new IOException("OID contains no tokens");
}
token = tokenizer.nextToken();
if (token == null)
{
throw new IOException("OID contains no tokens");
}
try
{
int test = (Integer.valueOf(token)).intValue();
if (test < 0 || test > 2)
{
throw new IOException("first token is not >= 0 and <=2");
}
if (!tokenizer.hasMoreTokens())
{
throw new IOException("OID contains only one token");
}
token = tokenizer.nextToken();
if (token == null)
{
throw new IOException("OID contains only one token");
}
test = (Integer.valueOf(token)).intValue();
if (test < 0 || test > 39)
{
throw new IOException("secon token is not >= 0 and <=39");
}
}
catch (NumberFormatException ex)
{
throw new IOException("token: " + token + ": " + ex.toString());
}
ASN1Object derData = new ASN1ObjectIdentifier(oid);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
DEROutputStream derOutStream = new DEROutputStream(outStream);
derOutStream.writeObject(derData);
derOutStream.close();
return outStream.toByteArray();
}
/**
* Parse the given IPv4 or IPv6 into DER encoded byte array representation.
*
* @param the
* IP in well known String format
*
* @return the IP as byte array
*
* @exception IOException
* if the String could not be parsed
*/
private static byte[] parseIP(String data) throws IOException
{
byte[] encoded = parseIPv4(data);
if (encoded == null)
{
encoded = parseIPv6(data);
}
if (encoded == null)
{
throw new IOException(
"unable to parse IP to DER encoded byte array");
}
return encoded;
}
/**
* Parse the given IPv4 into DER encoded byte array representation.
*
* @param the
* IP in well known String format
*
* @return the IP as byte array or <code>null</code> if not parseable
*/
private static byte[] parseIPv4(String data)
{
if (data.length() == 0)
{
return null;
}
int octet;
int octets = 0;
byte[] dst = new byte[4];
int pos = 0;
int start = 0;
while (start < data.length()
&& (pos = data.indexOf('.', start)) > start && pos - start > 3)
{
try
{
octet = (Integer.valueOf(data.substring(start, pos - start)))
.intValue();
}
catch (NumberFormatException ex)
{
return null;
}
if (octet < 0 || octet > 255)
{
return null;
}
dst[octets++] = (byte)(octet & 0xff);
start = pos + 1;
}
if (octets < 4)
{
return null;
}
return dst;
}
/**
* Parse the given IPv6 into DER encoded byte array representation.<br />
* <br />
* <b>TODO: implement this</b>
*
* @param the
* IP in well known String format
*
* @return the IP as byte array or <code>null</code> if not parseable
*/
private static byte[] parseIPv6(String data)
{
return null;
}
/**
* Parse the given URI into DER encoded byte array representation.
*
* @param the
* URI in well known String format
*
* @return the URI as byte array
*
* @exception IOException
* if the String could not be parsed
*/
private static byte[] parseURI(String data) throws IOException
{
// TODO do parsing test
ASN1Object derData = new DERIA5String(data);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
DEROutputStream derOutStream = new DEROutputStream(outStream);
derOutStream.writeObject(derData);
derOutStream.close();
return outStream.toByteArray();
}
/**
* Parse the given rfc822 addr-spec into DER encoded byte array
* representation.
*
* @param the
* rfc822 addr-spec in well known String format
*
* @return the rfc822 addr-spec as byte array
*
* @exception IOException
* if the String could not be parsed
*/
private static byte[] parseRfc822(String data) throws IOException
{
int tmpInt = data.indexOf('@');
if (tmpInt < 0 || tmpInt >= data.length() - 1)
{
throw new IOException("wrong format of rfc822Name:" + data);
}
// TODO more test for illegal charateers
ASN1Object derData = new DERIA5String(data);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
DEROutputStream derOutStream = new DEROutputStream(outStream);
derOutStream.writeObject(derData);
derOutStream.close();
return outStream.toByteArray();
}
/**
* Parse the given DNS name into DER encoded byte array representation. The
* String must be in den preffered name syntax as defined in RFC 1034.
*
* @param the
* DNS name in well known String format
*
* @return the DNS name as byte array
*
* @exception IOException
* if the String could not be parsed
*/
private static byte[] parseDNSName(String data) throws IOException
{
// TODO more test for illegal charateers
ASN1Object derData = new DERIA5String(data);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
DEROutputStream derOutStream = new DEROutputStream(outStream);
derOutStream.writeObject(derData);
derOutStream.close();
return outStream.toByteArray();
}
/**
* Parse the given X.509 name into DER encoded byte array representation.
*
* @param the
* X.509 name in well known String format
*
* @return the X.509 name as byte array
*
* @exception IOException
* if the String could not be parsed
*/
private static byte[] parseX509Name(String data) throws IOException
{
// TODO more test for illegal charateers
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
DEROutputStream derOutStream = new DEROutputStream(outStream);
derOutStream.writeObject(new X509Name(trimX509Name(data)));
derOutStream.close();
return outStream.toByteArray();
}
/**
* Returns the given name converted to upper case and all multi spaces squezed
* to one space.
**/
static String trimX509Name(String name)
{
String data = Strings.toUpperCase(name.trim());
int pos;
while ((pos = data.indexOf(" ")) >= 0)
{
data = data.substring(0, pos) + data.substring(pos + 1);
}
while ((pos = data.indexOf(" =")) >= 0)
{
data = data.substring(0, pos) + data.substring(pos + 1);
}
while ((pos = data.indexOf("= ")) >= 0)
{
data = data.substring(0, pos + 1) + data.substring(pos + 2);
}
return data;
}
}