--- /dev/null
+// This is just a cut-and-paste of a bunch of files from Bouncycastle
+package org.xwt.shoehorn3;
+
+import java.io.*;
+import java.math.BigInteger;
+import java.util.*;
+import java.text.*;
+
+abstract class ASN1OctetString
+ extends DERObject
+{
+ byte[] string;
+
+ /**
+ * return an Octet String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want.
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static ASN1OctetString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * return an Octet String from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static ASN1OctetString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof ASN1OctetString)
+ {
+ return (ASN1OctetString)obj;
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ Vector v = new Vector();
+ Enumeration e = ((ASN1Sequence)obj).getObjects();
+
+ while (e.hasMoreElements())
+ {
+ v.addElement(e.nextElement());
+ }
+
+ return new BERConstructedOctetString(v);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * @param string the octets making up the octet string.
+ */
+ public ASN1OctetString(
+ byte[] string)
+ {
+ this.string = string;
+ }
+
+ public ASN1OctetString(
+ DEREncodable obj)
+ {
+ try
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+
+ dOut.writeObject(obj);
+ dOut.close();
+
+ this.string = bOut.toByteArray();
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("Error processing object : " + e.toString());
+ }
+ }
+
+ public byte[] getOctets()
+ {
+ return string;
+ }
+
+ public int hashCode()
+ {
+ byte[] b = this.getOctets();
+ int value = 0;
+
+ for (int i = 0; i != b.length; i++)
+ {
+ value ^= (b[i] & 0xff) << (i % 4);
+ }
+
+ return value;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == null || !(o instanceof DEROctetString))
+ {
+ return false;
+ }
+
+ DEROctetString other = (DEROctetString)o;
+
+ byte[] b1 = other.getOctets();
+ byte[] b2 = this.getOctets();
+
+ if (b1.length != b2.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != b1.length; i++)
+ {
+ if (b1[i] != b2[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ abstract void encode(DEROutputStream out)
+ throws IOException;
+}
+
+
+class ASN1OutputStream
+ extends DEROutputStream
+{
+ public ASN1OutputStream(
+ OutputStream os)
+ {
+ super(os);
+ }
+
+ public void writeObject(
+ Object obj)
+ throws IOException
+ {
+ if (obj == null)
+ {
+ writeNull();
+ }
+ else if (obj instanceof DERObject)
+ {
+ ((DERObject)obj).encode(this);
+ }
+ else if (obj instanceof DEREncodable)
+ {
+ ((DEREncodable)obj).getDERObject().encode(this);
+ }
+ else
+ {
+ throw new IOException("object not ASN1Encodable");
+ }
+ }
+}
+
+
+abstract class ASN1Sequence
+ extends DERObject
+{
+ private Vector seq = new Vector();
+
+ /**
+ * return an ASN1Sequence from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static ASN1Sequence getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof ASN1Sequence)
+ {
+ return (ASN1Sequence)obj;
+ }
+
+ throw new IllegalArgumentException("unknown object in getInstance");
+ }
+
+ /**
+ * Return an ASN1 sequence from a tagged object. There is a special
+ * case here, if an object appears to have been explicitly tagged on
+ * reading but we were expecting it to be implictly tagged in the
+ * normal course of events it indicates that we lost the surrounding
+ * sequence - so we need to add it back (this will happen if the tagged
+ * object is a sequence that contains other sequences). If you are
+ * dealing with implicitly tagged sequences you really <b>should</b>
+ * be using this method.
+ *
+ * @param obj the tagged object.
+ * @param explicit true if the object is meant to be explicitly tagged,
+ * false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static ASN1Sequence getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ if (explicit)
+ {
+ if (!obj.isExplicit())
+ {
+ throw new IllegalArgumentException("object implicit - explicit expected.");
+ }
+
+ return (ASN1Sequence)obj.getObject();
+ }
+ else
+ {
+ //
+ // constructed object which appears to be explicitly tagged
+ // when it should be implicit means we have to add the
+ // surrounding sequence.
+ //
+ if (obj.isExplicit())
+ {
+ ASN1Sequence seq;
+
+ if (obj instanceof BERTaggedObject)
+ {
+ seq = new BERConstructedSequence();
+ }
+ else
+ {
+ seq = new DERConstructedSequence();
+ }
+
+ seq.addObject(obj.getObject());
+
+ return seq;
+ }
+ else
+ {
+ ASN1Sequence seq;
+
+ if (obj.getObject() instanceof ASN1Sequence)
+ {
+ return (ASN1Sequence)obj.getObject();
+ }
+ }
+ }
+
+ throw new IllegalArgumentException(
+ "unknown object in getInstanceFromTagged");
+ }
+
+ public Enumeration getObjects()
+ {
+ return seq.elements();
+ }
+
+ /**
+ * return the object at the sequence postion indicated by index.
+ *
+ * @param the sequence number (starting at zero) of the object
+ * @return the object at the sequence postion indicated by index.
+ */
+ public DEREncodable getObjectAt(
+ int index)
+ {
+ return (DEREncodable)seq.elementAt(index);
+ }
+
+ /**
+ * return the number of objects in this sequence.
+ *
+ * @return the number of objects in this sequence.
+ */
+ public int size()
+ {
+ return seq.size();
+ }
+
+ public int hashCode()
+ {
+ Enumeration e = this.getObjects();
+ int hashCode = 0;
+
+ while (e.hasMoreElements())
+ {
+ hashCode ^= e.nextElement().hashCode();
+ }
+
+ return hashCode;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == null || !(o instanceof ASN1Sequence))
+ {
+ return false;
+ }
+
+ ASN1Sequence other = (ASN1Sequence)o;
+
+ if (this.size() != other.size())
+ {
+ return false;
+ }
+
+ Enumeration s1 = this.getObjects();
+ Enumeration s2 = other.getObjects();
+
+ while (s1.hasMoreElements())
+ {
+ if (!s1.nextElement().equals(s2.nextElement()))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected void addObject(
+ DEREncodable obj)
+ {
+ seq.addElement(obj);
+ }
+
+ abstract void encode(DEROutputStream out)
+ throws IOException;
+}
+
+
+abstract class ASN1Set
+ extends DERObject
+{
+ protected Vector set = new Vector();
+
+ /**
+ * return an ASN1Set from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static ASN1Set getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof ASN1Set)
+ {
+ return (ASN1Set)obj;
+ }
+
+ throw new IllegalArgumentException("unknown object in getInstance");
+ }
+
+ /**
+ * Return an ASN1 set from a tagged object. There is a special
+ * case here, if an object appears to have been explicitly tagged on
+ * reading but we were expecting it to be implictly tagged in the
+ * normal course of events it indicates that we lost the surrounding
+ * set - so we need to add it back (this will happen if the tagged
+ * object is a sequence that contains other sequences). If you are
+ * dealing with implicitly tagged sets you really <b>should</b>
+ * be using this method.
+ *
+ * @param obj the tagged object.
+ * @param explicit true if the object is meant to be explicitly tagged
+ * false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static ASN1Set getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ if (explicit)
+ {
+ if (!obj.isExplicit())
+ {
+ throw new IllegalArgumentException("object implicit - explicit expected.");
+ }
+
+ return (ASN1Set)obj.getObject();
+ }
+ else
+ {
+ //
+ // constructed object which appears to be explicitly tagged
+ // and it's really implicit means we have to add the
+ // surrounding sequence.
+ //
+ if (obj.isExplicit())
+ {
+ ASN1Set set = new DERSet(obj.getObject());
+
+ return set;
+ }
+ else
+ {
+ //
+ // in this case the parser returns a sequence, convert it
+ // into a set.
+ //
+ DEREncodableVector v = new DEREncodableVector();
+
+ if (obj.getObject() instanceof ASN1Sequence)
+ {
+ ASN1Sequence s = (ASN1Sequence)obj.getObject();
+ Enumeration e = s.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ v.add((DEREncodable)e.nextElement());
+ }
+
+ return new DERSet(v);
+ }
+ }
+ }
+
+ throw new IllegalArgumentException(
+ "unknown object in getInstanceFromTagged");
+ }
+
+ public ASN1Set()
+ {
+ }
+
+ public Enumeration getObjects()
+ {
+ return set.elements();
+ }
+
+ /**
+ * return the object at the set postion indicated by index.
+ *
+ * @param the set number (starting at zero) of the object
+ * @return the object at the set postion indicated by index.
+ */
+ public DEREncodable getObjectAt(
+ int index)
+ {
+ return (DEREncodable)set.elementAt(index);
+ }
+
+ /**
+ * return the number of objects in this set.
+ *
+ * @return the number of objects in this set.
+ */
+ public int size()
+ {
+ return set.size();
+ }
+
+ public int hashCode()
+ {
+ Enumeration e = this.getObjects();
+ int hashCode = 0;
+
+ while (e.hasMoreElements())
+ {
+ hashCode ^= e.nextElement().hashCode();
+ }
+
+ return hashCode;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == null || !(o instanceof ASN1Set))
+ {
+ return false;
+ }
+
+ ASN1Set other = (ASN1Set)o;
+
+ if (this.size() != other.size())
+ {
+ return false;
+ }
+
+ Enumeration s1 = this.getObjects();
+ Enumeration s2 = other.getObjects();
+
+ while (s1.hasMoreElements())
+ {
+ if (!s1.nextElement().equals(s2.nextElement()))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected void addObject(
+ DEREncodable obj)
+ {
+ set.addElement(obj);
+ }
+
+ abstract void encode(DEROutputStream out)
+ throws IOException;
+}
+
+
+/**
+ * ASN.1 TaggedObject - in ASN.1 nottation this is any object proceeded by
+ * a [n] where n is some number - these are assume to follow the construction
+ * rules (as with sequences).
+ */
+abstract class ASN1TaggedObject
+ extends DERObject
+{
+ int tagNo;
+ boolean empty = false;
+ boolean explicit = true;
+ DEREncodable obj = null;
+
+ /**
+ * @param tagNo the tag number for this object.
+ * @param obj the tagged object.
+ */
+ public ASN1TaggedObject(
+ int tagNo,
+ DEREncodable obj)
+ {
+ this.explicit = true;
+ this.tagNo = tagNo;
+ this.obj = obj;
+ }
+
+ /**
+ * @param explicit true if the object is explicitly tagged.
+ * @param tagNo the tag number for this object.
+ * @param obj the tagged object.
+ */
+ public ASN1TaggedObject(
+ boolean explicit,
+ int tagNo,
+ DEREncodable obj)
+ {
+ this.explicit = explicit;
+ this.tagNo = tagNo;
+ this.obj = obj;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == null || !(o instanceof ASN1TaggedObject))
+ {
+ return false;
+ }
+
+ ASN1TaggedObject other = (ASN1TaggedObject)o;
+
+ if(tagNo != other.tagNo || empty != other.empty || explicit != other.explicit)
+ {
+ return false;
+ }
+
+ if(obj == null)
+ {
+ if(other.obj != null)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if(!(obj.equals(other.obj)))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public int getTagNo()
+ {
+ return tagNo;
+ }
+
+ /**
+ * return whether or not the object may be explicitly tagged.
+ * <p>
+ * Note: if the object has been read from an input stream, the only
+ * time you can be sure if isExplicit is returning the true state of
+ * affairs is if it returns false. An implicitly tagged object may appear
+ * to be explicitly tagged, so you need to understand the context under
+ * which the reading was done as well, see getObject below.
+ */
+ public boolean isExplicit()
+ {
+ return explicit;
+ }
+
+ public boolean isEmpty()
+ {
+ return empty;
+ }
+
+ /**
+ * return whatever was following the tag.
+ * <p>
+ * Note: tagged objects are generally context dependent if you're
+ * trying to extract a tagged object you should be going via the
+ * appropriate getInstance method.
+ */
+ public DERObject getObject()
+ {
+ if (obj != null)
+ {
+ return obj.getDERObject();
+ }
+
+ return null;
+ }
+
+ abstract void encode(DEROutputStream out)
+ throws IOException;
+}
+
+
+class BERConstructedOctetString
+ extends DEROctetString
+{
+ /**
+ * convert a vector of octet strings into a single byte string
+ */
+ static private byte[] toBytes(
+ Vector octs)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ for (int i = 0; i != octs.size(); i++)
+ {
+ DEROctetString o = (DEROctetString)octs.elementAt(i);
+
+ try
+ {
+ bOut.write(o.getOctets());
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("exception converting octets " + e.toString());
+ }
+ }
+
+ return bOut.toByteArray();
+ }
+
+ private Vector octs;
+
+ /**
+ * @param string the octets making up the octet string.
+ */
+ public BERConstructedOctetString(
+ byte[] string)
+ {
+ super(string);
+ }
+
+ public BERConstructedOctetString(
+ Vector octs)
+ {
+ super(toBytes(octs));
+
+ this.octs = octs;
+ }
+
+ public BERConstructedOctetString(
+ DERObject obj)
+ {
+ super(obj);
+ }
+
+ public BERConstructedOctetString(
+ DEREncodable obj)
+ {
+ super(obj.getDERObject());
+ }
+
+ public byte[] getOctets()
+ {
+ return string;
+ }
+
+ /**
+ * return the DER octets that make up this string.
+ */
+ public Enumeration getObjects()
+ {
+ if (octs == null)
+ {
+ octs = generateOcts();
+ }
+
+ return octs.elements();
+ }
+
+ private Vector generateOcts()
+ {
+ int start = 0;
+ int end = 0;
+ Vector vec = new Vector();
+
+ while ((end + 1) < string.length)
+ {
+ if (string[end] == 0 && string[end + 1] == 0)
+ {
+ byte[] nStr = new byte[end - start + 1];
+
+ for (int i = 0; i != nStr.length; i++)
+ {
+ nStr[i] = string[start + i];
+ }
+
+ vec.addElement(new DEROctetString(nStr));
+ start = end + 1;
+ }
+ end++;
+ }
+
+ byte[] nStr = new byte[string.length - start];
+ for (int i = 0; i != nStr.length; i++)
+ {
+ nStr[i] = string[start + i];
+ }
+
+ vec.addElement(new DEROctetString(nStr));
+
+ return vec;
+ }
+
+ public void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ if (out instanceof ASN1OutputStream || out instanceof BEROutputStream)
+ {
+ out.write(CONSTRUCTED | OCTET_STRING);
+
+ out.write(0x80);
+
+ if (octs == null)
+ {
+ octs = generateOcts();
+ }
+
+ for (int i = 0; i != octs.size(); i++)
+ {
+ out.writeObject(octs.elementAt(i));
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+ else
+ {
+ super.encode(out);
+ }
+ }
+}
+
+
+class BERConstructedSequence
+ extends DERConstructedSequence
+{
+ /*
+ */
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ if (out instanceof ASN1OutputStream || out instanceof BEROutputStream)
+ {
+ out.write(SEQUENCE | CONSTRUCTED);
+ out.write(0x80);
+
+ Enumeration e = getObjects();
+ while (e.hasMoreElements())
+ {
+ out.writeObject(e.nextElement());
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+ else
+ {
+ super.encode(out);
+ }
+ }
+}
+
+
+class BERInputStream
+ extends DERInputStream
+{
+ private DERObject END_OF_STREAM = new DERObject() {
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ throw new IOException("Eeek!");
+ }
+
+ };
+ public BERInputStream(
+ InputStream is)
+ {
+ super(is);
+ }
+
+ /**
+ * read a string of bytes representing an indefinite length object.
+ */
+ private byte[] readIndefiniteLengthFully()
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ int b, b1;
+
+ b1 = read();
+
+ while ((b = read()) >= 0)
+ {
+ if (b1 == 0 && b == 0)
+ {
+ break;
+ }
+
+ bOut.write(b1);
+ b1 = b;
+ }
+
+ return bOut.toByteArray();
+ }
+
+ private BERConstructedOctetString buildConstructedOctetString()
+ throws IOException
+ {
+ Vector octs = new Vector();
+
+ for (;;)
+ {
+ DERObject o = readObject();
+
+ if (o == END_OF_STREAM)
+ {
+ break;
+ }
+
+ octs.addElement(o);
+ }
+
+ return new BERConstructedOctetString(octs);
+ }
+
+ public DERObject readObject()
+ throws IOException
+ {
+ int tag = read();
+ if (tag == -1)
+ {
+ throw new EOFException();
+ }
+
+ int length = readLength();
+
+ if (length < 0) // indefinite length method
+ {
+ switch (tag)
+ {
+ case NULL:
+ return null;
+ case SEQUENCE | CONSTRUCTED:
+ BERConstructedSequence seq = new BERConstructedSequence();
+
+ for (;;)
+ {
+ DERObject obj = readObject();
+
+ if (obj == END_OF_STREAM)
+ {
+ break;
+ }
+
+ seq.addObject(obj);
+ }
+ return seq;
+ case OCTET_STRING | CONSTRUCTED:
+ return buildConstructedOctetString();
+ case SET | CONSTRUCTED:
+ DEREncodableVector v = new DEREncodableVector();
+
+ for (;;)
+ {
+ DERObject obj = readObject();
+
+ if (obj == END_OF_STREAM)
+ {
+ break;
+ }
+
+ v.add(obj);
+ }
+ return new BERSet(v);
+ default:
+ //
+ // with tagged object tag number is bottom 5 bits
+ //
+ if ((tag & TAGGED) != 0)
+ {
+ if ((tag & 0x1f) == 0x1f)
+ {
+ throw new IOException("unsupported high tag encountered");
+ }
+
+ //
+ // simple type - implicit... return an octet string
+ //
+ if ((tag & CONSTRUCTED) == 0)
+ {
+ byte[] bytes = readIndefiniteLengthFully();
+
+ return new BERTaggedObject(false, tag & 0x1f, new DEROctetString(bytes));
+ }
+
+ //
+ // either constructed or explicitly tagged
+ //
+ DERObject dObj = readObject();
+
+ if (dObj == END_OF_STREAM) // empty tag!
+ {
+ return new DERTaggedObject(tag & 0x1f);
+ }
+
+ DERObject next = readObject();
+
+ //
+ // explicitly tagged (probably!) - if it isn't we'd have to
+ // tell from the context
+ //
+ if (next == END_OF_STREAM)
+ {
+ return new BERTaggedObject(tag & 0x1f, dObj);
+ }
+
+ //
+ // another implicit object, we'll create a sequence...
+ //
+ seq = new BERConstructedSequence();
+
+ seq.addObject(dObj);
+
+ do
+ {
+ seq.addObject(next);
+ next = readObject();
+ }
+ while (next != END_OF_STREAM);
+
+ return new BERTaggedObject(false, tag & 0x1f, seq);
+ }
+
+ throw new IOException("unknown BER object encountered");
+ }
+ }
+ else
+ {
+ if (tag == 0 && length == 0) // end of contents marker.
+ {
+ return END_OF_STREAM;
+ }
+
+ byte[] bytes = new byte[length];
+
+ readFully(bytes);
+
+ return buildObject(tag, bytes);
+ }
+ }
+}
+
+
+class BEROutputStream
+ extends DEROutputStream
+{
+ public BEROutputStream(
+ OutputStream os)
+ {
+ super(os);
+ }
+
+ public void writeObject(
+ Object obj)
+ throws IOException
+ {
+ if (obj == null)
+ {
+ writeNull();
+ }
+ else if (obj instanceof DERObject)
+ {
+ ((DERObject)obj).encode(this);
+ }
+ else if (obj instanceof DEREncodable)
+ {
+ ((DEREncodable)obj).getDERObject().encode(this);
+ }
+ else
+ {
+ throw new IOException("object not BEREncodable");
+ }
+ }
+}
+
+
+class BERSet
+ extends DERSet
+{
+ /**
+ * create an empty sequence
+ */
+ public BERSet()
+ {
+ }
+
+ /**
+ * create a set containing one object
+ */
+ public BERSet(
+ DEREncodable obj)
+ {
+ super(obj);
+ }
+
+ /**
+ * create a set containing a vector of objects.
+ */
+ public BERSet(
+ DEREncodableVector v)
+ {
+ super(v);
+ }
+
+ /*
+ */
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ if (out instanceof ASN1OutputStream || out instanceof BEROutputStream)
+ {
+ out.write(SET | CONSTRUCTED);
+ out.write(0x80);
+
+ Enumeration e = getObjects();
+ while (e.hasMoreElements())
+ {
+ out.writeObject(e.nextElement());
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+ else
+ {
+ super.encode(out);
+ }
+ }
+}
+
+
+/**
+ * BER TaggedObject - in ASN.1 nottation this is any object proceeded by
+ * a [n] where n is some number - these are assume to follow the construction
+ * rules (as with sequences).
+ */
+class BERTaggedObject
+ extends DERTaggedObject
+{
+ /**
+ * @param tagNo the tag number for this object.
+ * @param obj the tagged object.
+ */
+ public BERTaggedObject(
+ int tagNo,
+ DEREncodable obj)
+ {
+ super(tagNo, obj);
+ }
+
+ /**
+ * @param explicit true if an explicitly tagged object.
+ * @param tagNo the tag number for this object.
+ * @param obj the tagged object.
+ */
+ public BERTaggedObject(
+ boolean explicit,
+ int tagNo,
+ DEREncodable obj)
+ {
+ super(explicit, tagNo, obj);
+ }
+
+ /**
+ * create an implicitly tagged object that contains a zero
+ * length sequence.
+ */
+ public BERTaggedObject(
+ int tagNo)
+ {
+ super(false, tagNo, new BERConstructedSequence());
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ if (out instanceof ASN1OutputStream || out instanceof BEROutputStream)
+ {
+ out.write(CONSTRUCTED | TAGGED | tagNo);
+ out.write(0x80);
+
+ if (!empty)
+ {
+ if (!explicit)
+ {
+ if (obj instanceof BERConstructedOctetString)
+ {
+ Enumeration e = ((BERConstructedOctetString)obj).getObjects();
+
+ while (e.hasMoreElements())
+ {
+ out.writeObject(e.nextElement());
+ }
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ Enumeration e = ((ASN1Sequence)obj).getObjects();
+
+ while (e.hasMoreElements())
+ {
+ out.writeObject(e.nextElement());
+ }
+ }
+ else if (obj instanceof ASN1Set)
+ {
+ Enumeration e = ((ASN1Set)obj).getObjects();
+
+ while (e.hasMoreElements())
+ {
+ out.writeObject(e.nextElement());
+ }
+ }
+ else
+ {
+ throw new RuntimeException("not implemented: " + obj.getClass().getName());
+ }
+ }
+ else
+ {
+ out.writeObject(obj);
+ }
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+ else
+ {
+ super.encode(out);
+ }
+ }
+}
+
+
+class Attribute
+ implements DEREncodable
+{
+ private DERObjectIdentifier attrType;
+ private ASN1Set attrValues;
+
+ /**
+ * return an Attribute object from the given object.
+ *
+ * @param o the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static Attribute getInstance(
+ Object o)
+ {
+ if (o == null || o instanceof Attribute)
+ {
+ return (Attribute)o;
+ }
+
+ if (o instanceof ASN1Sequence)
+ {
+ return new Attribute((ASN1Sequence)o);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory");
+ }
+
+ public Attribute(
+ ASN1Sequence seq)
+ {
+ attrType = (DERObjectIdentifier)seq.getObjectAt(0);
+ attrValues = (ASN1Set)seq.getObjectAt(1);
+ }
+
+ public Attribute(
+ DERObjectIdentifier attrType,
+ ASN1Set attrValues)
+ {
+ this.attrType = attrType;
+ this.attrValues = attrValues;
+ }
+
+ public DERObjectIdentifier getAttrType()
+ {
+ return attrType;
+ }
+
+ public ASN1Set getAttrValues()
+ {
+ return attrValues;
+ }
+
+ /**
+ * <pre>
+ * Attribute ::= SEQUENCE {
+ * attrType OBJECT IDENTIFIER,
+ * attrValues SET OF AttributeValue
+ * }
+ * </pre>
+ */
+ public DERObject getDERObject()
+ {
+ DEREncodableVector v = new DEREncodableVector();
+
+ v.add(attrType);
+ v.add(attrValues);
+
+ return new DERSequence(v);
+ }
+}
+// Decompiled by Jad v1.5.7f. Copyright 2000 Pavel Kouznetsov.
+// Jad home page: http://www.geocities.com/SiliconValley/Bridge/8617/jad.html
+// Decompiler options: packimports(3)
+// Source File Name: SignedAttributes.java
+
+
+
+// Referenced classes of package org.bouncycastle.asn1.cms:
+// Attribute
+
+class SignedAttributes
+ implements DEREncodable
+{
+
+ public SignedAttributes(Vector vector)
+ {
+ setAttributes(vector);
+ }
+
+ public SignedAttributes(DERConstructedSet derconstructedset)
+ {
+ attributes = derconstructedset;
+ }
+
+ public SignedAttributes(SignedAttributes signedattributes)
+ {
+ attributes = signedattributes.attributes;
+ }
+
+ public static SignedAttributes getInstance(Object obj)
+ {
+ if(obj == null)
+ return null;
+ if(obj instanceof SignedAttributes)
+ return (SignedAttributes)obj;
+ if(obj instanceof DERConstructedSet)
+ return new SignedAttributes((DERConstructedSet)obj);
+ if(obj instanceof DERTaggedObject)
+ return getInstance(((DERTaggedObject)obj).getObject());
+ else
+ throw new IllegalArgumentException("Invalid SignedAttributes");
+ }
+
+ public static SignedAttributes newInstance(Object obj)
+ {
+ if(obj == null)
+ return null;
+ if(obj instanceof SignedAttributes)
+ return new SignedAttributes((SignedAttributes)obj);
+ if(obj instanceof DERConstructedSet)
+ return new SignedAttributes((DERConstructedSet)obj);
+ if(obj instanceof DERTaggedObject)
+ return getInstance(((DERTaggedObject)obj).getObject());
+ else
+ throw new IllegalArgumentException("Invalid SignedAttributes");
+ }
+
+ public Vector getAttributes()
+ {
+ int i = attributes.getSize();
+ Vector vector = new Vector();
+ for(int j = 0; j < i; j++)
+ vector.addElement(Attribute.getInstance(attributes.getObjectAt(j)));
+
+ return vector;
+ }
+
+ private void setAttributes(Vector vector)
+ {
+ int i = vector.size();
+ attributes = new DERConstructedSet();
+ for(int j = 0; j < i; j++)
+ attributes.addObject(Attribute.getInstance(vector.elementAt(j)));
+
+ }
+
+ public DERObject getDERObject()
+ {
+ return attributes;
+ }
+
+ private DERConstructedSet attributes;
+}
+
+
+class DERBitString
+ extends DERObject
+{
+ protected byte[] data;
+ protected int padBits;
+
+ /**
+ * return the correct number of pad bits for a bit string defined in
+ * a 16 bit constant
+ */
+ static protected int getPadBits(
+ int bitString)
+ {
+ int val;
+
+ if (bitString == 0)
+ {
+ return 7;
+ }
+
+ if (bitString > 255)
+ {
+ val = ((bitString >> 8) & 0xFF);
+ }
+ else
+ {
+ val = (bitString & 0xFF);
+ }
+
+ int bits = 1;
+
+ while (((val <<= 1) & 0xFF) != 0)
+ {
+ bits++;
+ }
+
+ return 8 - bits;
+ }
+
+ /**
+ * return the correct number of bytes for a bit string defined in
+ * a 16 bit constant
+ */
+ static protected byte[] getBytes(
+ int bitString)
+ {
+ if (bitString > 255)
+ {
+ byte[] bytes = new byte[2];
+
+ bytes[0] = (byte)(bitString & 0xFF);
+ bytes[1] = (byte)((bitString >> 8) & 0xFF);
+
+ return bytes;
+ }
+ else
+ {
+ byte[] bytes = new byte[1];
+
+ bytes[0] = (byte)(bitString & 0xFF);
+
+ return bytes;
+ }
+ }
+
+ /**
+ * return a Bit String from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERBitString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERBitString)
+ {
+ return (DERBitString)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ byte[] bytes = ((ASN1OctetString)obj).getOctets();
+ int padBits = bytes[0];
+ byte[] data = new byte[bytes.length - 1];
+
+ System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
+
+ return new DERBitString(data, padBits);
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Bit String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERBitString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ protected DERBitString(
+ byte data,
+ int padBits)
+ {
+ this.data = new byte[1];
+ this.data[0] = data;
+ this.padBits = padBits;
+ }
+
+ /**
+ * @param data the octets making up the bit string.
+ * @param padBits the number of extra bits at the end of the string.
+ */
+ public DERBitString(
+ byte[] data,
+ int padBits)
+ {
+ this.data = data;
+ this.padBits = padBits;
+ }
+
+ public DERBitString(
+ byte[] data)
+ {
+ this(data, 0);
+ }
+
+ public DERBitString(
+ DEREncodable obj)
+ {
+ try
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+
+ dOut.writeObject(obj);
+ dOut.close();
+
+ this.data = bOut.toByteArray();
+ this.padBits = 0;
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("Error processing object : " + e.toString());
+ }
+ }
+
+ public byte[] getBytes()
+ {
+ return data;
+ }
+
+ public int getPadBits()
+ {
+ return padBits;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ byte[] bytes = new byte[getBytes().length + 1];
+
+ bytes[0] = (byte)getPadBits();
+ System.arraycopy(getBytes(), 0, bytes, 1, bytes.length - 1);
+
+ out.writeEncoded(BIT_STRING, bytes);
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == null || !(o instanceof DERBitString))
+ {
+ return false;
+ }
+
+ DERBitString other = (DERBitString)o;
+
+ if (data.length != other.data.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != data.length; i++)
+ {
+ if (data[i] != other.data[i])
+ {
+ return false;
+ }
+ }
+
+ return (padBits == other.padBits);
+ }
+}
+
+
+/**
+ * DER BMPString object.
+ */
+class DERBMPString
+ extends DERObject
+ implements DERString
+{
+ String string;
+
+ /**
+ * return a BMP String from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERBMPString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERBMPString)
+ {
+ return (DERBMPString)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERBMPString(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a BMP String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERBMPString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+
+ /**
+ * basic constructor - byte encoded string.
+ */
+ public DERBMPString(
+ byte[] string)
+ {
+ char[] cs = new char[string.length / 2];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ cs[i] = (char)((string[2 * i] << 8) | (string[2 * i + 1] & 0xff));
+ }
+
+ this.string = new String(cs);
+ }
+
+ /**
+ * basic constructor
+ */
+ public DERBMPString(
+ String string)
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ return string;
+ }
+
+ public int hashCode()
+ {
+ return this.getString().hashCode();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DERBMPString))
+ {
+ return false;
+ }
+
+ DERPrintableString s = (DERPrintableString)o;
+
+ return this.getString().equals(s.getString());
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ char[] c = string.toCharArray();
+ byte[] b = new byte[c.length * 2];
+
+ for (int i = 0; i != c.length; i++)
+ {
+ b[2 * i] = (byte)(c[i] >> 8);
+ b[2 * i + 1] = (byte)c[i];
+ }
+
+ out.writeEncoded(BMP_STRING, b);
+ }
+}
+
+
+class DERBoolean
+ extends DERObject
+{
+ byte value;
+
+ public static final DERBoolean FALSE = new DERBoolean(false);
+ public static final DERBoolean TRUE = new DERBoolean(true);
+
+ /**
+ * return a boolean from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERBoolean getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERBoolean)
+ {
+ return (DERBoolean)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERBoolean(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a DERBoolean from the passed in boolean.
+ */
+ public static DERBoolean getInstance(
+ boolean value)
+ {
+ return (value ? TRUE : FALSE);
+ }
+
+ /**
+ * return a Boolean from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERBoolean getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ public DERBoolean(
+ byte[] value)
+ {
+ this.value = value[0];
+ }
+
+ public DERBoolean(
+ boolean value)
+ {
+ this.value = (value) ? (byte)0xff : (byte)0;
+ }
+
+ public boolean isTrue()
+ {
+ return (value != 0);
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ byte[] bytes = new byte[1];
+
+ bytes[0] = value;
+
+ out.writeEncoded(BOOLEAN, bytes);
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERBoolean))
+ {
+ return false;
+ }
+
+ return (value == ((DERBoolean)o).value);
+ }
+
+}
+
+
+class DERConstructedSequence
+ extends ASN1Sequence
+{
+ public void addObject(
+ DEREncodable obj)
+ {
+ super.addObject(obj);
+ }
+
+ public int getSize()
+ {
+ return size();
+ }
+
+ /*
+ * A note on the implementation:
+ * <p>
+ * As DER requires the constructed, definite-length model to
+ * be used for structured types, this varies slightly from the
+ * ASN.1 descriptions given. Rather than just outputing SEQUENCE,
+ * we also have to specify CONSTRUCTED, and the objects length.
+ */
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+ Enumeration e = this.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ Object obj = e.nextElement();
+
+ dOut.writeObject(obj);
+ }
+
+ dOut.close();
+
+ byte[] bytes = bOut.toByteArray();
+
+ out.writeEncoded(SEQUENCE | CONSTRUCTED, bytes);
+ }
+}
+
+
+class DERConstructedSet
+ extends ASN1Set
+{
+ public DERConstructedSet()
+ {
+ }
+
+ /**
+ * @param obj - a single object that makes up the set.
+ */
+ public DERConstructedSet(
+ DEREncodable obj)
+ {
+ this.addObject(obj);
+ }
+
+ /**
+ * @param v - a vector of objects making up the set.
+ */
+ public DERConstructedSet(
+ DEREncodableVector v)
+ {
+ for (int i = 0; i != v.size(); i++)
+ {
+ this.addObject(v.get(i));
+ }
+ }
+
+ public void addObject(
+ DEREncodable obj)
+ {
+ super.addObject(obj);
+ }
+
+ public int getSize()
+ {
+ return size();
+ }
+
+ /*
+ * A note on the implementation:
+ * <p>
+ * As DER requires the constructed, definite-length model to
+ * be used for structured types, this varies slightly from the
+ * ASN.1 descriptions given. Rather than just outputing SET,
+ * we also have to specify CONSTRUCTED, and the objects length.
+ */
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+ Enumeration e = this.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ Object obj = e.nextElement();
+
+ dOut.writeObject(obj);
+ }
+
+ dOut.close();
+
+ byte[] bytes = bOut.toByteArray();
+
+ out.writeEncoded(SET | CONSTRUCTED, bytes);
+ }
+}
+
+interface DEREncodable
+{
+ public DERObject getDERObject();
+}
+
+
+/**
+ * a general class for building up a vector of DER encodable objects
+ */
+class DEREncodableVector
+{
+ private Vector v = new Vector();
+
+ public void add(
+ DEREncodable obj)
+ {
+ v.addElement(obj);
+ }
+
+ public DEREncodable get(
+ int i)
+ {
+ return (DEREncodable)v.elementAt(i);
+ }
+
+ public int size()
+ {
+ return v.size();
+ }
+}
+
+
+class DEREnumerated
+ extends DERObject
+{
+ byte[] bytes;
+
+ /**
+ * return an integer from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DEREnumerated getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DEREnumerated)
+ {
+ return (DEREnumerated)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DEREnumerated(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an Enumerated from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DEREnumerated getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ public DEREnumerated(
+ int value)
+ {
+ bytes = BigInteger.valueOf(value).toByteArray();
+ }
+
+ public DEREnumerated(
+ BigInteger value)
+ {
+ bytes = value.toByteArray();
+ }
+
+ public DEREnumerated(
+ byte[] bytes)
+ {
+ this.bytes = bytes;
+ }
+
+ public BigInteger getValue()
+ {
+ return new BigInteger(bytes);
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(ENUMERATED, bytes);
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == null || !(o instanceof DEREnumerated))
+ {
+ return false;
+ }
+
+ DEREnumerated other = (DEREnumerated)o;
+
+ if (bytes.length != other.bytes.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != bytes.length; i++)
+ {
+ if (bytes[i] != other.bytes[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
+
+
+/**
+ * Generalized time object.
+ */
+class DERGeneralizedTime
+ extends DERObject
+{
+ String time;
+
+ /**
+ * return a generalized time from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERGeneralizedTime getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERGeneralizedTime)
+ {
+ return (DERGeneralizedTime)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERGeneralizedTime(((ASN1OctetString)obj).getOctets());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Generalized Time object from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERGeneralizedTime getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * The correct format for this is YYYYMMDDHHMMSSZ, or without the Z
+ * for local time, or Z+-HHMM on the end, for difference between local
+ * time and UTC time.
+ * <p>
+ *
+ * @param time the time string.
+ */
+ public DERGeneralizedTime(
+ String time)
+ {
+ this.time = time;
+ }
+
+ /**
+ * base constructer from a java.util.date object
+ */
+ public DERGeneralizedTime(
+ Date time)
+ {
+ SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
+
+ dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
+
+ this.time = dateF.format(time);
+ }
+
+ DERGeneralizedTime(
+ byte[] bytes)
+ {
+ //
+ // explicitly convert to characters
+ //
+ char[] dateC = new char[bytes.length];
+
+ for (int i = 0; i != dateC.length; i++)
+ {
+ dateC[i] = (char)(bytes[i] & 0xff);
+ }
+
+ this.time = new String(dateC);
+ }
+
+ /**
+ * return the time - always in the form of
+ * YYYYMMDDhhmmssGMT(+hh:mm|-hh:mm).
+ * <p>
+ * Normally in a certificate we would expect "Z" rather than "GMT",
+ * however adding the "GMT" means we can just use:
+ * <pre>
+ * dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
+ * </pre>
+ * To read in the time and get a date which is compatible with our local
+ * time zone.
+ */
+ public String getTime()
+ {
+ //
+ // standardise the format.
+ //
+ if (time.length() == 15)
+ {
+ return time.substring(0, 14) + "GMT+00:00";
+ }
+ else if (time.length() == 17)
+ {
+ return time.substring(0, 14) + "GMT" + time.substring(15, 17) + ":" + time.substring(17, 19);
+ }
+
+ return time;
+ }
+
+ private byte[] getOctets()
+ {
+ char[] cs = time.toCharArray();
+ byte[] bs = new byte[cs.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ bs[i] = (byte)cs[i];
+ }
+
+ return bs;
+ }
+
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(GENERALIZED_TIME, this.getOctets());
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERGeneralizedTime))
+ {
+ return false;
+ }
+
+ return time.equals(((DERGeneralizedTime)o).time);
+ }
+}
+
+
+/**
+ * DER IA5String object - this is an ascii string.
+ */
+class DERIA5String
+ extends DERObject
+ implements DERString
+{
+ String string;
+
+ /**
+ * return a IA5 string from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERIA5String getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERIA5String)
+ {
+ return (DERIA5String)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERIA5String(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an IA5 String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERIA5String getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * basic constructor - with bytes.
+ */
+ public DERIA5String(
+ byte[] string)
+ {
+ char[] cs = new char[string.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ cs[i] = (char)(string[i] & 0xff);
+ }
+
+ this.string = new String(cs);
+ }
+
+ /**
+ * basic constructor - with string.
+ */
+ public DERIA5String(
+ String string)
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ return string;
+ }
+
+ public byte[] getOctets()
+ {
+ char[] cs = string.toCharArray();
+ byte[] bs = new byte[cs.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ bs[i] = (byte)cs[i];
+ }
+
+ return bs;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(IA5_STRING, this.getOctets());
+ }
+
+ public int hashCode()
+ {
+ return this.getString().hashCode();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DERIA5String))
+ {
+ return false;
+ }
+
+ DERIA5String s = (DERIA5String)o;
+
+ return this.getString().equals(s.getString());
+ }
+}
+
+
+
+
+class DERInputStream
+ extends FilterInputStream implements DERTags
+{
+ public DERInputStream(
+ InputStream is)
+ {
+ super(is);
+ }
+
+ protected int readLength()
+ throws IOException
+ {
+ int length = read();
+ if (length < 0)
+ {
+ throw new IOException("EOF found when length expected");
+ }
+
+ if (length == 0x80)
+ {
+ return -1; // indefinite-length encoding
+ }
+
+ if (length > 127)
+ {
+ int size = length & 0x7f;
+
+ length = 0;
+ for (int i = 0; i < size; i++)
+ {
+ int next = read();
+
+ if (next < 0)
+ {
+ throw new IOException("EOF found reading length");
+ }
+
+ length = (length << 8) + next;
+ }
+ }
+
+ return length;
+ }
+
+ protected void readFully(
+ byte[] bytes)
+ throws IOException
+ {
+ int left = bytes.length;
+
+ if (left == 0)
+ {
+ return;
+ }
+
+ while ((left -= read(bytes, bytes.length - left, left)) != 0)
+ {
+ ;
+ }
+ }
+
+ /**
+ * build an object given its tag and a byte stream to construct it
+ * from.
+ */
+ protected DERObject buildObject(
+ int tag,
+ byte[] bytes)
+ throws IOException
+ {
+ switch (tag)
+ {
+ case NULL:
+ return null;
+ case SEQUENCE | CONSTRUCTED:
+ ByteArrayInputStream bIn = new ByteArrayInputStream(bytes);
+ BERInputStream dIn = new BERInputStream(bIn);
+ DERConstructedSequence seq = new DERConstructedSequence();
+
+ try
+ {
+ for (;;)
+ {
+ DERObject obj = dIn.readObject();
+
+ seq.addObject(obj);
+ }
+ }
+ catch (EOFException ex)
+ {
+ return seq;
+ }
+ case SET | CONSTRUCTED:
+ bIn = new ByteArrayInputStream(bytes);
+ dIn = new BERInputStream(bIn);
+
+ DEREncodableVector v = new DEREncodableVector();
+
+ try
+ {
+ for (;;)
+ {
+ DERObject obj = dIn.readObject();
+
+ v.add(obj);
+ }
+ }
+ catch (EOFException ex)
+ {
+ return new DERConstructedSet(v);
+ }
+ case BOOLEAN:
+ return new DERBoolean(bytes);
+ case INTEGER:
+ return new DERInteger(bytes);
+ case ENUMERATED:
+ return new DEREnumerated(bytes);
+ case OBJECT_IDENTIFIER:
+ return new DERObjectIdentifier(bytes);
+ case BIT_STRING:
+ int padBits = bytes[0];
+ byte[] data = new byte[bytes.length - 1];
+
+ System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
+
+ return new DERBitString(data, padBits);
+ case UTF8_STRING:
+ return new DERUTF8String(bytes);
+ case PRINTABLE_STRING:
+ return new DERPrintableString(bytes);
+ case IA5_STRING:
+ return new DERIA5String(bytes);
+ case T61_STRING:
+ return new DERT61String(bytes);
+ case VISIBLE_STRING:
+ return new DERVisibleString(bytes);
+ case UNIVERSAL_STRING:
+ return new DERUniversalString(bytes);
+ case BMP_STRING:
+ return new DERBMPString(bytes);
+ case OCTET_STRING:
+ return new DEROctetString(bytes);
+ case UTC_TIME:
+ return new DERUTCTime(bytes);
+ case GENERALIZED_TIME:
+ return new DERGeneralizedTime(bytes);
+ default:
+ //
+ // with tagged object tag number is bottom 5 bits
+ //
+ if ((tag & TAGGED) != 0)
+ {
+ if ((tag & 0x1f) == 0x1f)
+ {
+ throw new IOException("unsupported high tag encountered");
+ }
+
+ if (bytes.length == 0) // empty tag!
+ {
+ return new DERTaggedObject(false, tag & 0x1f, new DERConstructedSequence());
+ }
+
+ //
+ // simple type - implicit... return an octet string
+ //
+ if ((tag & CONSTRUCTED) == 0)
+ {
+ return new DERTaggedObject(false, tag & 0x1f, new DEROctetString(bytes));
+ }
+
+ bIn = new ByteArrayInputStream(bytes);
+ dIn = new BERInputStream(bIn);
+
+ DEREncodable dObj = dIn.readObject();
+
+ //
+ // explicitly tagged (probably!) - if it isn't we'd have to
+ // tell from the context
+ //
+ if (dIn.available() == 0)
+ {
+ return new DERTaggedObject(tag & 0x1f, dObj);
+ }
+
+ //
+ // another implicit object, we'll create a sequence...
+ //
+ seq = new DERConstructedSequence();
+
+ seq.addObject(dObj);
+
+ try
+ {
+ for (;;)
+ {
+ dObj = dIn.readObject();
+
+ seq.addObject(dObj);
+ }
+ }
+ catch (EOFException ex)
+ {
+ // ignore --
+ }
+
+ return new DERTaggedObject(false, tag & 0x1f, seq);
+ }
+
+ return new DERUnknownTag(tag, bytes);
+ }
+ }
+
+ public DERObject readObject()
+ throws IOException
+ {
+ int tag = read();
+ if (tag == -1)
+ {
+ throw new EOFException();
+ }
+
+ int length = readLength();
+ byte[] bytes = new byte[length];
+
+ readFully(bytes);
+
+ return buildObject(tag, bytes);
+ }
+}
+
+
+class DERInteger
+ extends DERObject
+{
+ byte[] bytes;
+
+ /**
+ * return an integer from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERInteger getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERInteger)
+ {
+ return (DERInteger)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERInteger(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an Integer from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERInteger getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ public DERInteger(
+ int value)
+ {
+ bytes = BigInteger.valueOf(value).toByteArray();
+ }
+
+ public DERInteger(
+ BigInteger value)
+ {
+ bytes = value.toByteArray();
+ }
+
+ public DERInteger(
+ byte[] bytes)
+ {
+ this.bytes = bytes;
+ }
+
+ public BigInteger getValue()
+ {
+ return new BigInteger(bytes);
+ }
+
+ /**
+ * in some cases positive values get crammed into a space,
+ * that's not quite big enough...
+ */
+ public BigInteger getPositiveValue()
+ {
+ return new BigInteger(1, bytes);
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(INTEGER, bytes);
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == null || !(o instanceof DERInteger))
+ {
+ return false;
+ }
+
+ DERInteger other = (DERInteger)o;
+
+ if (bytes.length != other.bytes.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != bytes.length; i++)
+ {
+ if (bytes[i] != other.bytes[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
+
+
+abstract class DERObject
+ implements DERTags, DEREncodable
+{
+ public DERObject getDERObject()
+ {
+ return this;
+ }
+
+ abstract void encode(DEROutputStream out)
+ throws IOException;
+}
+
+
+class DERObjectIdentifier
+ extends DERObject
+{
+ String identifier;
+
+ /**
+ * return an OID from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERObjectIdentifier getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERObjectIdentifier)
+ {
+ return (DERObjectIdentifier)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERObjectIdentifier(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an Object Identifier from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERObjectIdentifier getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+
+ DERObjectIdentifier(
+ byte[] bytes)
+ {
+ int head = bytes[0] & 0xff;
+ StringBuffer objId = new StringBuffer();
+ int value = 0;
+ boolean first = true;
+
+ for (int i = 0; i != bytes.length; i++)
+ {
+ int b = bytes[i] & 0xff;
+
+ value = value * 128 + (b & 0x7f);
+ if ((b & 0x80) == 0) // end of number reached
+ {
+ if (first)
+ {
+ switch (value / 40)
+ {
+ case 0:
+ objId.append('0');
+ break;
+ case 1:
+ objId.append('1');
+ value -= 40;
+ break;
+ default:
+ objId.append('2');
+ value -= 80;
+ }
+ first = false;
+ }
+
+ objId.append('.');
+ objId.append(Integer.toString(value));
+ value = 0;
+ }
+ }
+
+ this.identifier = objId.toString();
+ }
+
+ public DERObjectIdentifier(
+ String identifier)
+ {
+ this.identifier = identifier;
+ }
+
+ public String getId()
+ {
+ return identifier;
+ }
+
+ private void writeField(
+ OutputStream out,
+ int fieldValue)
+ throws IOException
+ {
+ if (fieldValue >= (1 << 7))
+ {
+ if (fieldValue >= (1 << 14))
+ {
+ if (fieldValue >= (1 << 21))
+ {
+ if (fieldValue >= (1 << 28))
+ {
+ out.write((fieldValue >> 28) | 0x80);
+ }
+ out.write((fieldValue >> 21) | 0x80);
+ }
+ out.write((fieldValue >> 14) | 0x80);
+ }
+ out.write((fieldValue >> 7) | 0x80);
+ }
+ out.write(fieldValue & 0x7f);
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ OIDTokenizer tok = new OIDTokenizer(identifier);
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+
+ writeField(bOut,
+ Integer.parseInt(tok.nextToken()) * 40
+ + Integer.parseInt(tok.nextToken()));
+
+ while (tok.hasMoreTokens())
+ {
+ writeField(bOut, Integer.parseInt(tok.nextToken()));
+ }
+
+ dOut.close();
+
+ byte[] bytes = bOut.toByteArray();
+
+ out.writeEncoded(OBJECT_IDENTIFIER, bytes);
+ }
+
+ public int hashCode()
+ {
+ return identifier.hashCode();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERObjectIdentifier))
+ {
+ return false;
+ }
+
+ return identifier.equals(((DERObjectIdentifier)o).identifier);
+ }
+}
+
+
+class DEROctetString
+ extends ASN1OctetString
+{
+ /**
+ * @param string the octets making up the octet string.
+ */
+ public DEROctetString(
+ byte[] string)
+ {
+ super(string);
+ }
+
+ public DEROctetString(
+ DEREncodable obj)
+ {
+ super(obj);
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(OCTET_STRING, string);
+ }
+}
+
+
+
+class DEROutputStream
+ extends FilterOutputStream implements DERTags
+{
+ public DEROutputStream(
+ OutputStream os)
+ {
+ super(os);
+ }
+
+ private void writeLength(
+ int length)
+ throws IOException
+ {
+ if (length > 127)
+ {
+ int size = 1;
+ int val = length;
+
+ while ((val >>>= 8) != 0)
+ {
+ size++;
+ }
+
+ write((byte)(size | 0x80));
+
+ for (int i = (size - 1) * 8; i >= 0; i -= 8)
+ {
+ write((byte)(length >> i));
+ }
+ }
+ else
+ {
+ write((byte)length);
+ }
+ }
+
+ void writeEncoded(
+ int tag,
+ byte[] bytes)
+ throws IOException
+ {
+ write(tag);
+ writeLength(bytes.length);
+ write(bytes);
+ }
+
+ protected void writeNull()
+ throws IOException
+ {
+ write(NULL);
+ write(0x00);
+ }
+
+ public void writeObject(
+ Object obj)
+ throws IOException
+ {
+ if (obj == null)
+ {
+ writeNull();
+ }
+ else if (obj instanceof DERObject)
+ {
+ ((DERObject)obj).encode(this);
+ }
+ else if (obj instanceof DEREncodable)
+ {
+ ((DEREncodable)obj).getDERObject().encode(this);
+ }
+ else
+ {
+ throw new IOException("object not DEREncodable");
+ }
+ }
+}
+
+
+/**
+ * DER PrintableString object.
+ */
+class DERPrintableString
+ extends DERObject
+ implements DERString
+{
+ String string;
+
+ /**
+ * return a printable string from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERPrintableString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERPrintableString)
+ {
+ return (DERPrintableString)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERPrintableString(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Printable String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERPrintableString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * basic constructor - byte encoded string.
+ */
+ public DERPrintableString(
+ byte[] string)
+ {
+ char[] cs = new char[string.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ cs[i] = (char)(string[i] & 0xff);
+ }
+
+ this.string = new String(cs);
+ }
+
+ /**
+ * basic constructor
+ */
+ public DERPrintableString(
+ String string)
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ return string;
+ }
+
+ public byte[] getOctets()
+ {
+ char[] cs = string.toCharArray();
+ byte[] bs = new byte[cs.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ bs[i] = (byte)cs[i];
+ }
+
+ return bs;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(PRINTABLE_STRING, this.getOctets());
+ }
+
+ public int hashCode()
+ {
+ return this.getString().hashCode();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DERPrintableString))
+ {
+ return false;
+ }
+
+ DERPrintableString s = (DERPrintableString)o;
+
+ return this.getString().equals(s.getString());
+ }
+}
+
+
+class DERSequence
+ extends ASN1Sequence
+{
+ /**
+ * create an empty sequence
+ */
+ public DERSequence()
+ {
+ }
+
+ /**
+ * create a sequence containing one object
+ */
+ public DERSequence(
+ DEREncodable obj)
+ {
+ this.addObject(obj);
+ }
+
+ /**
+ * create a sequence containing a vector of objects.
+ */
+ public DERSequence(
+ DEREncodableVector v)
+ {
+ for (int i = 0; i != v.size(); i++)
+ {
+ this.addObject(v.get(i));
+ }
+ }
+
+ /*
+ * A note on the implementation:
+ * <p>
+ * As DER requires the constructed, definite-length model to
+ * be used for structured types, this varies slightly from the
+ * ASN.1 descriptions given. Rather than just outputing SEQUENCE,
+ * we also have to specify CONSTRUCTED, and the objects length.
+ */
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+ Enumeration e = this.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ Object obj = e.nextElement();
+
+ dOut.writeObject(obj);
+ }
+
+ dOut.close();
+
+ byte[] bytes = bOut.toByteArray();
+
+ out.writeEncoded(SEQUENCE | CONSTRUCTED, bytes);
+ }
+}
+
+
+/**
+ * A DER encoded set object
+ */
+class DERSet
+ extends ASN1Set
+{
+ /**
+ * create an empty set
+ */
+ public DERSet()
+ {
+ }
+
+ /**
+ * @param obj - a single object that makes up the set.
+ */
+ public DERSet(
+ DEREncodable obj)
+ {
+ this.addObject(obj);
+ }
+
+ /**
+ * @param v - a vector of objects making up the set.
+ */
+ public DERSet(
+ DEREncodableVector v)
+ {
+ for (int i = 0; i != v.size(); i++)
+ {
+ this.addObject(v.get(i));
+ }
+ }
+
+ /*
+ * A note on the implementation:
+ * <p>
+ * As DER requires the constructed, definite-length model to
+ * be used for structured types, this varies slightly from the
+ * ASN.1 descriptions given. Rather than just outputing SET,
+ * we also have to specify CONSTRUCTED, and the objects length.
+ */
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+ Enumeration e = this.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ Object obj = e.nextElement();
+
+ dOut.writeObject(obj);
+ }
+
+ dOut.close();
+
+ byte[] bytes = bOut.toByteArray();
+
+ out.writeEncoded(SET | CONSTRUCTED, bytes);
+ }
+}
+
+/**
+ * basic interface for DER string objects.
+ */
+interface DERString
+{
+ public String getString();
+}
+
+
+/**
+ * DER T61String (also the teletex string)
+ */
+class DERT61String
+ extends DERObject
+ implements DERString
+{
+ String string;
+
+ /**
+ * return a T61 string from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERT61String getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERT61String)
+ {
+ return (DERT61String)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERT61String(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an T61 String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERT61String getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * basic constructor - with bytes.
+ */
+ public DERT61String(
+ byte[] string)
+ {
+ this.string = new String(string);
+ }
+
+ /**
+ * basic constructor - with string.
+ */
+ public DERT61String(
+ String string)
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ return string;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(T61_STRING, string.getBytes());
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERT61String))
+ {
+ return false;
+ }
+
+ return this.getString().equals(((DERT61String)o).getString());
+ }
+}
+
+
+/**
+ * DER TaggedObject - in ASN.1 nottation this is any object proceeded by
+ * a [n] where n is some number - these are assume to follow the construction
+ * rules (as with sequences).
+ */
+class DERTaggedObject
+ extends ASN1TaggedObject
+{
+ /**
+ * @param tagNo the tag number for this object.
+ * @param obj the tagged object.
+ */
+ public DERTaggedObject(
+ int tagNo,
+ DEREncodable obj)
+ {
+ super(tagNo, obj);
+ }
+
+ /**
+ * @param explicit true if an explicitly tagged object.
+ * @param tagNo the tag number for this object.
+ * @param obj the tagged object.
+ */
+ public DERTaggedObject(
+ boolean explicit,
+ int tagNo,
+ DEREncodable obj)
+ {
+ super(explicit, tagNo, obj);
+ }
+
+ /**
+ * create an implicitly tagged object that contains a zero
+ * length sequence.
+ */
+ public DERTaggedObject(
+ int tagNo)
+ {
+ super(false, tagNo, new DERSequence());
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ if (!empty)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+
+ dOut.writeObject(obj);
+ dOut.close();
+
+ byte[] bytes = bOut.toByteArray();
+
+ if (explicit)
+ {
+ out.writeEncoded(CONSTRUCTED | TAGGED | tagNo, bytes);
+ }
+ else
+ {
+ //
+ // need to mark constructed types...
+ //
+ if ((bytes[0] & CONSTRUCTED) != 0)
+ {
+ bytes[0] = (byte)(CONSTRUCTED | TAGGED | tagNo);
+ }
+ else
+ {
+ bytes[0] = (byte)(TAGGED | tagNo);
+ }
+
+ out.write(bytes);
+ }
+ }
+ else
+ {
+ out.writeEncoded(CONSTRUCTED | TAGGED | tagNo, new byte[0]);
+ }
+ }
+}
+
+interface DERTags
+{
+ public static final int BOOLEAN = 0x01;
+ public static final int INTEGER = 0x02;
+ public static final int BIT_STRING = 0x03;
+ public static final int OCTET_STRING = 0x04;
+ public static final int NULL = 0x05;
+ public static final int OBJECT_IDENTIFIER = 0x06;
+ public static final int EXTERNAL = 0x08;
+ public static final int ENUMERATED = 0x0a;
+ public static final int SEQUENCE = 0x10;
+ public static final int SEQUENCE_OF = 0x10; // for completeness
+ public static final int SET = 0x11;
+ public static final int SET_OF = 0x11; // for completeness
+ public static final int CONSTRUCTED = 0x20;
+ public static final int TAGGED = 0x80;
+
+ public static final int NUMERIC_STRING = 0x12;
+ public static final int PRINTABLE_STRING = 0x13;
+ public static final int T61_STRING = 0x14;
+ public static final int VIDEOTEX_STRING = 0x15;
+ public static final int IA5_STRING = 0x16;
+ public static final int UTC_TIME = 0x17;
+ public static final int GENERALIZED_TIME = 0x18;
+ public static final int GRAPHIC_STRING = 0x19;
+ public static final int VISIBLE_STRING = 0x1a;
+ public static final int GENERAL_STRING = 0x1b;
+ public static final int UNIVERSAL_STRING = 0x1c;
+ public static final int BMP_STRING = 0x1e;
+ public static final int UTF8_STRING = 0x0c;
+}
+
+
+/**
+ * DER UniversalString object.
+ */
+class DERUniversalString
+ extends DERObject
+ implements DERString
+{
+ byte[] string;
+ char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+ /**
+ * return a Universal String from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERUniversalString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERUniversalString)
+ {
+ return (DERUniversalString)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERUniversalString(((ASN1OctetString)obj).getOctets());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Universal String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERUniversalString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * basic constructor - byte encoded string.
+ */
+ public DERUniversalString(
+ byte[] string)
+ {
+ this.string = string;
+ }
+
+ /**
+ * UniversalStrings have characters which are 4 bytes long - for the
+ * moment we just return them in Hex...
+ */
+ public String getString()
+ {
+ StringBuffer buf = new StringBuffer();
+
+ for (int i = 0; i != string.length; i++)
+ {
+ buf.append(table[(string[i] >>> 4) % 0xf]);
+ buf.append(table[string[i] & 0xf]);
+ }
+
+ return buf.toString();
+ }
+
+ public byte[] getOctets()
+ {
+ return string;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(UNIVERSAL_STRING, this.getOctets());
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERUniversalString))
+ {
+ return false;
+ }
+
+ return this.getString().equals(((DERUniversalString)o).getString());
+ }
+}
+
+
+/**
+ * We insert one of these when we find a tag we don't recognise.
+ */
+class DERUnknownTag
+ extends DERObject
+{
+ int tag;
+ byte[] data;
+
+ /**
+ * @param tag the tag value.
+ * @param data the octets making up the time.
+ */
+ public DERUnknownTag(
+ int tag,
+ byte[] data)
+ {
+ this.tag = tag;
+ this.data = data;
+ }
+
+ public int getTag()
+ {
+ return tag;
+ }
+
+ public byte[] getData()
+ {
+ return data;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(tag, data);
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERUnknownTag))
+ {
+ return false;
+ }
+
+ DERUnknownTag other = (DERUnknownTag)o;
+
+ if(tag != other.tag)
+ {
+ return false;
+ }
+
+ if(data.length != other.data.length)
+ {
+ return false;
+ }
+
+ for(int i = 0; i < data.length; i++)
+ {
+ if(data[i] != other.data[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
+
+
+/**
+ * UTC time object.
+ */
+class DERUTCTime
+ extends DERObject
+{
+ String time;
+
+ /**
+ * return an UTC Time from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERUTCTime getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERUTCTime)
+ {
+ return (DERUTCTime)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERUTCTime(((ASN1OctetString)obj).getOctets());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an UTC Time from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERUTCTime getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were
+ * never encoded. When you're creating one of these objects from scratch, that's
+ * what you want to use, otherwise we'll try to deal with whatever gets read from
+ * the input stream... (this is why the input format is different from the getTime()
+ * method output).
+ * <p>
+ *
+ * @param time the time string.
+ */
+ public DERUTCTime(
+ String time)
+ {
+ this.time = time;
+ }
+
+ /**
+ * base constructer from a java.util.date object
+ */
+ public DERUTCTime(
+ Date time)
+ {
+ SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmss'Z'");
+
+ dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
+
+ this.time = dateF.format(time);
+ }
+
+ DERUTCTime(
+ byte[] bytes)
+ {
+ //
+ // explicitly convert to characters
+ //
+ char[] dateC = new char[bytes.length];
+
+ for (int i = 0; i != dateC.length; i++)
+ {
+ dateC[i] = (char)(bytes[i] & 0xff);
+ }
+
+ this.time = new String(dateC);
+ }
+
+ /**
+ * return the time - always in the form of
+ * YYMMDDhhmmssGMT(+hh:mm|-hh:mm).
+ * <p>
+ * Normally in a certificate we would expect "Z" rather than "GMT",
+ * however adding the "GMT" means we can just use:
+ * <pre>
+ * dateF = new SimpleDateFormat("yyMMddHHmmssz");
+ * </pre>
+ * To read in the time and get a date which is compatible with our local
+ * time zone.
+ * <p>
+ * <b>Note:</b> In some cases, due to the local date processing, this
+ * may lead to unexpected results. If you want to stick the normal
+ * convention of 1950 to 2049 use the getAdjustedTime() method.
+ */
+ public String getTime()
+ {
+ //
+ // standardise the format.
+ //
+ if (time.length() == 11)
+ {
+ return time.substring(0, 10) + "00GMT+00:00";
+ }
+ else if (time.length() == 13)
+ {
+ return time.substring(0, 12) + "GMT+00:00";
+ }
+ else if (time.length() == 17)
+ {
+ return time.substring(0, 12) + "GMT" + time.substring(12, 15) + ":" + time.substring(15, 17);
+ }
+
+ return time;
+ }
+
+ /**
+ * return the time as an adjusted date with a 4 digit year. This goes
+ * in the range of 1950 - 2049.
+ */
+ public String getAdjustedTime()
+ {
+ String d = this.getTime();
+
+ if (d.charAt(0) < '5')
+ {
+ return "20" + d;
+ }
+ else
+ {
+ return "19" + d;
+ }
+ }
+
+ private byte[] getOctets()
+ {
+ char[] cs = time.toCharArray();
+ byte[] bs = new byte[cs.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ bs[i] = (byte)cs[i];
+ }
+
+ return bs;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(UTC_TIME, this.getOctets());
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERUTCTime))
+ {
+ return false;
+ }
+
+ return time.equals(((DERUTCTime)o).time);
+ }
+}
+
+
+/**
+ * DER UTF8String object.
+ */
+class DERUTF8String
+ extends DERObject
+ implements DERString
+{
+ String string;
+
+ /**
+ * return an UTF8 string from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERUTF8String getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERUTF8String)
+ {
+ return (DERUTF8String)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERUTF8String(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an UTF8 String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERUTF8String getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * basic constructor - byte encoded string.
+ */
+ DERUTF8String(
+ byte[] string)
+ {
+ int i = 0;
+ int length = 0;
+
+ while (i < string.length)
+ {
+ length++;
+ if ((string[i] & 0xe0) == 0xe0)
+ {
+ i += 3;
+ }
+ else if ((string[i] & 0xc0) == 0xc0)
+ {
+ i += 2;
+ }
+ else
+ {
+ i += 1;
+ }
+ }
+
+ char[] cs = new char[length];
+
+ i = 0;
+ length = 0;
+
+ while (i < string.length)
+ {
+ char ch;
+
+ if ((string[i] & 0xe0) == 0xe0)
+ {
+ ch = (char)(((string[i] & 0x1f) << 12)
+ | ((string[i + 1] & 0x3f) << 6) | (string[i + 2] & 0x3f));
+ i += 3;
+ }
+ else if ((string[i] & 0xc0) == 0xc0)
+ {
+ ch = (char)(((string[i] & 0x3f) << 6) | (string[i + 1] & 0x3f));
+ i += 2;
+ }
+ else
+ {
+ ch = (char)(string[i] & 0xff);
+ i += 1;
+ }
+
+ cs[length++] = ch;
+ }
+
+ this.string = new String(cs);
+ }
+
+ /**
+ * basic constructor
+ */
+ public DERUTF8String(
+ String string)
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ return string;
+ }
+
+ public int hashCode()
+ {
+ return this.getString().hashCode();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DERUTF8String))
+ {
+ return false;
+ }
+
+ DERUTF8String s = (DERUTF8String)o;
+
+ return this.getString().equals(s.getString());
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ char[] c = string.toCharArray();
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ for (int i = 0; i != c.length; i++)
+ {
+ char ch = c[i];
+
+ if (ch < 0x0080)
+ {
+ bOut.write(ch);
+ }
+ else if (ch < 0x0800)
+ {
+ bOut.write(0xc0 | (ch >> 6));
+ bOut.write(0x80 | (ch & 0x3f));
+ }
+ else
+ {
+ bOut.write(0xe0 | (ch >> 12));
+ bOut.write(0x80 | ((ch >> 6) & 0x3F));
+ bOut.write(0x80 | (ch & 0x3F));
+ }
+ }
+
+ out.writeEncoded(UTF8_STRING, bOut.toByteArray());
+ }
+}
+
+
+/**
+ * DER VisibleString object.
+ */
+class DERVisibleString
+ extends DERObject
+ implements DERString
+{
+ String string;
+
+ /**
+ * return a Visible String from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERVisibleString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERVisibleString)
+ {
+ return (DERVisibleString)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERVisibleString(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Visible String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERVisibleString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * basic constructor - byte encoded string.
+ */
+ public DERVisibleString(
+ byte[] string)
+ {
+ char[] cs = new char[string.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ cs[i] = (char)(string[i] & 0xff);
+ }
+
+ this.string = new String(cs);
+ }
+
+ /**
+ * basic constructor
+ */
+ public DERVisibleString(
+ String string)
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ return string;
+ }
+
+ public byte[] getOctets()
+ {
+ char[] cs = string.toCharArray();
+ byte[] bs = new byte[cs.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ bs[i] = (byte)cs[i];
+ }
+
+ return bs;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(VISIBLE_STRING, this.getOctets());
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERVisibleString))
+ {
+ return false;
+ }
+
+ return this.getString().equals(((DERVisibleString)o).getString());
+ }
+}
+
+/**
+ * class for breaking up an OID into it's component tokens, ala
+ * java.util.StringTokenizer. We need this class as some of the
+ * lightweight Java environment don't support classes like
+ * StringTokenizer.
+ */
+class OIDTokenizer
+{
+ private String oid;
+ private int index;
+
+ public OIDTokenizer(
+ String oid)
+ {
+ this.oid = oid;
+ this.index = 0;
+ }
+
+ public boolean hasMoreTokens()
+ {
+ return (index != -1);
+ }
+
+ public String nextToken()
+ {
+ if (index == -1)
+ {
+ return null;
+ }
+
+ String token;
+ int end = oid.indexOf('.', index);
+
+ if (end == -1)
+ {
+ token = oid.substring(index);
+ index = -1;
+ return token;
+ }
+
+ token = oid.substring(index, end);
+
+ index = end + 1;
+ return token;
+ }
+}
+
+
+interface PKCSObjectIdentifiers
+{
+ //
+ // pkcs-1 OBJECT IDENTIFIER ::= {
+ // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 }
+ //
+ static final String pkcs_1 = "1.2.840.113549.1.1";
+ static final DERObjectIdentifier rsaEncryption = new DERObjectIdentifier(pkcs_1 + ".1");
+ static final DERObjectIdentifier md2WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".2");
+ static final DERObjectIdentifier md4WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".3");
+ static final DERObjectIdentifier md5WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".4");
+ static final DERObjectIdentifier sha1WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".5");
+ static final DERObjectIdentifier srsaOAEPEncryptionSET = new DERObjectIdentifier(pkcs_1 + ".6");
+ static final DERObjectIdentifier sha256WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".11");
+ static final DERObjectIdentifier sha384WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".12");
+ static final DERObjectIdentifier sha512WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".13");
+
+ //
+ // pkcs-3 OBJECT IDENTIFIER ::= {
+ // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 3 }
+ //
+ static final String pkcs_3 = "1.2.840.113549.1.3";
+ static final DERObjectIdentifier dhKeyAgreement = new DERObjectIdentifier(pkcs_3 + ".1");
+
+ //
+ // pkcs-5 OBJECT IDENTIFIER ::= {
+ // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 5 }
+ //
+ static final String pkcs_5 = "1.2.840.113549.1.5";
+
+ static final DERObjectIdentifier id_PBES2 = new DERObjectIdentifier(pkcs_5 + ".13");
+
+ static final DERObjectIdentifier id_PBKDF2 = new DERObjectIdentifier(pkcs_5 + ".12");
+
+ //
+ // encryptionAlgorithm OBJECT IDENTIFIER ::= {
+ // iso(1) member-body(2) us(840) rsadsi(113549) 3 }
+ //
+ static final String encryptionAlgorithm = "1.2.840.113549.3";
+
+ static final DERObjectIdentifier des_EDE3_CBC = new DERObjectIdentifier(encryptionAlgorithm + ".7");
+ static final DERObjectIdentifier RC2_CBC = new DERObjectIdentifier(encryptionAlgorithm + ".2");
+
+ //
+ // object identifiers for digests
+ //
+
+ //
+ // md2 OBJECT IDENTIFIER ::=
+ // {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 2}
+ //
+ static final DERObjectIdentifier md2 = new DERObjectIdentifier("1.2.840.113549.2.2");
+
+ //
+ // md5 OBJECT IDENTIFIER ::=
+ // {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 5}
+ //
+ static final DERObjectIdentifier md5 = new DERObjectIdentifier("1.2.840.113549.2.5");
+
+ //
+ // pkcs-7 OBJECT IDENTIFIER ::= {
+ // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 7 }
+ //
+ static final String pkcs_7 = "1.2.840.113549.1.7";
+ static final DERObjectIdentifier data = new DERObjectIdentifier(pkcs_7 + ".1");
+ static final DERObjectIdentifier signedData = new DERObjectIdentifier(pkcs_7 + ".2");
+ static final DERObjectIdentifier envelopedData = new DERObjectIdentifier(pkcs_7 + ".3");
+ static final DERObjectIdentifier signedAndEnvelopedData = new DERObjectIdentifier(pkcs_7 + ".4");
+ static final DERObjectIdentifier digestedData = new DERObjectIdentifier(pkcs_7 + ".5");
+ static final DERObjectIdentifier encryptedData = new DERObjectIdentifier(pkcs_7 + ".6");
+
+ //
+ // pkcs-9 OBJECT IDENTIFIER ::= {
+ // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 }
+ //
+ static final String pkcs_9 = "1.2.840.113549.1.9";
+
+ static final DERObjectIdentifier pkcs_9_at_emailAddress = new DERObjectIdentifier(pkcs_9 + ".1");
+ static final DERObjectIdentifier pkcs_9_at_unstructuredName = new DERObjectIdentifier(pkcs_9 + ".2");
+ static final DERObjectIdentifier pkcs_9_at_contentType = new DERObjectIdentifier(pkcs_9 + ".3");
+ static final DERObjectIdentifier pkcs_9_at_messageDigest = new DERObjectIdentifier(pkcs_9 + ".4");
+ static final DERObjectIdentifier pkcs_9_at_signingTime = new DERObjectIdentifier(pkcs_9 + ".5");
+ static final DERObjectIdentifier pkcs_9_at_counterSignature = new DERObjectIdentifier(pkcs_9 + ".6");
+ static final DERObjectIdentifier pkcs_9_at_challengePassword = new DERObjectIdentifier(pkcs_9 + ".7");
+ static final DERObjectIdentifier pkcs_9_at_unstructuredAddress = new DERObjectIdentifier(pkcs_9 + ".8");
+ static final DERObjectIdentifier pkcs_9_at_extendedCertificateAttributes = new DERObjectIdentifier(pkcs_9 + ".9");
+
+ static final DERObjectIdentifier pkcs_9_at_signingDescription = new DERObjectIdentifier(pkcs_9 + ".13");
+ static final DERObjectIdentifier pkcs_9_at_extensionRequest = new DERObjectIdentifier(pkcs_9 + ".14");
+ static final DERObjectIdentifier pkcs_9_at_smimeCapabilities = new DERObjectIdentifier(pkcs_9 + ".15");
+
+ static final DERObjectIdentifier pkcs_9_at_friendlyName = new DERObjectIdentifier(pkcs_9 + ".20");
+ static final DERObjectIdentifier pkcs_9_at_localKeyId = new DERObjectIdentifier(pkcs_9 + ".21");
+
+ static final DERObjectIdentifier x509certType = new DERObjectIdentifier(pkcs_9 + ".22.1");
+
+ //
+ // SMIME capability sub oids.
+ //
+ static final DERObjectIdentifier preferSignedData = new DERObjectIdentifier(pkcs_9 + ".15.1");
+ static final DERObjectIdentifier canNotDecryptAny = new DERObjectIdentifier(pkcs_9 + ".15.2");
+ static final DERObjectIdentifier sMIMECapabilitiesVersions = new DERObjectIdentifier(pkcs_9 + ".15.3");
+
+ //
+ // other SMIME attributes
+ //
+
+ //
+ // id-aa OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840)
+ // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) attributes(2)}
+ //
+ static String id_aa = "1.2.840.113549.1.9.16.2";
+
+ /*
+ * id-aa-encrypKeyPref OBJECT IDENTIFIER ::= {id-aa 11}
+ *
+ */
+ static DERObjectIdentifier id_aa_encrypKeyPref = new DERObjectIdentifier(id_aa + ".11");
+
+ //
+ // pkcs-12 OBJECT IDENTIFIER ::= {
+ // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 12 }
+ //
+ static final String pkcs_12 = "1.2.840.113549.1.12";
+ static final String bagtypes = pkcs_12 + ".10.1";
+
+ static final DERObjectIdentifier keyBag = new DERObjectIdentifier(bagtypes + ".1");
+ static final DERObjectIdentifier pkcs8ShroudedKeyBag = new DERObjectIdentifier(bagtypes + ".2");
+ static final DERObjectIdentifier certBag = new DERObjectIdentifier(bagtypes + ".3");
+ static final DERObjectIdentifier crlBag = new DERObjectIdentifier(bagtypes + ".4");
+ static final DERObjectIdentifier secretBag = new DERObjectIdentifier(bagtypes + ".5");
+ static final DERObjectIdentifier safeContentsBag = new DERObjectIdentifier(bagtypes + ".6");
+}
+
+
+
+
+class AlgorithmIdentifier
+ implements DEREncodable
+{
+ private DERObjectIdentifier objectId;
+ private DEREncodable parameters;
+ private boolean parametersDefined = false;
+
+ public static AlgorithmIdentifier getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static AlgorithmIdentifier getInstance(
+ Object obj)
+ {
+ if (obj instanceof AlgorithmIdentifier)
+ {
+ return (AlgorithmIdentifier)obj;
+ }
+
+ if (obj instanceof DERObjectIdentifier)
+ {
+ return new AlgorithmIdentifier((DERObjectIdentifier)obj);
+ }
+
+ if (obj instanceof String)
+ {
+ return new AlgorithmIdentifier((String)obj);
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new AlgorithmIdentifier((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory");
+ }
+
+ public AlgorithmIdentifier(
+ DERObjectIdentifier objectId)
+ {
+ this.objectId = objectId;
+ }
+
+ public AlgorithmIdentifier(
+ String objectId)
+ {
+ this.objectId = new DERObjectIdentifier(objectId);
+ }
+
+ public AlgorithmIdentifier(
+ DERObjectIdentifier objectId,
+ DEREncodable parameters)
+ {
+ parametersDefined = true;
+ this.objectId = objectId;
+ this.parameters = parameters;
+ }
+
+ public AlgorithmIdentifier(
+ ASN1Sequence seq)
+ {
+ objectId = (DERObjectIdentifier)seq.getObjectAt(0);
+
+ if (seq.size() == 2)
+ {
+ parametersDefined = true;
+ parameters = seq.getObjectAt(1);
+ }
+ else
+ {
+ parameters = null;
+ }
+ }
+
+ public DERObjectIdentifier getObjectId()
+ {
+ return objectId;
+ }
+
+ public DEREncodable getParameters()
+ {
+ return parameters;
+ }
+
+ /**
+ * <pre>
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL }
+ * </pre>
+ */
+ public DERObject getDERObject()
+ {
+ DERConstructedSequence seq = new DERConstructedSequence();
+
+ seq.addObject(objectId);
+
+ if (parametersDefined)
+ {
+ seq.addObject(parameters);
+ }
+
+ return seq;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof AlgorithmIdentifier))
+ {
+ return false;
+ }
+
+ AlgorithmIdentifier other = (AlgorithmIdentifier)o;
+
+ if (!this.getObjectId().equals(other.getObjectId()))
+ {
+ return false;
+ }
+
+ if (this.getParameters() == null && other.getParameters() == null)
+ {
+ return true;
+ }
+
+ if (this.getParameters() == null || other.getParameters() == null)
+ {
+ return false;
+ }
+
+ ByteArrayOutputStream b1Out = new ByteArrayOutputStream();
+ ByteArrayOutputStream b2Out = new ByteArrayOutputStream();
+ DEROutputStream d1Out = new DEROutputStream(b1Out);
+ DEROutputStream d2Out = new DEROutputStream(b2Out);
+
+ try
+ {
+ d1Out.writeObject(this.getParameters());
+ d2Out.writeObject(other.getParameters());
+
+ byte[] b1 = b1Out.toByteArray();
+ byte[] b2 = b2Out.toByteArray();
+
+ if (b1.length != b2.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != b1.length; i++)
+ {
+ if (b1[i] != b2[i])
+ {
+ return false;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ return false;
+ }
+
+ return true;
+ }
+}
+
+
+
+
+/**
+ * <pre>
+ * id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 }
+ *
+ * AuthorityKeyIdentifier ::= SEQUENCE {
+ * keyIdentifier [0] IMPLICIT KeyIdentifier OPTIONAL,
+ * authorityCertIssuer [1] IMPLICIT GeneralNames OPTIONAL,
+ * authorityCertSerialNumber [2] IMPLICIT CertificateSerialNumber OPTIONAL }
+ *
+ * KeyIdentifier ::= OCTET STRING
+ * </pre>
+ *
+ */
+class AuthorityKeyIdentifier
+ implements DEREncodable, DERTags
+{
+ ASN1OctetString keyidentifier=null;
+ GeneralNames certissuer=null;
+ DERInteger certserno=null;
+
+ public static AuthorityKeyIdentifier getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static AuthorityKeyIdentifier getInstance(
+ Object obj)
+ {
+ if (obj instanceof AuthorityKeyIdentifier)
+ {
+ return (AuthorityKeyIdentifier)obj;
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ return new AuthorityKeyIdentifier((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory");
+ }
+
+ public AuthorityKeyIdentifier(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ DERTaggedObject o = (DERTaggedObject)e.nextElement();
+
+ switch (o.getTagNo())
+ {
+ case 0:
+ this.keyidentifier = ASN1OctetString.getInstance(o, false);
+ break;
+ case 1:
+ this.certissuer = GeneralNames.getInstance(o, false);
+ break;
+ case 2:
+ this.certserno = DERInteger.getInstance(o, false);
+ break;
+ default:
+ throw new IllegalArgumentException("illegal tag");
+ }
+ }
+ }
+
+ /**
+ *
+ * Calulates the keyidentifier using a SHA1 hash over the BIT STRING
+ * from SubjectPublicKeyInfo as defined in RFC2459.
+ *
+ * Example of making a AuthorityKeyIdentifier:
+ * <pre>
+ * SubjectPublicKeyInfo apki = new SubjectPublicKeyInfo((DERConstructedSequence)new DERInputStream(
+ * new ByteArrayInputStream(publicKey.getEncoded())).readObject());
+ * AuthorityKeyIdentifier aki = new AuthorityKeyIdentifier(apki);
+ * </pre>
+ *
+ **/
+ public AuthorityKeyIdentifier(
+ SubjectPublicKeyInfo spki)
+ {
+ Digest digest = new SHA1Digest();
+ byte[] resBuf = new byte[digest.getDigestSize()];
+
+ byte[] bytes = spki.getPublicKeyData().getBytes();
+ digest.update(bytes, 0, bytes.length);
+ digest.doFinal(resBuf, 0);
+ this.keyidentifier = new DEROctetString(resBuf);
+ }
+
+ /**
+ * create an AuthorityKeyIdentifier with the GeneralNames tag and
+ * the serial number provided as well.
+ */
+ public AuthorityKeyIdentifier(
+ SubjectPublicKeyInfo spki,
+ GeneralNames name,
+ BigInteger serialNumber)
+ {
+ Digest digest = new SHA1Digest();
+ byte[] resBuf = new byte[digest.getDigestSize()];
+
+ byte[] bytes = spki.getPublicKeyData().getBytes();
+ digest.update(bytes, 0, bytes.length);
+ digest.doFinal(resBuf, 0);
+
+ this.keyidentifier = new DEROctetString(resBuf);
+ this.certissuer = name;
+ this.certserno = new DERInteger(serialNumber);
+ }
+
+ public byte[] getKeyIdentifier()
+ {
+ if (keyidentifier != null)
+ {
+ return keyidentifier.getOctets();
+ }
+
+ return null;
+ }
+
+ /**
+ * <pre>
+ * AuthorityKeyIdentifier ::= SEQUENCE {
+ * keyIdentifier [0] IMPLICIT KeyIdentifier OPTIONAL,
+ * authorityCertIssuer [1] IMPLICIT GeneralNames OPTIONAL,
+ * authorityCertSerialNumber [2] IMPLICIT CertificateSerialNumber OPTIONAL }
+ *
+ * KeyIdentifier ::= OCTET STRING
+ * </pre>
+ */
+ public DERObject getDERObject()
+ {
+ DERConstructedSequence seq = new DERConstructedSequence();
+
+ if (keyidentifier != null)
+ {
+ seq.addObject(new DERTaggedObject(false, 0, keyidentifier));
+ }
+
+ if (certissuer != null)
+ {
+ seq.addObject(new DERTaggedObject(false, 1, certissuer));
+ }
+
+ if (certserno != null)
+ {
+ seq.addObject(new DERTaggedObject(false, 2, certserno));
+ }
+
+
+ return seq;
+ }
+
+ public String toString()
+ {
+ return ("AuthorityKeyIdentifier: KeyID(" + this.keyidentifier.getOctets() + ")");
+ }
+}
+
+
+
+class BasicConstraints
+ implements DEREncodable
+{
+ DERBoolean cA = new DERBoolean(false);
+ DERInteger pathLenConstraint = null;
+
+ public static BasicConstraints getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static BasicConstraints getInstance(
+ Object obj)
+ {
+ if (obj instanceof BasicConstraints)
+ {
+ return (BasicConstraints)obj;
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ return new BasicConstraints((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory");
+ }
+
+ public BasicConstraints(
+ ASN1Sequence seq)
+ {
+ if (seq.size() != 0)
+ {
+ this.cA = (DERBoolean)seq.getObjectAt(0);
+ this.pathLenConstraint = (DERInteger)seq.getObjectAt(1);
+ }
+ }
+
+ public BasicConstraints(
+ boolean cA,
+ int pathLenConstraint)
+ {
+ this.cA = new DERBoolean(cA);
+ this.pathLenConstraint = new DERInteger(pathLenConstraint);
+ }
+
+ public BasicConstraints(
+ boolean cA)
+ {
+ this.cA = new DERBoolean(cA);
+ this.pathLenConstraint = null;
+ }
+
+ public boolean isCA()
+ {
+ return cA.isTrue();
+ }
+
+ public BigInteger getPathLenConstraint()
+ {
+ if (pathLenConstraint != null)
+ {
+ return pathLenConstraint.getValue();
+ }
+
+ return null;
+ }
+
+ /**
+ * <pre>
+ * BasicConstraints := SEQUENCE {
+ * cA BOOLEAN DEFAULT FALSE,
+ * pathLenConstraint INTEGER (0..MAX) OPTIONAL
+ * }
+ * </pre>
+ */
+ public DERObject getDERObject()
+ {
+ DERConstructedSequence seq = new DERConstructedSequence();
+
+ seq.addObject(cA);
+
+ if (pathLenConstraint != null)
+ {
+ seq.addObject(pathLenConstraint);
+ }
+
+ return seq;
+ }
+
+ public String toString()
+ {
+ return "BasicConstraints: isCa(" + this.isCA() + "), pathLenConstraint = " + pathLenConstraint.getValue();
+ }
+}
+
+
+
+/**
+ * PKIX RFC-2459
+ *
+ * The X.509 v2 CRL syntax is as follows. For signature calculation,
+ * the data that is to be signed is ASN.1 DER encoded.
+ *
+ * <pre>
+ * CertificateList ::= SEQUENCE {
+ * tbsCertList TBSCertList,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signatureValue BIT STRING }
+ * </pre>
+ */
+class CertificateList
+ implements DEREncodable
+{
+ TBSCertList tbsCertList;
+ AlgorithmIdentifier sigAlgId;
+ DERBitString sig;
+
+ public static CertificateList getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static CertificateList getInstance(
+ Object obj)
+ {
+ if (obj instanceof CertificateList)
+ {
+ return (CertificateList)obj;
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ return new CertificateList((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory");
+ }
+
+ public CertificateList(
+ ASN1Sequence seq)
+ {
+ tbsCertList = TBSCertList.getInstance(seq.getObjectAt(0));
+ sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+ sig = (DERBitString)seq.getObjectAt(2);
+ }
+
+ public TBSCertList getTBSCertList()
+ {
+ return tbsCertList;
+ }
+
+ public TBSCertList.CRLEntry[] getRevokedCertificates()
+ {
+ return tbsCertList.getRevokedCertificates();
+ }
+
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return sigAlgId;
+ }
+
+ public DERBitString getSignature()
+ {
+ return sig;
+ }
+
+ public int getVersion()
+ {
+ return tbsCertList.getVersion();
+ }
+
+ public X509Name getIssuer()
+ {
+ return tbsCertList.getIssuer();
+ }
+
+ public Time getThisUpdate()
+ {
+ return tbsCertList.getThisUpdate();
+ }
+
+ public Time getNextUpdate()
+ {
+ return tbsCertList.getNextUpdate();
+ }
+
+ public DERObject getDERObject()
+ {
+ DERConstructedSequence seq = new DERConstructedSequence();
+ seq.addObject(tbsCertList);
+ seq.addObject(sigAlgId);
+ seq.addObject(sig);
+ return seq;
+ }
+}
+
+
+class CRLDistPoint
+ implements DEREncodable
+{
+ ASN1Sequence seq = null;
+
+ public static CRLDistPoint getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static CRLDistPoint getInstance(
+ Object obj)
+ {
+ if (obj instanceof CRLDistPoint)
+ {
+ return (CRLDistPoint)obj;
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ return new CRLDistPoint((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory");
+ }
+
+ public CRLDistPoint(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+ }
+
+ public CRLDistPoint(
+ DistributionPoint[] points)
+ {
+ DEREncodableVector v = new DEREncodableVector();
+
+ for (int i = 0; i != points.length; i++)
+ {
+ v.add(points[i]);
+ }
+
+ seq = new DERSequence(v);
+ }
+
+ /**
+ * <pre>
+ * CRLDistPoint ::= SEQUENCE SIZE {1..MAX} OF DistributionPoint
+ * </pre>
+ */
+ public DERObject getDERObject()
+ {
+ return seq;
+ }
+}
+
+
+
+/**
+ * <pre>
+ * CRLNumber::= INTEGER(0..MAX)
+ * </pre>
+ */
+class CRLNumber
+ extends DERInteger
+{
+
+ public CRLNumber(
+ BigInteger number)
+ {
+ super(number);
+ }
+
+ public BigInteger getCRLNumber()
+ {
+ return getPositiveValue();
+ }
+}
+
+
+class CRLReason
+ extends DEREnumerated
+{
+ public static final int UNSPECIFIED = 0;
+ public static final int KEY_COMPROMISE = 1;
+ public static final int CA_COMPROMISE = 2;
+ public static final int AFFILIATION_CHANGED = 3;
+ public static final int SUPERSEDED = 4;
+ public static final int CESSATION_OF_OPERATION = 5;
+ public static final int CERTIFICATE_HOLD = 6;
+ public static final int REMOVE_FROM_CRL = 8;
+ public static final int PRIVILEGE_WITHDRAWN = 9;
+ public static final int AA_COMPROMISE = 10;
+
+ /**
+ * <pre>
+ * CRLReason ::= ENUMERATED {
+ * unspecified (0),
+ * keyCompromise (1),
+ * cACompromise (2),
+ * affiliationChanged (3),
+ * superseded (4),
+ * cessationOfOperation (5),
+ * certificateHold (6),
+ * removeFromCRL (8),
+ * privilegeWithdrawn (9),
+ * aACompromise (10)
+ * }
+ * </pre>
+ */
+ public CRLReason(
+ int reason)
+ {
+ super(reason);
+ }
+}
+
+
+
+/**
+ * <pre>
+ * DigestInfo::=SEQUENCE{
+ * digestAlgorithm AlgorithmIdentifier,
+ * digest OCTET STRING }
+ * </pre>
+ */
+class DigestInfo
+ implements DEREncodable
+{
+ private byte[] digest;
+ private AlgorithmIdentifier algId;
+
+ public static DigestInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static DigestInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof DigestInfo)
+ {
+ return (DigestInfo)obj;
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ return new DigestInfo((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory");
+ }
+
+ public DigestInfo(
+ AlgorithmIdentifier algId,
+ byte[] digest)
+ {
+ this.digest = digest;
+ this.algId = algId;
+ }
+
+ public DigestInfo(
+ ASN1Sequence obj)
+ {
+ Enumeration e = obj.getObjects();
+
+ algId = AlgorithmIdentifier.getInstance(e.nextElement());
+ digest = ((ASN1OctetString)e.nextElement()).getOctets();
+ }
+
+ public AlgorithmIdentifier getAlgorithmId()
+ {
+ return algId;
+ }
+
+ public byte[] getDigest()
+ {
+ return digest;
+ }
+
+ public DERObject getDERObject()
+ {
+ DEREncodableVector v = new DEREncodableVector();
+
+ v.add(algId);
+ v.add(new DEROctetString(digest));
+
+ return new DERSequence(v);
+ }
+}
+
+
+class DistributionPoint
+ implements DEREncodable
+{
+ ASN1Sequence seq = null;
+
+ public static DistributionPoint getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static DistributionPoint getInstance(
+ Object obj)
+ {
+ if(obj == null || obj instanceof DistributionPoint)
+ {
+ return (DistributionPoint)obj;
+ }
+
+ if(obj instanceof ASN1Sequence)
+ {
+ return new DistributionPoint((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid DistributionPoint: " + obj.getClass().getName());
+ }
+
+ public DistributionPoint(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+ }
+
+ public DistributionPoint(
+ DistributionPointName distributionPoint,
+ ReasonFlags reasons,
+ GeneralNames cRLIssuer)
+ {
+ DEREncodableVector v = new DEREncodableVector();
+
+ if (distributionPoint != null)
+ {
+ v.add(new DERTaggedObject(0, distributionPoint));
+ }
+
+ if (reasons != null)
+ {
+ v.add(new DERTaggedObject(1, reasons));
+ }
+
+ if (cRLIssuer != null)
+ {
+ v.add(new DERTaggedObject(2, cRLIssuer));
+ }
+
+ seq = new DERSequence(v);
+ }
+
+ /**
+ * <pre>
+ * DistributionPoint ::= SEQUENCE {
+ * distributionPoint [0] DistributionPointName OPTIONAL,
+ * reasons [1] ReasonFlags OPTIONAL,
+ * cRLIssuer [2] GeneralNames OPTIONAL
+ * }
+ * </pre>
+ */
+ public DERObject getDERObject()
+ {
+ return seq;
+ }
+}
+
+
+class DistributionPointName
+ implements DEREncodable
+{
+ DEREncodable name;
+ int type;
+
+ public static final int FULL_NAME = 0;
+ public static final int NAME_RELATIVE_TO_CRL_ISSUER = 1;
+
+ public DistributionPointName(
+ int type,
+ DEREncodable name)
+ {
+ this.type = type;
+ this.name = name;
+ }
+
+ /**
+ * <pre>
+ * DistributionPointName ::= CHOICE {
+ * fullName [0] GeneralNames,
+ * nameRelativeToCRLIssuer [1] RelativeDistinguishedName
+ * }
+ * </pre>
+ */
+ public DERObject getDERObject()
+ {
+ return new DERTaggedObject(false, type, name);
+ }
+}
+
+
+
+class DSAParameter
+ implements DEREncodable
+{
+ DERInteger p, q, g;
+
+ public static DSAParameter getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static DSAParameter getInstance(
+ Object obj)
+ {
+ if(obj == null || obj instanceof DSAParameter)
+ {
+ return (DSAParameter)obj;
+ }
+
+ if(obj instanceof ASN1Sequence)
+ {
+ return new DSAParameter((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid DSAParameter: " + obj.getClass().getName());
+ }
+
+ public DSAParameter(
+ BigInteger p,
+ BigInteger q,
+ BigInteger g)
+ {
+ this.p = new DERInteger(p);
+ this.q = new DERInteger(q);
+ this.g = new DERInteger(g);
+ }
+
+ public DSAParameter(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ p = (DERInteger)e.nextElement();
+ q = (DERInteger)e.nextElement();
+ g = (DERInteger)e.nextElement();
+ }
+
+ public BigInteger getP()
+ {
+ return p.getPositiveValue();
+ }
+
+ public BigInteger getQ()
+ {
+ return q.getPositiveValue();
+ }
+
+ public BigInteger getG()
+ {
+ return g.getPositiveValue();
+ }
+
+ public DERObject getDERObject()
+ {
+ DEREncodableVector v = new DEREncodableVector();
+
+ v.add(p);
+ v.add(q);
+ v.add(g);
+
+ return new DERSequence(v);
+ }
+}
+
+
+/**
+ * <pre>
+ * GeneralName ::= CHOICE {
+ * otherName [0] OtherName,
+ * rfc822Name [1] IA5String,
+ * dNSName [2] IA5String,
+ * x400Address [3] ORAddress,
+ * directoryName [4] Name,
+ * ediPartyName [5] EDIPartyName,
+ * uniformResourceIdentifier [6] IA5String,
+ * iPAddress [7] OCTET STRING,
+ * registeredID [8] OBJECT IDENTIFIER}
+ *
+ * OtherName ::= SEQUENCE {
+ * type-id OBJECT IDENTIFIER,
+ * value [0] EXPLICIT ANY DEFINED BY type-id }
+ *
+ * EDIPartyName ::= SEQUENCE {
+ * nameAssigner [0] DirectoryString OPTIONAL,
+ * partyName [1] DirectoryString }
+ * </pre>
+ */
+class GeneralName
+ implements DEREncodable
+{
+ DEREncodable obj;
+ int tag;
+ boolean isInsideImplicit = false; // if we are in an implicitly tagged object
+
+ public GeneralName(
+ X509Name directoryName)
+ {
+ this.obj = directoryName;
+ this.tag = 4;
+ }
+
+ /**
+ * When the subjectAltName extension contains an Internet mail address,
+ * the address MUST be included as an rfc822Name. The format of an
+ * rfc822Name is an "addr-spec" as defined in RFC 822 [RFC 822].
+ *
+ * When the subjectAltName extension contains a domain name service
+ * label, the domain name MUST be stored in the dNSName (an IA5String).
+ * The name MUST be in the "preferred name syntax," as specified by RFC
+ * 1034 [RFC 1034].
+ *
+ * When the subjectAltName extension contains a URI, the name MUST be
+ * stored in the uniformResourceIdentifier (an IA5String). The name MUST
+ * be a non-relative URL, and MUST follow the URL syntax and encoding
+ * rules specified in [RFC 1738]. The name must include both a scheme
+ * (e.g., "http" or "ftp") and a scheme-specific-part. The scheme-
+ * specific-part must include a fully qualified domain name or IP
+ * address as the host.
+ *
+ * When the subjectAltName extension contains a iPAddress, the address
+ * MUST be stored in the octet string in "network byte order," as
+ * specified in RFC 791 [RFC 791]. The least significant bit (LSB) of
+ * each octet is the LSB of the corresponding byte in the network
+ * address. For IP Version 4, as specified in RFC 791, the octet string
+ * MUST contain exactly four octets. For IP Version 6, as specified in
+ * RFC 1883, the octet string MUST contain exactly sixteen octets [RFC
+ * 1883].
+ */
+ public GeneralName(
+ DERObject name, int tag)
+ {
+ this.obj = name;
+ this.tag = tag;
+ }
+
+ /**
+ * mark whether or not we are contained inside an implicitly tagged
+ * object.
+ * @deprecated
+ */
+ public void markInsideImplicit(
+ boolean isInsideImplicit)
+ {
+ this.isInsideImplicit = isInsideImplicit;
+ }
+
+ public DERObject getDERObject()
+ {
+ if (obj.getDERObject() instanceof ASN1Sequence)
+ {
+ return new DERTaggedObject(true, tag, obj);
+ }
+ else
+ {
+ return new DERTaggedObject(false, tag, obj);
+ }
+ }
+}
+
+
+
+class GeneralNames
+ implements DEREncodable
+{
+ ASN1Sequence seq;
+ boolean isInsideImplicit = false;
+
+ public static GeneralNames getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof GeneralNames)
+ {
+ return (GeneralNames)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new GeneralNames((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ public static GeneralNames getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public GeneralNames(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+ }
+
+ /*
+ * this is a hack! But it will have to do until the ambiguity rules
+ * get sorted out for implicit/explicit tagging...
+ * @deprecated
+ */
+ public void markInsideImplicit(
+ boolean isInsideImplicit)
+ {
+ this.isInsideImplicit = isInsideImplicit;
+ }
+
+ /**
+ * <pre>
+ * GeneralNames ::= SEQUENCE SIZE {1..MAX} OF GeneralName
+ * </pre>
+ */
+ public DERObject getDERObject()
+ {
+ return seq;
+ }
+}
+
+
+/**
+ * <pre>
+ * id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 }
+ *
+ * KeyUsage ::= BIT STRING {
+ * digitalSignature (0),
+ * nonRepudiation (1),
+ * keyEncipherment (2),
+ * dataEncipherment (3),
+ * keyAgreement (4),
+ * keyCertSign (5),
+ * cRLSign (6),
+ * encipherOnly (7),
+ * decipherOnly (8) }
+ * </pre>
+ */
+class KeyUsage
+ extends DERBitString
+{
+ public static final int digitalSignature = (1 << 7);
+ public static final int nonRepudiation = (1 << 6);
+ public static final int keyEncipherment = (1 << 5);
+ public static final int dataEncipherment = (1 << 4);
+ public static final int keyAgreement = (1 << 3);
+ public static final int keyCertSign = (1 << 2);
+ public static final int cRLSign = (1 << 1);
+ public static final int encipherOnly = (1 << 0);
+ public static final int decipherOnly = (1 << 15);
+
+ /**
+ * Basic constructor.
+ *
+ * @param usage - the bitwise OR of the Key Usage flags giving the
+ * allowed uses for the key.
+ * e.g. (X509KeyUsage.keyEncipherment | X509KeyUsage.dataEncipherment)
+ */
+ public KeyUsage(
+ int usage)
+ {
+ super(getBytes(usage), getPadBits(usage));
+ }
+
+ public KeyUsage(
+ DERBitString usage)
+ {
+ super(usage.getBytes(), usage.getPadBits());
+ }
+
+ public String toString()
+ {
+ return "KeyUsage: 0x" + Integer.toHexString(data[0] & 0xff);
+ }
+}
+
+
+class ReasonFlags
+ extends DERBitString
+{
+ public static final int UNUSED = (1 << 7);
+ public static final int KEY_COMPROMISE = (1 << 6);
+ public static final int CA_COMPROMISE = (1 << 5);
+ public static final int AFFILIATION_CHANGED = (1 << 4);
+ public static final int SUPERSEDED = (1 << 3);
+ public static final int CESSATION_OF_OPERATION = (1 << 2);
+ public static final int CERTIFICATE_HOLD = (1 << 1);
+ public static final int PRIVILEGE_WITHDRAWN = (1 << 0);
+ public static final int AA_COMPROMISE = (1 << 15);
+
+ /**
+ * <pre>
+ * ReasonFlags ::= BIT STRING {
+ * unused(0),
+ * keyCompromise(1),
+ * cACompromise(2),
+ * affiliationChanged(3),
+ * superseded(4),
+ * cessationOfOperation(5),
+ * certficateHold(6)
+ * }
+ * </pre>
+ * @param reasons - the bitwise OR of the Key Reason flags giving the
+ * allowed uses for the key.
+ */
+ public ReasonFlags(
+ int reasons)
+ {
+ super(getBytes(reasons), getPadBits(reasons));
+ }
+
+ public ReasonFlags(
+ DERBitString reasons)
+ {
+ super(reasons.getBytes(), reasons.getPadBits());
+ }
+}
+
+
+
+class RSAPublicKeyStructure
+ implements DEREncodable
+{
+ private BigInteger modulus;
+ private BigInteger publicExponent;
+
+ public static RSAPublicKeyStructure getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static RSAPublicKeyStructure getInstance(
+ Object obj)
+ {
+ if(obj == null || obj instanceof RSAPublicKeyStructure)
+ {
+ return (RSAPublicKeyStructure)obj;
+ }
+
+ if(obj instanceof ASN1Sequence)
+ {
+ return new RSAPublicKeyStructure((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid RSAPublicKeyStructure: " + obj.getClass().getName());
+ }
+
+ public RSAPublicKeyStructure(
+ BigInteger modulus,
+ BigInteger publicExponent)
+ {
+ this.modulus = modulus;
+ this.publicExponent = publicExponent;
+ }
+
+ public RSAPublicKeyStructure(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ modulus = ((DERInteger)e.nextElement()).getValue();
+ publicExponent = ((DERInteger)e.nextElement()).getValue();
+ }
+
+ public BigInteger getModulus()
+ {
+ return modulus;
+ }
+
+ public BigInteger getPublicExponent()
+ {
+ return publicExponent;
+ }
+
+ /**
+ * This outputs the key in PKCS1v2 format.
+ * <pre>
+ * RSAPublicKey ::= SEQUENCE {
+ * modulus INTEGER, -- n
+ * publicExponent INTEGER, -- e
+ * }
+ * </pre>
+ * <p>
+ */
+ public DERObject getDERObject()
+ {
+ DERConstructedSequence seq = new DERConstructedSequence();
+
+ seq.addObject(new DERInteger(getModulus()));
+ seq.addObject(new DERInteger(getPublicExponent()));
+
+ return seq;
+ }
+}
+
+
+/**
+ * <pre>
+ * SubjectKeyIdentifier::= OCTET STRING
+ * </pre>
+ */
+class SubjectKeyIdentifier
+ implements DEREncodable
+{
+ private byte[] keyidentifier;
+
+ public static SubjectKeyIdentifier getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1OctetString.getInstance(obj, explicit));
+ }
+
+ public static SubjectKeyIdentifier getInstance(
+ Object obj)
+ {
+ if(obj == null || obj instanceof SubjectKeyIdentifier)
+ {
+ return (SubjectKeyIdentifier)obj;
+ }
+
+ if(obj instanceof SubjectPublicKeyInfo)
+ {
+ return new SubjectKeyIdentifier((SubjectPublicKeyInfo)obj);
+ }
+
+ if(obj instanceof ASN1OctetString)
+ {
+ return new SubjectKeyIdentifier((ASN1OctetString)obj);
+ }
+
+ throw new IllegalArgumentException("Invalid SubjectKeyIdentifier: " + obj.getClass().getName());
+ }
+
+ public SubjectKeyIdentifier(
+ byte[] keyid)
+ {
+ this.keyidentifier=keyid;
+ }
+
+ public SubjectKeyIdentifier(
+ ASN1OctetString keyid)
+ {
+ this.keyidentifier=keyid.getOctets();
+
+ }
+
+ /**
+ *
+ * Calulates the keyidentifier using a SHA1 hash over the BIT STRING
+ * from SubjectPublicKeyInfo as defined in RFC2459.
+ *
+ **/
+ public SubjectKeyIdentifier(
+ SubjectPublicKeyInfo spki)
+ {
+ Digest digest = new SHA1Digest();
+ byte[] resBuf = new byte[digest.getDigestSize()];
+
+ byte[] bytes = spki.getPublicKeyData().getBytes();
+ digest.update(bytes, 0, bytes.length);
+ digest.doFinal(resBuf, 0);
+ this.keyidentifier=resBuf;
+ }
+
+ public byte[] getKeyIdentifier()
+ {
+ return keyidentifier;
+ }
+
+ /**
+ * <pre>
+ * SubjectKeyIdentifier := OCTET STRING
+ * </pre>
+ */
+ public DERObject getDERObject()
+ {
+ return new DEROctetString(keyidentifier);
+ }
+}
+
+
+
+/**
+ * The object that contains the public key stored in a certficate.
+ * <p>
+ * The getEncoded() method in the public keys in the JCE produces a DER
+ * encoded one of these.
+ */
+class SubjectPublicKeyInfo
+ implements DEREncodable
+{
+ private AlgorithmIdentifier algId;
+ private DERBitString keyData;
+
+ public static SubjectPublicKeyInfo getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static SubjectPublicKeyInfo getInstance(
+ Object obj)
+ {
+ if (obj instanceof SubjectPublicKeyInfo)
+ {
+ return (SubjectPublicKeyInfo)obj;
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ return new SubjectPublicKeyInfo((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory");
+ }
+
+ public SubjectPublicKeyInfo(
+ AlgorithmIdentifier algId,
+ DEREncodable publicKey)
+ {
+ this.keyData = new DERBitString(publicKey);
+ this.algId = algId;
+ }
+
+ public SubjectPublicKeyInfo(
+ AlgorithmIdentifier algId,
+ byte[] publicKey)
+ {
+ this.keyData = new DERBitString(publicKey);
+ this.algId = algId;
+ }
+
+ public SubjectPublicKeyInfo(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ this.algId = AlgorithmIdentifier.getInstance(e.nextElement());
+ this.keyData = (DERBitString)e.nextElement();
+ }
+
+ public AlgorithmIdentifier getAlgorithmId()
+ {
+ return algId;
+ }
+
+ /**
+ * for when the public key is an encoded object - if the bitstring
+ * can't be decoded this routine throws an IOException.
+ *
+ * @exception IOException - if the bit string doesn't represent a DER
+ * encoded object.
+ */
+ public DERObject getPublicKey()
+ throws IOException
+ {
+ ByteArrayInputStream bIn = new ByteArrayInputStream(keyData.getBytes());
+ DERInputStream dIn = new DERInputStream(bIn);
+
+ return dIn.readObject();
+ }
+
+ /**
+ * for when the public key is raw bits...
+ */
+ public DERBitString getPublicKeyData()
+ {
+ return keyData;
+ }
+
+ /**
+ * <pre>
+ * SubjectPublicKeyInfo ::= SEQUENCE {
+ * algorithm AlgorithmIdentifier,
+ * publicKey BIT STRING }
+ * </pre>
+ */
+ public DERObject getDERObject()
+ {
+ DERConstructedSequence seq = new DERConstructedSequence();
+
+ seq.addObject(algId);
+ seq.addObject(keyData);
+
+ return seq;
+ }
+}
+
+
+/**
+ * <pre>
+ * TBSCertificate ::= SEQUENCE {
+ * version [ 0 ] Version DEFAULT v1(0),
+ * serialNumber CertificateSerialNumber,
+ * signature AlgorithmIdentifier,
+ * issuer Name,
+ * validity Validity,
+ * subject Name,
+ * subjectPublicKeyInfo SubjectPublicKeyInfo,
+ * issuerUniqueID [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ * subjectUniqueID [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ * extensions [ 3 ] Extensions OPTIONAL
+ * }
+ * </pre>
+ * <p>
+ * Note: issuerUniqueID and subjectUniqueID are both deprecated by the IETF. This class
+ * will parse them, but you really shouldn't be creating new ones.
+ */
+class TBSCertificateStructure
+ implements DEREncodable, X509ObjectIdentifiers, PKCSObjectIdentifiers
+{
+ ASN1Sequence seq;
+
+ DERInteger version;
+ DERInteger serialNumber;
+ AlgorithmIdentifier signature;
+ X509Name issuer;
+ Time startDate, endDate;
+ X509Name subject;
+ SubjectPublicKeyInfo subjectPublicKeyInfo;
+ DERBitString issuerUniqueId;
+ DERBitString subjectUniqueId;
+ X509Extensions extensions;
+
+ public static TBSCertificateStructure getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static TBSCertificateStructure getInstance(
+ Object obj)
+ {
+ if (obj instanceof TBSCertificateStructure)
+ {
+ return (TBSCertificateStructure)obj;
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ return new TBSCertificateStructure((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory");
+ }
+
+ public TBSCertificateStructure(
+ ASN1Sequence seq)
+ {
+ int seqStart = 0;
+
+ this.seq = seq;
+
+ //
+ // some certficates don't include a version number - we assume v1
+ //
+ if (seq.getObjectAt(0) instanceof DERTaggedObject)
+ {
+ version = DERInteger.getInstance(seq.getObjectAt(0));
+ }
+ else
+ {
+ seqStart = -1; // field 0 is missing!
+ version = new DERInteger(0);
+ }
+
+ serialNumber = DERInteger.getInstance(seq.getObjectAt(seqStart + 1));
+
+ signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(seqStart + 2));
+ issuer = X509Name.getInstance(seq.getObjectAt(seqStart + 3));
+
+ //
+ // before and after dates
+ //
+ ASN1Sequence dates = (ASN1Sequence)seq.getObjectAt(seqStart + 4);
+
+ startDate = Time.getInstance(dates.getObjectAt(0));
+ endDate = Time.getInstance(dates.getObjectAt(1));
+
+ subject = X509Name.getInstance(seq.getObjectAt(seqStart + 5));
+
+ //
+ // public key info.
+ //
+ subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(seq.getObjectAt(seqStart + 6));
+
+ for (int extras = seq.size() - (seqStart + 6) - 1; extras > 0; extras--)
+ {
+ DERTaggedObject extra = (DERTaggedObject)seq.getObjectAt(seqStart + 6 + extras);
+
+ switch (extra.getTagNo())
+ {
+ case 1:
+ issuerUniqueId = DERBitString.getInstance(extra);
+ break;
+ case 2:
+ subjectUniqueId = DERBitString.getInstance(extra);
+ break;
+ case 3:
+ extensions = X509Extensions.getInstance(extra);
+ }
+ }
+ }
+
+ public int getVersion()
+ {
+ return version.getValue().intValue() + 1;
+ }
+
+ public DERInteger getVersionNumber()
+ {
+ return version;
+ }
+
+ public DERInteger getSerialNumber()
+ {
+ return serialNumber;
+ }
+
+ public AlgorithmIdentifier getSignature()
+ {
+ return signature;
+ }
+
+ public X509Name getIssuer()
+ {
+ return issuer;
+ }
+
+ public Time getStartDate()
+ {
+ return startDate;
+ }
+
+ public Time getEndDate()
+ {
+ return endDate;
+ }
+
+ public X509Name getSubject()
+ {
+ return subject;
+ }
+
+ public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+ {
+ return subjectPublicKeyInfo;
+ }
+
+ public DERBitString getIssuerUniqueId()
+ {
+ return issuerUniqueId;
+ }
+
+ public DERBitString getSubjectUniqueId()
+ {
+ return subjectUniqueId;
+ }
+
+ public X509Extensions getExtensions()
+ {
+ return extensions;
+ }
+
+ public DERObject getDERObject()
+ {
+ return seq;
+ }
+}
+
+
+
+/**
+ * PKIX RFC-2459
+ *
+ * <pre>
+ * TBSCertList ::= SEQUENCE {
+ * version Version OPTIONAL,
+ * -- if present, shall be v2
+ * signature AlgorithmIdentifier,
+ * issuer Name,
+ * thisUpdate Time,
+ * nextUpdate Time OPTIONAL,
+ * revokedCertificates SEQUENCE OF SEQUENCE {
+ * userCertificate CertificateSerialNumber,
+ * revocationDate Time,
+ * crlEntryExtensions Extensions OPTIONAL
+ * -- if present, shall be v2
+ * } OPTIONAL,
+ * crlExtensions [0] EXPLICIT Extensions OPTIONAL
+ * -- if present, shall be v2
+ * }
+ * </pre>
+ */
+
+class TBSCertList
+ implements DEREncodable
+{
+ class CRLEntry
+ implements DEREncodable
+ {
+ DERConstructedSequence seq;
+
+ DERInteger userCertificate;
+ Time revocationDate;
+ X509Extensions crlEntryExtensions;
+
+ public CRLEntry(
+ DERConstructedSequence seq)
+ {
+ this.seq = seq;
+
+ userCertificate = (DERInteger)seq.getObjectAt(0);
+ revocationDate = Time.getInstance(seq.getObjectAt(1));
+ if (seq.getSize() == 3)
+ {
+ crlEntryExtensions = X509Extensions.getInstance(seq.getObjectAt(2));
+ }
+ }
+
+ public DERInteger getUserCertificate()
+ {
+ return userCertificate;
+ }
+
+ public Time getRevocationDate()
+ {
+ return revocationDate;
+ }
+
+ public X509Extensions getExtensions()
+ {
+ return crlEntryExtensions;
+ }
+
+ public DERObject getDERObject()
+ {
+ return seq;
+ }
+ }
+
+ ASN1Sequence seq;
+
+ DERInteger version;
+ AlgorithmIdentifier signature;
+ X509Name issuer;
+ Time thisUpdate;
+ Time nextUpdate;
+ CRLEntry[] revokedCertificates;
+ X509Extensions crlExtensions;
+
+ public static TBSCertList getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static TBSCertList getInstance(
+ Object obj)
+ {
+ if (obj instanceof TBSCertList)
+ {
+ return (TBSCertList)obj;
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ return new TBSCertList((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory");
+ }
+
+ public TBSCertList(
+ ASN1Sequence seq)
+ {
+ int seqPos = 0;
+
+ this.seq = seq;
+
+ if (seq.getObjectAt(seqPos) instanceof DERInteger)
+ {
+ version = (DERInteger)seq.getObjectAt(seqPos++);
+ }
+ else
+ {
+ version = new DERInteger(0);
+ }
+
+ signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(seqPos++));
+ issuer = X509Name.getInstance(seq.getObjectAt(seqPos++));
+ thisUpdate = Time.getInstance(seq.getObjectAt(seqPos++));
+
+ if (seqPos < seq.size()
+ && (seq.getObjectAt(seqPos) instanceof DERUTCTime
+ || seq.getObjectAt(seqPos) instanceof DERGeneralizedTime
+ || seq.getObjectAt(seqPos) instanceof Time))
+ {
+ nextUpdate = Time.getInstance(seq.getObjectAt(seqPos++));
+ }
+
+ if (seqPos < seq.size()
+ && !(seq.getObjectAt(seqPos) instanceof DERTaggedObject))
+ {
+ DERConstructedSequence certs = (DERConstructedSequence)seq.getObjectAt(seqPos++);
+ revokedCertificates = new CRLEntry[certs.getSize()];
+
+ for ( int i = 0; i < revokedCertificates.length; i++)
+ {
+ revokedCertificates[i] = new CRLEntry((DERConstructedSequence)certs.getObjectAt(i));
+ }
+ }
+
+ if (seqPos < seq.size()
+ && seq.getObjectAt(seqPos) instanceof DERTaggedObject)
+ {
+ crlExtensions = X509Extensions.getInstance(seq.getObjectAt(seqPos++));
+ }
+ }
+
+ public int getVersion()
+ {
+ return version.getValue().intValue() + 1;
+ }
+
+ public DERInteger getVersionNumber()
+ {
+ return version;
+ }
+
+ public AlgorithmIdentifier getSignature()
+ {
+ return signature;
+ }
+
+ public X509Name getIssuer()
+ {
+ return issuer;
+ }
+
+ public Time getThisUpdate()
+ {
+ return thisUpdate;
+ }
+
+ public Time getNextUpdate()
+ {
+ return nextUpdate;
+ }
+
+ public CRLEntry[] getRevokedCertificates()
+ {
+ return revokedCertificates;
+ }
+
+ public X509Extensions getExtensions()
+ {
+ return crlExtensions;
+ }
+
+ public DERObject getDERObject()
+ {
+ return seq;
+ }
+}
+
+
+
+class Time
+ implements DEREncodable
+{
+ DERObject time;
+
+ public static Time getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ public Time(
+ DERObject time)
+ {
+ if (!(time instanceof DERUTCTime)
+ && !(time instanceof DERGeneralizedTime))
+ {
+ throw new IllegalArgumentException("unknown object passed to Time");
+ }
+
+ this.time = time;
+ }
+
+ /**
+ * creates a time object from a given date - if the date is between 1950
+ * and 2049 a UTCTime object is generated, otherwise a GeneralizedTime
+ * is used.
+ */
+ public Time(
+ Date date)
+ {
+ SimpleTimeZone tz = new SimpleTimeZone(0, "Z");
+ SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss");
+
+ dateF.setTimeZone(tz);
+
+ String d = dateF.format(date) + "Z";
+ int year = Integer.parseInt(d.substring(0, 4));
+
+ if (year < 1950 || year > 2049)
+ {
+ time = new DERGeneralizedTime(d);
+ }
+ else
+ {
+ time = new DERUTCTime(d.substring(2));
+ }
+ }
+
+ public static Time getInstance(
+ Object obj)
+ {
+ if (obj instanceof Time)
+ {
+ return (Time)obj;
+ }
+ else if (obj instanceof DERUTCTime)
+ {
+ return new Time((DERUTCTime)obj);
+ }
+ else if (obj instanceof DERGeneralizedTime)
+ {
+ return new Time((DERGeneralizedTime)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory");
+ }
+
+ public String getTime()
+ {
+ if (time instanceof DERUTCTime)
+ {
+ return ((DERUTCTime)time).getAdjustedTime();
+ }
+ else
+ {
+ return ((DERGeneralizedTime)time).getTime();
+ }
+ }
+
+ public Date getDate()
+ {
+ SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
+
+ return dateF.parse(this.getTime(), new ParsePosition(0));
+ }
+
+ /**
+ * <pre>
+ * Time ::= CHOICE {
+ * utcTime UTCTime,
+ * generalTime GeneralizedTime }
+ * </pre>
+ */
+ public DERObject getDERObject()
+ {
+ return time;
+ }
+}
+
+
+/**
+ * Generator for Version 1 TBSCertificateStructures.
+ * <pre>
+ * TBSCertificate ::= SEQUENCE {
+ * version [ 0 ] Version DEFAULT v1(0),
+ * serialNumber CertificateSerialNumber,
+ * signature AlgorithmIdentifier,
+ * issuer Name,
+ * validity Validity,
+ * subject Name,
+ * subjectPublicKeyInfo SubjectPublicKeyInfo,
+ * }
+ * </pre>
+ *
+ */
+class V1TBSCertificateGenerator
+{
+ DERTaggedObject version = new DERTaggedObject(0, new DERInteger(0));
+
+ DERInteger serialNumber;
+ AlgorithmIdentifier signature;
+ X509Name issuer;
+ Time startDate, endDate;
+ X509Name subject;
+ SubjectPublicKeyInfo subjectPublicKeyInfo;
+
+ public V1TBSCertificateGenerator()
+ {
+ }
+
+ public void setSerialNumber(
+ DERInteger serialNumber)
+ {
+ this.serialNumber = serialNumber;
+ }
+
+ public void setSignature(
+ AlgorithmIdentifier signature)
+ {
+ this.signature = signature;
+ }
+
+ public void setIssuer(
+ X509Name issuer)
+ {
+ this.issuer = issuer;
+ }
+
+ public void setStartDate(
+ Time startDate)
+ {
+ this.startDate = startDate;
+ }
+
+ public void setStartDate(
+ DERUTCTime startDate)
+ {
+ this.startDate = new Time(startDate);
+ }
+
+ public void setEndDate(
+ Time endDate)
+ {
+ this.endDate = endDate;
+ }
+
+ public void setEndDate(
+ DERUTCTime endDate)
+ {
+ this.endDate = new Time(endDate);
+ }
+
+ public void setSubject(
+ X509Name subject)
+ {
+ this.subject = subject;
+ }
+
+ public void setSubjectPublicKeyInfo(
+ SubjectPublicKeyInfo pubKeyInfo)
+ {
+ this.subjectPublicKeyInfo = pubKeyInfo;
+ }
+
+ public TBSCertificateStructure generateTBSCertificate()
+ {
+ if ((serialNumber == null) || (signature == null)
+ || (issuer == null) || (startDate == null) || (endDate == null)
+ || (subject == null) || (subjectPublicKeyInfo == null))
+ {
+ throw new IllegalStateException("not all mandatory fields set in V1 TBScertificate generator");
+ }
+
+ DERConstructedSequence seq = new DERConstructedSequence();
+
+ seq.addObject(version);
+ seq.addObject(serialNumber);
+ seq.addObject(signature);
+ seq.addObject(issuer);
+
+ //
+ // before and after dates
+ //
+ DERConstructedSequence validity = new DERConstructedSequence();
+
+ validity.addObject(startDate);
+ validity.addObject(endDate);
+
+ seq.addObject(validity);
+
+ seq.addObject(subject);
+
+ seq.addObject(subjectPublicKeyInfo);
+
+ return new TBSCertificateStructure(seq);
+ }
+}
+
+
+
+/**
+ * Generator for Version 2 TBSCertList structures.
+ * <pre>
+ * TBSCertList ::= SEQUENCE {
+ * version Version OPTIONAL,
+ * -- if present, shall be v2
+ * signature AlgorithmIdentifier,
+ * issuer Name,
+ * thisUpdate Time,
+ * nextUpdate Time OPTIONAL,
+ * revokedCertificates SEQUENCE OF SEQUENCE {
+ * userCertificate CertificateSerialNumber,
+ * revocationDate Time,
+ * crlEntryExtensions Extensions OPTIONAL
+ * -- if present, shall be v2
+ * } OPTIONAL,
+ * crlExtensions [0] EXPLICIT Extensions OPTIONAL
+ * -- if present, shall be v2
+ * }
+ * </pre>
+ *
+ * <b>Note: This class may be subject to change</b>
+ */
+class V2TBSCertListGenerator
+{
+ DERInteger version = new DERInteger(1);
+
+ AlgorithmIdentifier signature;
+ X509Name issuer;
+ Time thisUpdate, nextUpdate=null;
+ X509Extensions extensions=null;
+ private Vector crlentries=null;
+
+ public V2TBSCertListGenerator()
+ {
+ }
+
+
+ public void setSignature(
+ AlgorithmIdentifier signature)
+ {
+ this.signature = signature;
+ }
+
+ public void setIssuer(
+ X509Name issuer)
+ {
+ this.issuer = issuer;
+ }
+
+ public void setThisUpdate(
+ DERUTCTime thisUpdate)
+ {
+ this.thisUpdate = new Time(thisUpdate);
+ }
+
+ public void setNextUpdate(
+ DERUTCTime nextUpdate)
+ {
+ this.nextUpdate = new Time(nextUpdate);
+ }
+
+ public void setThisUpdate(
+ Time thisUpdate)
+ {
+ this.thisUpdate = thisUpdate;
+ }
+
+ public void setNextUpdate(
+ Time nextUpdate)
+ {
+ this.nextUpdate = nextUpdate;
+ }
+
+ public void addCRLEntry(
+ DERConstructedSequence crlEntry)
+ {
+ if (crlentries == null)
+ crlentries = new Vector();
+ crlentries.addElement(crlEntry);
+ }
+
+ public void addCRLEntry(DERInteger userCertificate, DERUTCTime revocationDate, int reason)
+ {
+ addCRLEntry(userCertificate, new Time(revocationDate), reason);
+ }
+
+ public void addCRLEntry(DERInteger userCertificate, Time revocationDate, int reason)
+ {
+ DERConstructedSequence seq = new DERConstructedSequence();
+ seq.addObject(userCertificate);
+ seq.addObject(revocationDate);
+
+ if (reason != 0)
+ {
+ CRLReason rf = new CRLReason(reason);
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+ try
+ {
+ dOut.writeObject(rf);
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("error encoding value: " + e);
+ }
+ byte[] value = bOut.toByteArray();
+ DERConstructedSequence eseq = new DERConstructedSequence();
+ DERConstructedSequence eseq1 = new DERConstructedSequence();
+ eseq1.addObject(X509Extensions.ReasonCode);
+ eseq1.addObject(new DEROctetString(value));
+ eseq.addObject(eseq1);
+ X509Extensions ex = new X509Extensions(eseq);
+ seq.addObject(ex);
+ }
+ if (crlentries == null)
+ crlentries = new Vector();
+ crlentries.addElement(seq);
+ }
+
+ public void setExtensions(
+ X509Extensions extensions)
+ {
+ this.extensions = extensions;
+ }
+
+ public TBSCertList generateTBSCertList()
+ {
+ if ((signature == null) || (issuer == null) || (thisUpdate == null))
+ {
+ throw new IllegalStateException("Not all mandatory fields set in V2 TBSCertList generator.");
+ }
+
+ DERConstructedSequence seq = new DERConstructedSequence();
+
+ seq.addObject(version);
+ seq.addObject(signature);
+ seq.addObject(issuer);
+
+ seq.addObject(thisUpdate);
+ if (nextUpdate != null)
+ seq.addObject(nextUpdate);
+
+ // Add CRLEntries if they exist
+ if (crlentries != null) {
+ DERConstructedSequence certseq = new DERConstructedSequence();
+ Enumeration it = crlentries.elements();
+ while( it.hasMoreElements() ) {
+ certseq.addObject((DERConstructedSequence)it.nextElement());
+ }
+ seq.addObject(certseq);
+ }
+
+ if (extensions != null)
+ {
+ seq.addObject(new DERTaggedObject(0, extensions));
+ }
+
+ return new TBSCertList(seq);
+ }
+}
+
+
+/**
+ * Generator for Version 3 TBSCertificateStructures.
+ * <pre>
+ * TBSCertificate ::= SEQUENCE {
+ * version [ 0 ] Version DEFAULT v1(0),
+ * serialNumber CertificateSerialNumber,
+ * signature AlgorithmIdentifier,
+ * issuer Name,
+ * validity Validity,
+ * subject Name,
+ * subjectPublicKeyInfo SubjectPublicKeyInfo,
+ * issuerUniqueID [ 1 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ * subjectUniqueID [ 2 ] IMPLICIT UniqueIdentifier OPTIONAL,
+ * extensions [ 3 ] Extensions OPTIONAL
+ * }
+ * </pre>
+ *
+ */
+class V3TBSCertificateGenerator
+{
+ DERTaggedObject version = new DERTaggedObject(0, new DERInteger(2));
+
+ DERInteger serialNumber;
+ AlgorithmIdentifier signature;
+ X509Name issuer;
+ Time startDate, endDate;
+ X509Name subject;
+ SubjectPublicKeyInfo subjectPublicKeyInfo;
+ X509Extensions extensions;
+
+ public V3TBSCertificateGenerator()
+ {
+ }
+
+ public void setSerialNumber(
+ DERInteger serialNumber)
+ {
+ this.serialNumber = serialNumber;
+ }
+
+ public void setSignature(
+ AlgorithmIdentifier signature)
+ {
+ this.signature = signature;
+ }
+
+ public void setIssuer(
+ X509Name issuer)
+ {
+ this.issuer = issuer;
+ }
+
+ public void setStartDate(
+ DERUTCTime startDate)
+ {
+ this.startDate = new Time(startDate);
+ }
+
+ public void setStartDate(
+ Time startDate)
+ {
+ this.startDate = startDate;
+ }
+
+ public void setEndDate(
+ DERUTCTime endDate)
+ {
+ this.endDate = new Time(endDate);
+ }
+
+ public void setEndDate(
+ Time endDate)
+ {
+ this.endDate = endDate;
+ }
+
+ public void setSubject(
+ X509Name subject)
+ {
+ this.subject = subject;
+ }
+
+ public void setSubjectPublicKeyInfo(
+ SubjectPublicKeyInfo pubKeyInfo)
+ {
+ this.subjectPublicKeyInfo = pubKeyInfo;
+ }
+
+ public void setExtensions(
+ X509Extensions extensions)
+ {
+ this.extensions = extensions;
+ }
+
+ public TBSCertificateStructure generateTBSCertificate()
+ {
+ if ((serialNumber == null) || (signature == null)
+ || (issuer == null) || (startDate == null) || (endDate == null)
+ || (subject == null) || (subjectPublicKeyInfo == null))
+ {
+ throw new IllegalStateException("not all mandatory fields set in V3 TBScertificate generator");
+ }
+
+ DERConstructedSequence seq = new DERConstructedSequence();
+
+ seq.addObject(version);
+ seq.addObject(serialNumber);
+ seq.addObject(signature);
+ seq.addObject(issuer);
+
+ //
+ // before and after dates
+ //
+ DERConstructedSequence validity = new DERConstructedSequence();
+
+ validity.addObject(startDate);
+ validity.addObject(endDate);
+
+ seq.addObject(validity);
+
+ seq.addObject(subject);
+
+ seq.addObject(subjectPublicKeyInfo);
+
+ if (extensions != null)
+ {
+ seq.addObject(new DERTaggedObject(3, extensions));
+ }
+
+ return new TBSCertificateStructure(seq);
+ }
+}
+
+
+/**
+ * an X509Certificate structure.
+ * <pre>
+ * Certificate ::= SEQUENCE {
+ * tbsCertificate TBSCertificate,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signature BIT STRING
+ * }
+ * </pre>
+ */
+class X509CertificateStructure
+ implements DEREncodable, X509ObjectIdentifiers, PKCSObjectIdentifiers
+{
+ ASN1Sequence seq;
+ TBSCertificateStructure tbsCert;
+ AlgorithmIdentifier sigAlgId;
+ DERBitString sig;
+
+ public static X509CertificateStructure getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static X509CertificateStructure getInstance(
+ Object obj)
+ {
+ if (obj instanceof X509CertificateStructure)
+ {
+ return (X509CertificateStructure)obj;
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ return new X509CertificateStructure((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory");
+ }
+
+ public X509CertificateStructure(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+
+ //
+ // correct x509 certficate
+ //
+ if (seq.size() == 3)
+ {
+ tbsCert = TBSCertificateStructure.getInstance(seq.getObjectAt(0));
+ sigAlgId = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
+
+ sig = (DERBitString)seq.getObjectAt(2);
+ }
+ }
+
+ public TBSCertificateStructure getTBSCertificate()
+ {
+ return tbsCert;
+ }
+
+ public int getVersion()
+ {
+ return tbsCert.getVersion();
+ }
+
+ public DERInteger getSerialNumber()
+ {
+ return tbsCert.getSerialNumber();
+ }
+
+ public X509Name getIssuer()
+ {
+ return tbsCert.getIssuer();
+ }
+
+ public Time getStartDate()
+ {
+ return tbsCert.getStartDate();
+ }
+
+ public Time getEndDate()
+ {
+ return tbsCert.getEndDate();
+ }
+
+ public X509Name getSubject()
+ {
+ return tbsCert.getSubject();
+ }
+
+ public SubjectPublicKeyInfo getSubjectPublicKeyInfo()
+ {
+ return tbsCert.getSubjectPublicKeyInfo();
+ }
+
+ public AlgorithmIdentifier getSignatureAlgorithm()
+ {
+ return sigAlgId;
+ }
+
+ public DERBitString getSignature()
+ {
+ return sig;
+ }
+
+ public DERObject getDERObject()
+ {
+ return seq;
+ }
+}
+
+
+/**
+ * an object for the elements in the X.509 V3 extension block.
+ */
+class X509Extension
+{
+ boolean critical;
+ DEROctetString value;
+
+ public X509Extension(
+ DERBoolean critical,
+ DEROctetString value)
+ {
+ this.critical = critical.isTrue();
+ this.value = value;
+ }
+
+ public X509Extension(
+ boolean critical,
+ DEROctetString value)
+ {
+ this.critical = critical;
+ this.value = value;
+ }
+
+ public boolean isCritical()
+ {
+ return critical;
+ }
+
+ public DEROctetString getValue()
+ {
+ return value;
+ }
+
+ public int hashCode()
+ {
+ if (this.isCritical())
+ {
+ return this.getValue().hashCode();
+ }
+
+
+ return ~this.getValue().hashCode();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == null || !(o instanceof X509Extension))
+ {
+ return false;
+ }
+
+ X509Extension other = (X509Extension)o;
+
+ return other.getValue().equals(this.getValue())
+ && (other.isCritical() == this.isCritical());
+ }
+}
+
+
+
+class X509Extensions
+ implements DEREncodable
+{
+ /**
+ * Subject Key Identifier
+ */
+ public static final DERObjectIdentifier SubjectKeyIdentifier = new DERObjectIdentifier("2.5.29.14");
+
+ /**
+ * Key Usage
+ */
+ public static final DERObjectIdentifier KeyUsage = new DERObjectIdentifier("2.5.29.15");
+
+ /**
+ * Private Key Usage Period
+ */
+ public static final DERObjectIdentifier PrivateKeyUsagePeriod = new DERObjectIdentifier("2.5.29.16");
+
+ /**
+ * Subject Alternative Name
+ */
+ public static final DERObjectIdentifier SubjectAlternativeName = new DERObjectIdentifier("2.5.29.17");
+
+ /**
+ * Issuer Alternative Name
+ */
+ public static final DERObjectIdentifier IssuerAlternativeName = new DERObjectIdentifier("2.5.29.18");
+
+ /**
+ * Basic Constraints
+ */
+ public static final DERObjectIdentifier BasicConstraints = new DERObjectIdentifier("2.5.29.19");
+
+ /**
+ * CRL Number
+ */
+ public static final DERObjectIdentifier CRLNumber = new DERObjectIdentifier("2.5.29.20");
+
+ /**
+ * Reason code
+ */
+ public static final DERObjectIdentifier ReasonCode = new DERObjectIdentifier("2.5.29.21");
+
+ /**
+ * Hold Instruction Code
+ */
+ public static final DERObjectIdentifier InstructionCode = new DERObjectIdentifier("2.5.29.23");
+
+ /**
+ * Invalidity Date
+ */
+ public static final DERObjectIdentifier InvalidityDate = new DERObjectIdentifier("2.5.29.24");
+
+ /**
+ * Delta CRL indicator
+ */
+ public static final DERObjectIdentifier DeltaCRLIndicator = new DERObjectIdentifier("2.5.29.27");
+
+ /**
+ * Issuing Distribution Point
+ */
+ public static final DERObjectIdentifier IssuingDistributionPoint = new DERObjectIdentifier("2.5.29.28");
+
+ /**
+ * Certificate Issuer
+ */
+ public static final DERObjectIdentifier CertificateIssuer = new DERObjectIdentifier("2.5.29.29");
+
+ /**
+ * Name Constraints
+ */
+ public static final DERObjectIdentifier NameConstraints = new DERObjectIdentifier("2.5.29.30");
+
+ /**
+ * CRL Distribution Points
+ */
+ public static final DERObjectIdentifier CRLDistributionPoints = new DERObjectIdentifier("2.5.29.31");
+
+ /**
+ * Certificate Policies
+ */
+ public static final DERObjectIdentifier CertificatePolicies = new DERObjectIdentifier("2.5.29.32");
+
+ /**
+ * Policy Mappings
+ */
+ public static final DERObjectIdentifier PolicyMappings = new DERObjectIdentifier("2.5.29.33");
+
+ /**
+ * Authority Key Identifier
+ */
+ public static final DERObjectIdentifier AuthorityKeyIdentifier = new DERObjectIdentifier("2.5.29.35");
+
+ /**
+ * Policy Constraints
+ */
+ public static final DERObjectIdentifier PolicyConstraints = new DERObjectIdentifier("2.5.29.36");
+
+ /**
+ * Extended Key Usage
+ */
+ public static final DERObjectIdentifier ExtendedKeyUsage = new DERObjectIdentifier("2.5.29.37");
+
+ private Hashtable extensions = new Hashtable();
+ private Vector ordering = new Vector();
+
+ public static X509Extensions getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static X509Extensions getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof X509Extensions)
+ {
+ return (X509Extensions)obj;
+ }
+
+ if (obj instanceof ASN1Sequence)
+ {
+ return new X509Extensions((ASN1Sequence)obj);
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * Constructor from DERConstructedSequence.
+ *
+ * the extensions are a list of constructed sequences, either with (OID, OctetString) or (OID, Boolean, OctetString)
+ */
+ public X509Extensions(
+ ASN1Sequence seq)
+ {
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1Sequence s = (ASN1Sequence)e.nextElement();
+ Enumeration e1 = s.getObjects();
+
+ if (s.size() == 3)
+ {
+ extensions.put(s.getObjectAt(0), new X509Extension((DERBoolean)s.getObjectAt(1), (DEROctetString)s.getObjectAt(2)));
+ }
+ else
+ {
+ extensions.put(s.getObjectAt(0), new X509Extension(false, (DEROctetString)s.getObjectAt(1)));
+ }
+
+ ordering.addElement(s.getObjectAt(0));
+ }
+ }
+
+ /**
+ * constructor from a table of extensions.
+ * <p>
+ * it's is assumed the table contains OID/String pairs.
+ */
+ public X509Extensions(
+ Hashtable extensions)
+ {
+ this(null, extensions);
+ }
+
+ /**
+ * constructor from a table of extensions with ordering
+ * <p>
+ * it's is assumed the table contains OID/String pairs.
+ */
+ public X509Extensions(
+ Vector ordering,
+ Hashtable extensions)
+ {
+ Enumeration e;
+
+ if (ordering == null)
+ {
+ e = extensions.keys();
+ }
+ else
+ {
+ e = ordering.elements();
+ }
+
+ while (e.hasMoreElements())
+ {
+ this.ordering.addElement(e.nextElement());
+ }
+
+ e = this.ordering.elements();
+
+ while (e.hasMoreElements())
+ {
+ DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
+ X509Extension ext = (X509Extension)extensions.get(oid);
+
+ this.extensions.put(oid, ext);
+ }
+ }
+
+ /**
+ * return an Enumeration of the extension field's object ids.
+ */
+ public Enumeration oids()
+ {
+ return ordering.elements();
+ }
+
+ /**
+ * return the extension represented by the object identifier
+ * passed in.
+ *
+ * @return the extension if it's present, null otherwise.
+ */
+ public X509Extension getExtension(
+ DERObjectIdentifier oid)
+ {
+ return (X509Extension)extensions.get(oid);
+ }
+
+ public DERObject getDERObject()
+ {
+ DEREncodableVector vec = new DEREncodableVector();
+ Enumeration e = ordering.elements();
+
+ while (e.hasMoreElements())
+ {
+ DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
+ X509Extension ext = (X509Extension)extensions.get(oid);
+ DEREncodableVector v = new DEREncodableVector();
+
+ v.add(oid);
+
+ if (ext.isCritical())
+ {
+ v.add(new DERBoolean(true));
+ }
+
+ v.add(ext.getValue());
+
+ vec.add(new DERSequence(v));
+ }
+
+ return new DERSequence(vec);
+ }
+
+ public int hashCode()
+ {
+ Enumeration e = extensions.keys();
+ int hashCode = 0;
+
+ while (e.hasMoreElements())
+ {
+ Object o = e.nextElement();
+
+ hashCode ^= o.hashCode();
+ hashCode ^= extensions.get(o).hashCode();
+ }
+
+ return hashCode;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == null || !(o instanceof X509Extensions))
+ {
+ return false;
+ }
+
+ X509Extensions other = (X509Extensions)o;
+
+ Enumeration e1 = extensions.keys();
+ Enumeration e2 = other.extensions.keys();
+
+ while (e1.hasMoreElements() && e2.hasMoreElements())
+ {
+ Object o1 = e1.nextElement();
+ Object o2 = e2.nextElement();
+
+ if (!o1.equals(o2))
+ {
+ return false;
+ }
+ }
+
+ if (e1.hasMoreElements() || e2.hasMoreElements())
+ {
+ return false;
+ }
+
+ return true;
+ }
+}
+
+
+
+class X509Name
+ implements DEREncodable
+{
+ /**
+ * country code - StringType(SIZE(2))
+ */
+ public static final DERObjectIdentifier C = new DERObjectIdentifier("2.5.4.6");
+
+ /**
+ * organization - StringType(SIZE(1..64))
+ */
+ public static final DERObjectIdentifier O = new DERObjectIdentifier("2.5.4.10");
+
+ /**
+ * organizational unit name - StringType(SIZE(1..64))
+ */
+ public static final DERObjectIdentifier OU = new DERObjectIdentifier("2.5.4.11");
+
+ /**
+ * Title
+ */
+ public static final DERObjectIdentifier T = new DERObjectIdentifier("2.5.4.12");
+
+ /**
+ * common name - StringType(SIZE(1..64))
+ */
+ public static final DERObjectIdentifier CN = new DERObjectIdentifier("2.5.4.3");
+
+ /**
+ * device serial number name - StringType(SIZE(1..64))
+ */
+ public static final DERObjectIdentifier SN = new DERObjectIdentifier("2.5.4.5");
+
+ /**
+ * locality name - StringType(SIZE(1..64))
+ */
+ public static final DERObjectIdentifier L = new DERObjectIdentifier("2.5.4.7");
+
+ /**
+ * state, or province name - StringType(SIZE(1..64))
+ */
+ public static final DERObjectIdentifier ST = new DERObjectIdentifier("2.5.4.8");
+
+
+ /**
+ * email address (RSA PKCS#9 extension) - IA5String
+ * <p>
+ * note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here.
+ */
+ public static final DERObjectIdentifier EmailAddress = new DERObjectIdentifier("1.2.840.113549.1.9.1");
+
+ /**
+ * email address in Verisign certificates
+ */
+ public static final DERObjectIdentifier E = EmailAddress;
+
+ /*
+ * others...
+ */
+ public static final DERObjectIdentifier DC = new DERObjectIdentifier("0.9.2342.19200300.100.1.25");
+
+ /**
+ * LDAP User id.
+ */
+ public static final DERObjectIdentifier UID = new DERObjectIdentifier("0.9.2342.19200300.100.1.1");
+
+ /**
+ * look up table translating OID values into their common symbols.
+ */
+ public static Hashtable OIDLookUp = new Hashtable();
+
+ /**
+ * look up table translating common symbols into their OIDS.
+ */
+ public static Hashtable SymbolLookUp = new Hashtable();
+
+ static
+ {
+ OIDLookUp.put(C, "C");
+ OIDLookUp.put(O, "O");
+ OIDLookUp.put(T, "T");
+ OIDLookUp.put(OU, "OU");
+ OIDLookUp.put(CN, "CN");
+ OIDLookUp.put(L, "L");
+ OIDLookUp.put(ST, "ST");
+ OIDLookUp.put(SN, "SN");
+ OIDLookUp.put(EmailAddress, "E");
+ OIDLookUp.put(DC, "DC");
+ OIDLookUp.put(UID, "UID");
+
+ SymbolLookUp.put("c", C);
+ SymbolLookUp.put("o", O);
+ SymbolLookUp.put("t", T);
+ SymbolLookUp.put("ou", OU);
+ SymbolLookUp.put("cn", CN);
+ SymbolLookUp.put("l", L);
+ SymbolLookUp.put("st", ST);
+ SymbolLookUp.put("sn", SN);
+ SymbolLookUp.put("emailaddress", E);
+ SymbolLookUp.put("dc", DC);
+ SymbolLookUp.put("e", E);
+ SymbolLookUp.put("uid", UID);
+ }
+
+ private Vector ordering = new Vector();
+ private Vector values = new Vector();
+ private ASN1Sequence seq;
+
+ public static X509Name getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(ASN1Sequence.getInstance(obj, explicit));
+ }
+
+ public static X509Name getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof X509Name)
+ {
+ return (X509Name)obj;
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ return new X509Name((ASN1Sequence)obj);
+ }
+
+ throw new IllegalArgumentException("unknown object in factory");
+ }
+
+ /**
+ * Constructor from ASN1Sequence
+ *
+ * the principal will be a list of constructed sets, each containing an (OID, String) pair.
+ */
+ public X509Name(
+ ASN1Sequence seq)
+ {
+ this.seq = seq;
+
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ ASN1Set set = (ASN1Set)e.nextElement();
+ ASN1Sequence s = (ASN1Sequence)set.getObjectAt(0);
+
+ ordering.addElement(s.getObjectAt(0));
+ values.addElement(((DERString)s.getObjectAt(1)).getString());
+ }
+ }
+
+ /**
+ * constructor from a table of attributes.
+ * <p>
+ * it's is assumed the table contains OID/String pairs, and the contents
+ * of the table are copied into an internal table as part of the
+ * construction process.
+ * <p>
+ * <b>Note:</b> if the name you are trying to generate should be
+ * following a specific ordering, you should use the constructor
+ * with the ordering specified below.
+ */
+ public X509Name(
+ Hashtable attributes)
+ {
+ this(null, attributes);
+ }
+
+ /**
+ * constructor from a table of attributes with ordering.
+ * <p>
+ * it's is assumed the table contains OID/String pairs, and the contents
+ * of the table are copied into an internal table as part of the
+ * construction process. The ordering vector should contain the OIDs
+ * in the order they are meant to be encoded or printed in toString.
+ */
+ public X509Name(
+ Vector ordering,
+ Hashtable attributes)
+ {
+ if (ordering != null)
+ {
+ for (int i = 0; i != ordering.size(); i++)
+ {
+ this.ordering.addElement(ordering.elementAt(i));
+ }
+ }
+ else
+ {
+ Enumeration e = attributes.keys();
+
+ while (e.hasMoreElements())
+ {
+ this.ordering.addElement(e.nextElement());
+ }
+ }
+
+ for (int i = 0; i != this.ordering.size(); i++)
+ {
+ DERObjectIdentifier oid = (DERObjectIdentifier)this.ordering.elementAt(i);
+
+ if (OIDLookUp.get(oid) == null)
+ {
+ throw new IllegalArgumentException("Unknown object id - " + oid.getId() + " - passed to distinguished name");
+ }
+
+ if (attributes.get(oid) == null)
+ {
+ throw new IllegalArgumentException("No attribute for object id - " + oid.getId() + " - passed to distinguished name");
+ }
+
+ this.values.addElement(attributes.get(oid)); // copy the hash table
+ }
+ }
+
+ /**
+ * takes two vectors one of the oids and the other of the values.
+ */
+ public X509Name(
+ Vector ordering,
+ Vector values)
+ {
+ if (ordering.size() != values.size())
+ {
+ throw new IllegalArgumentException("ordering vector must be same length as values.");
+ }
+
+ for (int i = 0; i < ordering.size(); i++)
+ {
+ this.ordering.addElement(ordering.elementAt(i));
+ this.values.addElement(values.elementAt(i));
+ }
+ }
+
+ /**
+ * takes an X509 dir name as a string of the format "C=AU, ST=Victoria", or
+ * some such, converting it into an ordered set of name attributes.
+ */
+ public X509Name(
+ String dirName)
+ {
+ X509NameTokenizer nTok = new X509NameTokenizer(dirName);
+
+ while (nTok.hasMoreTokens())
+ {
+ String token = nTok.nextToken();
+ int index = token.indexOf('=');
+
+ if (index == -1)
+ {
+ throw new IllegalArgumentException("badly formated directory string");
+ }
+
+ String name = token.substring(0, index);
+ String value = token.substring(index + 1);
+ DERObjectIdentifier oid = null;
+
+ if (name.toUpperCase().startsWith("OID."))
+ {
+ oid = new DERObjectIdentifier(name.substring(4));
+ }
+ else if (name.charAt(0) >= '0' && name.charAt(0) <= '9')
+ {
+ oid = new DERObjectIdentifier(name);
+ }
+ else
+ {
+ oid = (DERObjectIdentifier)SymbolLookUp.get(name.toLowerCase());
+ if (oid == null)
+ {
+ throw new IllegalArgumentException("Unknown object id - " + name + " - passed to distinguished name");
+ }
+ }
+
+ this.ordering.addElement(oid);
+ this.values.addElement(value);
+ }
+ }
+
+ /**
+ * return false if we have characters out of the range of a printable
+ * string, true otherwise.
+ */
+ private boolean canBePrintable(
+ String str)
+ {
+ for (int i = str.length() - 1; i >= 0; i--)
+ {
+ if (str.charAt(i) > 0x007f)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public DERObject getDERObject()
+ {
+ if (seq == null)
+ {
+ DEREncodableVector vec = new DEREncodableVector();
+
+ for (int i = 0; i != ordering.size(); i++)
+ {
+ DEREncodableVector v = new DEREncodableVector();
+ DERObjectIdentifier oid = (DERObjectIdentifier)ordering.elementAt(i);
+
+ v.add(oid);
+
+ String str = (String)values.elementAt(i);
+
+ if (oid.equals(EmailAddress))
+ {
+ v.add(new DERIA5String(str));
+ }
+ else
+ {
+ if (canBePrintable(str))
+ {
+ v.add(new DERPrintableString(str));
+ }
+ else
+ {
+ v.add(new DERUTF8String(str));
+ }
+ }
+
+ vec.add(new DERSet(new DERSequence(v)));
+ }
+
+ seq = new DERSequence(vec);
+ }
+
+ return seq;
+ }
+
+ /**
+ * test for equality - note: case is ignored.
+ */
+ public boolean equals(Object _obj)
+ {
+ if (_obj == this)
+ {
+ return true;
+ }
+
+ if (_obj == null || !(_obj instanceof X509Name))
+ {
+ return false;
+ }
+
+ X509Name _oxn = (X509Name)_obj;
+ int _orderingSize = ordering.size();
+
+ if (_orderingSize != _oxn.ordering.size())
+ {
+ return false;
+ }
+
+ boolean[] _indexes = new boolean[_orderingSize];
+
+ for(int i = 0; i < _orderingSize; i++)
+ {
+ boolean _found = false;
+ String _oid = ((DERObjectIdentifier)ordering.elementAt(i)).getId();
+ String _val = (String)values.elementAt(i);
+
+ for(int j = 0; j < _orderingSize; j++)
+ {
+ if(_indexes[j] == true)
+ {
+ continue;
+ }
+
+ String _oOID = ((DERObjectIdentifier)_oxn.ordering.elementAt(j)).getId();
+ String _oVal = (String)_oxn.values.elementAt(j);
+
+ // was equalsIgnoreCase but MIDP doesn't like that.
+ if(_oid.equals(_oOID) && _val.toLowerCase().equals(_oVal.toLowerCase()))
+ {
+ _indexes[j] = true;
+ _found = true;
+ break;
+ }
+
+ }
+
+ if(!_found)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public int hashCode()
+ {
+ ASN1Sequence seq = (ASN1Sequence)this.getDERObject();
+ Enumeration e = seq.getObjects();
+ int hashCode = 0;
+
+ while (e.hasMoreElements())
+ {
+ hashCode ^= e.nextElement().hashCode();
+ }
+
+ return hashCode;
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ boolean first = true;
+ Enumeration e1 = ordering.elements();
+ Enumeration e2 = values.elements();
+
+ while (e1.hasMoreElements())
+ {
+ Object oid = e1.nextElement();
+ String sym = (String)OIDLookUp.get(oid);
+
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ buf.append(",");
+ }
+
+ if (sym != null)
+ {
+ buf.append(sym);
+ }
+ else
+ {
+ buf.append(((DERObjectIdentifier)oid).getId());
+ }
+
+ buf.append("=");
+
+ int index = buf.length();
+
+ buf.append((String)e2.nextElement());
+
+ int end = buf.length();
+
+ while (index != end)
+ {
+ if ((buf.charAt(index) == ',')
+ || (buf.charAt(index) == '"')
+ || (buf.charAt(index) == '\\')
+ || (buf.charAt(index) == '+')
+ || (buf.charAt(index) == '<')
+ || (buf.charAt(index) == '>')
+ || (buf.charAt(index) == ';'))
+ {
+ buf.insert(index, "\\");
+ index++;
+ end++;
+ }
+
+ index++;
+ }
+ }
+
+ return buf.toString();
+ }
+}
+
+/**
+ * class for breaking up an X500 Name into it's component tokens, ala
+ * java.util.StringTokenizer. We need this class as some of the
+ * lightweight Java environment don't support classes like
+ * StringTokenizer.
+ */
+class X509NameTokenizer
+{
+ private String oid;
+ private int index;
+ private StringBuffer buf = new StringBuffer();
+
+ public X509NameTokenizer(
+ String oid)
+ {
+ this.oid = oid;
+ this.index = -1;
+ }
+
+ public boolean hasMoreTokens()
+ {
+ return (index != oid.length());
+ }
+
+ public String nextToken()
+ {
+ if (index == oid.length())
+ {
+ return null;
+ }
+
+ int end = index + 1;
+ boolean quoted = false;
+ boolean escaped = false;
+
+ buf.setLength(0);
+
+ while (end != oid.length())
+ {
+ char c = oid.charAt(end);
+
+ if (c == '"')
+ {
+ if (!escaped)
+ {
+ quoted = !quoted;
+ }
+ else
+ {
+ buf.append(c);
+ }
+ escaped = false;
+ }
+ else
+ {
+ if (escaped || quoted)
+ {
+ buf.append(c);
+ escaped = false;
+ }
+ else if (c == '\\')
+ {
+ escaped = true;
+ }
+ else if (c == ',')
+ {
+ break;
+ }
+ else
+ {
+ buf.append(c);
+ }
+ }
+ end++;
+ }
+
+ index = end;
+ return buf.toString().trim();
+ }
+}
+
+
+interface X509ObjectIdentifiers
+{
+ //
+ // base id
+ //
+ static final String id = "2.5.4";
+
+ static final DERObjectIdentifier commonName = new DERObjectIdentifier(id + ".3");
+ static final DERObjectIdentifier countryName = new DERObjectIdentifier(id + ".6");
+ static final DERObjectIdentifier localityName = new DERObjectIdentifier(id + ".7");
+ static final DERObjectIdentifier stateOrProvinceName = new DERObjectIdentifier(id + ".8");
+ static final DERObjectIdentifier organization = new DERObjectIdentifier(id + ".10");
+ static final DERObjectIdentifier organizationalUnitName = new DERObjectIdentifier(id + ".11");
+
+ // id-SHA1 OBJECT IDENTIFIER ::=
+ // {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 } //
+ static final DERObjectIdentifier id_SHA1 = new DERObjectIdentifier("1.3.14.3.2.26");
+
+ //
+ // ripemd160 OBJECT IDENTIFIER ::=
+ // {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) hashAlgorithm(2) RIPEMD-160(1)}
+ //
+ static final DERObjectIdentifier ripemd160 = new DERObjectIdentifier("1.3.36.3.2.1");
+
+ //
+ // ripemd160WithRSAEncryption OBJECT IDENTIFIER ::=
+ // {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) signatureAlgorithm(3) rsaSignature(1) rsaSignatureWithripemd160(2) }
+ //
+ static final DERObjectIdentifier ripemd160WithRSAEncryption = new DERObjectIdentifier("1.3.36.3.3.1.2");
+
+
+ static final DERObjectIdentifier id_ea_rsa = new DERObjectIdentifier("2.5.8.1.1");
+}
+
+
+
+/**
+ * base interface that a public/private key block cipher needs
+ * to conform to.
+ */
+interface AsymmetricBlockCipher
+{
+ /**
+ * initialise the cipher.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param param the key and other data required by the cipher.
+ */
+ public void init(boolean forEncryption, CipherParameters param);
+
+ /**
+ * returns the largest size an input block can be.
+ *
+ * @return maximum size for an input block.
+ */
+ public int getInputBlockSize();
+
+ /**
+ * returns the maximum size of the block produced by this cipher.
+ *
+ * @return maximum size of the output block produced by the cipher.
+ */
+ public int getOutputBlockSize();
+
+ /**
+ * process the block of len bytes stored in in from offset inOff.
+ *
+ * @param in the input data
+ * @param inOff offset into the in array where the data starts
+ * @param len the length of the block to be processed.
+ * @return the resulting byte array of the encryption/decryption process.
+ * @exception InvalidCipherTextException data decrypts improperly.
+ * @exception DataLengthException the input data is too large for the cipher.
+ */
+ public byte[] processBlock(byte[] in, int inOff, int len)
+ throws InvalidCipherTextException;
+}
+
+/**
+ * a holding class for public/private parameter pairs.
+ */
+class AsymmetricCipherKeyPair
+{
+ private CipherParameters publicParam;
+ private CipherParameters privateParam;
+
+ /**
+ * basic constructor.
+ *
+ * @param publicParam a public key parameters object.
+ * @param privateParam the corresponding private key parameters.
+ */
+ public AsymmetricCipherKeyPair(
+ CipherParameters publicParam,
+ CipherParameters privateParam)
+ {
+ this.publicParam = publicParam;
+ this.privateParam = privateParam;
+ }
+
+ /**
+ * return the public key parameters.
+ *
+ * @return the public key parameters.
+ */
+ public CipherParameters getPublic()
+ {
+ return publicParam;
+ }
+
+ /**
+ * return the private key parameters.
+ *
+ * @return the private key parameters.
+ */
+ public CipherParameters getPrivate()
+ {
+ return privateParam;
+ }
+}
+
+/**
+ * interface that a public/private key pair generator should conform to.
+ */
+interface AsymmetricCipherKeyPairGenerator
+{
+ /**
+ * intialise the key pair generator.
+ *
+ * @param the parameters the key pair is to be initialised with.
+ */
+ public void init(KeyGenerationParameters param);
+
+ /**
+ * return an AsymmetricCipherKeyPair containing the generated keys.
+ *
+ * @return an AsymmetricCipherKeyPair containing the generated keys.
+ */
+ public AsymmetricCipherKeyPair generateKeyPair();
+}
+
+
+
+/**
+ * Block cipher engines are expected to conform to this interface.
+ */
+interface BlockCipher
+{
+ /**
+ * Initialise the cipher.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param param the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException;
+
+ /**
+ * Return the name of the algorithm the cipher implements.
+ *
+ * @return the name of the algorithm the cipher implements.
+ */
+ public String getAlgorithmName();
+
+ /**
+ * Return the block size for this cipher (in bytes).
+ *
+ * @return the block size for this cipher in bytes.
+ */
+ public int getBlockSize();
+
+ /**
+ * Process one block of input from the array in and write it to
+ * the out array.
+ *
+ * @param in the array containing the input data.
+ * @param inOff offset into the in array the data starts at.
+ * @param out the array the output data will be copied into.
+ * @param outOff the offset into the out array the output will start at.
+ * @exception DataLengthException if there isn't enough data in in, or
+ * space in out.
+ * @exception IllegalStateException if the cipher isn't initialised.
+ * @return the number of bytes processed and produced.
+ */
+ public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ throws DataLengthException, IllegalStateException;
+
+ /**
+ * Reset the cipher. After resetting the cipher is in the same state
+ * as it was after the last init (if there was one).
+ */
+ public void reset();
+}
+
+
+/**
+ * The base class for symmetric, or secret, cipher key generators.
+ */
+class CipherKeyGenerator
+{
+ protected Random random;
+ protected int strength;
+
+ /**
+ * initialise the key generator.
+ *
+ * @param param the parameters to be used for key generation
+ */
+ public void init(
+ KeyGenerationParameters param)
+ {
+ this.random = param.getRandom();
+ this.strength = (param.getStrength() + 7) / 8;
+ }
+
+ /**
+ * generate a secret key.
+ *
+ * @return a byte array containing the key value.
+ */
+ public byte[] generateKey()
+ {
+ byte[] key = new byte[strength];
+
+ random.nextBytes(key);
+
+ return key;
+ }
+}
+
+/**
+ * all parameter classes implement this.
+ */
+interface CipherParameters
+{
+}
+
+/**
+ * the foundation class for the hard exceptions thrown by the crypto packages.
+ */
+class CryptoException
+ extends Exception
+{
+ /**
+ * base constructor.
+ */
+ public CryptoException()
+ {
+ }
+
+ /**
+ * create a CryptoException with the given message.
+ *
+ * @param message the message to be carried with the exception.
+ */
+ public CryptoException(
+ String message)
+ {
+ super(message);
+ }
+}
+
+/**
+ * this exception is thrown if a buffer that is meant to have output
+ * copied into it turns out to be too short, or if we've been given
+ * insufficient input. In general this exception will get thrown rather
+ * than an ArrayOutOfBounds exception.
+ */
+class DataLengthException
+ extends RuntimeCryptoException
+{
+ /**
+ * base constructor.
+ */
+ public DataLengthException()
+ {
+ }
+
+ /**
+ * create a DataLengthException with the given message.
+ *
+ * @param message the message to be carried with the exception.
+ */
+ public DataLengthException(
+ String message)
+ {
+ super(message);
+ }
+}
+
+/**
+ * interface that a message digest conforms to.
+ */
+interface Digest
+{
+ /**
+ * return the algorithm name
+ *
+ * @return the algorithm name
+ */
+ public String getAlgorithmName();
+
+ /**
+ * return the size, in bytes, of the digest produced by this message digest.
+ *
+ * @return the size, in bytes, of the digest produced by this message digest.
+ */
+ public int getDigestSize();
+
+ /**
+ * update the message digest with a single byte.
+ *
+ * @param in the input byte to be entered.
+ */
+ public void update(byte in);
+
+ /**
+ * update the message digest with a block of bytes.
+ *
+ * @param in the byte array containing the data.
+ * @param inOff the offset into the byte array where the data starts.
+ * @param len the length of the data.
+ */
+ public void update(byte[] in, int inOff, int len);
+
+ /**
+ * close the digest, producing the final digest value. The doFinal
+ * call leaves the digest reset.
+ *
+ * @param out the array the digest is to be copied into.
+ * @param outOff the offset into the out array the digest is to start at.
+ */
+ public int doFinal(byte[] out, int outOff);
+
+ /**
+ * reset the digest back to it's initial state.
+ */
+ public void reset();
+}
+
+
+/**
+ * base implementation of MD4 family style digest as outlined in
+ * "Handbook of Applied Cryptography", pages 344 - 347.
+ */
+abstract class GeneralDigest
+ implements Digest
+{
+ private byte[] xBuf;
+ private int xBufOff;
+
+ private long byteCount;
+
+ /**
+ * Standard constructor
+ */
+ protected GeneralDigest()
+ {
+ xBuf = new byte[4];
+ xBufOff = 0;
+ }
+
+ /**
+ * Copy constructor. We are using copy constructors in place
+ * of the Object.clone() interface as this interface is not
+ * supported by J2ME.
+ */
+ protected GeneralDigest(GeneralDigest t)
+ {
+ xBuf = new byte[t.xBuf.length];
+ System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length);
+
+ xBufOff = t.xBufOff;
+ byteCount = t.byteCount;
+ }
+
+ public void update(
+ byte in)
+ {
+ xBuf[xBufOff++] = in;
+
+ if (xBufOff == xBuf.length)
+ {
+ processWord(xBuf, 0);
+ xBufOff = 0;
+ }
+
+ byteCount++;
+ }
+
+ public void update(
+ byte[] in,
+ int inOff,
+ int len)
+ {
+ //
+ // fill the current word
+ //
+ while ((xBufOff != 0) && (len > 0))
+ {
+ update(in[inOff]);
+
+ inOff++;
+ len--;
+ }
+
+ //
+ // process whole words.
+ //
+ while (len > xBuf.length)
+ {
+ processWord(in, inOff);
+
+ inOff += xBuf.length;
+ len -= xBuf.length;
+ byteCount += xBuf.length;
+ }
+
+ //
+ // load in the remainder.
+ //
+ while (len > 0)
+ {
+ update(in[inOff]);
+
+ inOff++;
+ len--;
+ }
+ }
+
+ public void finish()
+ {
+ long bitLength = (byteCount << 3);
+
+ //
+ // add the pad bytes.
+ //
+ update((byte)128);
+
+ while (xBufOff != 0)
+ {
+ update((byte)0);
+ }
+
+ processLength(bitLength);
+
+ processBlock();
+ }
+
+ public void reset()
+ {
+ byteCount = 0;
+
+ xBufOff = 0;
+ for ( int i = 0; i < xBuf.length; i++ ) {
+ xBuf[i] = 0;
+ }
+ }
+
+ protected abstract void processWord(byte[] in, int inOff);
+
+ protected abstract void processLength(long bitLength);
+
+ protected abstract void processBlock();
+}
+
+/**
+ * implementation of MD2
+ * as outlined in RFC1319 by B.Kaliski from RSA Laboratories April 1992
+ */
+class MD2Digest\r
+ implements Digest\r
+{\r
+ private static final int DIGEST_LENGTH = 16;\r
+\r
+ /* X buffer */\r
+ private byte[] X = new byte[48];\r
+ private int xOff;
+\r /* M buffer */
+\r private byte[] M = new byte[16];\r
+ private int mOff;
+\r /* check sum */
+\r private byte[] C = new byte[16];
+ private int COff;
+
+ public MD2Digest()\r
+ {\r
+ reset();\r
+ }\r
+ public MD2Digest(MD2Digest t)\r
+ {
+ System.arraycopy(t.X, 0, X, 0, t.X.length);
+ xOff = t.xOff;
+ System.arraycopy(t.M, 0, M, 0, t.M.length);
+ mOff = t.mOff;
+ System.arraycopy(t.C, 0, C, 0, t.C.length);
+ COff = t.COff;
+ }
+ /**\r
+ * return the algorithm name
+ *
+ * @return the algorithm name
+ */
+ public String getAlgorithmName()
+ {
+ return "MD2";
+ }
+ /**
+ * return the size, in bytes, of the digest produced by this message digest.
+ *
+ * @return the size, in bytes, of the digest produced by this message digest.
+ */
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+ /**
+ * close the digest, producing the final digest value. The doFinal
+ * call leaves the digest reset.
+ *
+ * @param out the array the digest is to be copied into.
+ * @param outOff the offset into the out array the digest is to start at.
+ */
+ public int doFinal(byte[] out, int outOff)
+ {
+ // add padding
+ byte paddingByte = (byte)(M.length-mOff);
+ for (int i=mOff;i<M.length;i++)
+ {
+ M[i] = paddingByte;
+ }
+ //do final check sum
+ processCheckSum(M);
+ // do final block process
+ processBlock(M);
+
+ processBlock(C);
+
+ System.arraycopy(X,xOff,out,outOff,16);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+ /**
+ * reset the digest back to it's initial state.
+ */
+ public void reset()\r
+ {\r
+ xOff = 0;\r
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ mOff = 0;\r
+ for (int i = 0; i != M.length; i++)
+ {
+ M[i] = 0;
+ }
+ COff = 0;\r
+ for (int i = 0; i != C.length; i++)
+ {
+ C[i] = 0;
+ }
+ }\r
+ /**\r
+ * update the message digest with a single byte.
+ *
+ * @param in the input byte to be entered.
+ */
+ public void update(byte in)
+ {
+ M[mOff++] = in;
+
+ if (mOff == 16)
+ {
+ processCheckSum(M);
+ processBlock(M);
+ mOff = 0;
+ }
+ }
+
+ /**
+ * update the message digest with a block of bytes.
+ *
+ * @param in the byte array containing the data.
+ * @param inOff the offset into the byte array where the data starts.
+ * @param len the length of the data.
+ */
+ public void update(byte[] in, int inOff, int len)\r
+ {\r
+ //\r
+ // fill the current word
+ //
+ while ((mOff != 0) && (len > 0))
+ {
+ update(in[inOff]);
+ inOff++;
+ len--;
+ }
+
+ //
+ // process whole words.
+ //
+ while (len > 16)
+ {
+ System.arraycopy(in,inOff,M,0,16);
+ processCheckSum(M);
+ processBlock(M);
+ len -= 16;
+ inOff += 16;
+ }
+
+ //
+ // load in the remainder.
+ //
+ while (len > 0)
+ {
+ update(in[inOff]);
+ inOff++;
+ len--;
+ }\r
+ }\r
+ protected void processCheckSum(byte[] m)\r
+ {\r
+ int L = C[15];\r
+ for (int i=0;i<16;i++)\r
+ {\r
+ C[i] ^= S[(m[i] ^ L) & 0xff];\r
+ L = C[i];\r
+ }\r
+ }\r
+ protected void processBlock(byte[] m)\r
+ {
+ for (int i=0;i<16;i++)
+ {
+ X[i+16] = m[i];
+ X[i+32] = (byte)(m[i] ^ X[i]);
+ }
+ // encrypt block
+ int t = 0;
+
+ for (int j=0;j<18;j++)
+ {
+ for (int k=0;k<48;k++)
+ {
+ t = X[k] ^= S[t];
+ t = t & 0xff;
+ }
+ t = (t + j)%256;
+ }
+ }
+ // 256-byte random permutation constructed from the digits of PI
+ private static final byte[] S = {\r
+ (byte)41,(byte)46,(byte)67,(byte)201,(byte)162,(byte)216,(byte)124,\r
+ (byte)1,(byte)61,(byte)54,(byte)84,(byte)161,(byte)236,(byte)240,\r
+ (byte)6,(byte)19,(byte)98,(byte)167,(byte)5,(byte)243,(byte)192,\r
+ (byte)199,(byte)115,(byte)140,(byte)152,(byte)147,(byte)43,(byte)217,\r
+ (byte)188,(byte)76,(byte)130,(byte)202,(byte)30,(byte)155,(byte)87,\r
+ (byte)60,(byte)253,(byte)212,(byte)224,(byte)22,(byte)103,(byte)66,\r
+ (byte)111,(byte)24,(byte)138,(byte)23,(byte)229,(byte)18,(byte)190,\r
+ (byte)78,(byte)196,(byte)214,(byte)218,(byte)158,(byte)222,(byte)73,\r
+ (byte)160,(byte)251,(byte)245,(byte)142,(byte)187,(byte)47,(byte)238,\r
+ (byte)122,(byte)169,(byte)104,(byte)121,(byte)145,(byte)21,(byte)178,\r
+ (byte)7,(byte)63,(byte)148,(byte)194,(byte)16,(byte)137,(byte)11,\r
+ (byte)34,(byte)95,(byte)33,(byte)128,(byte)127,(byte)93,(byte)154,\r
+ (byte)90,(byte)144,(byte)50,(byte)39,(byte)53,(byte)62,(byte)204,\r
+ (byte)231,(byte)191,(byte)247,(byte)151,(byte)3,(byte)255,(byte)25,\r
+ (byte)48,(byte)179,(byte)72,(byte)165,(byte)181,(byte)209,(byte)215,\r
+ (byte)94,(byte)146,(byte)42,(byte)172,(byte)86,(byte)170,(byte)198,\r
+ (byte)79,(byte)184,(byte)56,(byte)210,(byte)150,(byte)164,(byte)125,\r
+ (byte)182,(byte)118,(byte)252,(byte)107,(byte)226,(byte)156,(byte)116,\r
+ (byte)4,(byte)241,(byte)69,(byte)157,(byte)112,(byte)89,(byte)100,\r
+ (byte)113,(byte)135,(byte)32,(byte)134,(byte)91,(byte)207,(byte)101,\r
+ (byte)230,(byte)45,(byte)168,(byte)2,(byte)27,(byte)96,(byte)37,\r
+ (byte)173,(byte)174,(byte)176,(byte)185,(byte)246,(byte)28,(byte)70,\r
+ (byte)97,(byte)105,(byte)52,(byte)64,(byte)126,(byte)15,(byte)85,\r
+ (byte)71,(byte)163,(byte)35,(byte)221,(byte)81,(byte)175,(byte)58,\r
+ (byte)195,(byte)92,(byte)249,(byte)206,(byte)186,(byte)197,(byte)234,\r
+ (byte)38,(byte)44,(byte)83,(byte)13,(byte)110,(byte)133,(byte)40,\r
+ (byte)132, 9,(byte)211,(byte)223,(byte)205,(byte)244,(byte)65,\r
+ (byte)129,(byte)77,(byte)82,(byte)106,(byte)220,(byte)55,(byte)200,\r
+ (byte)108,(byte)193,(byte)171,(byte)250,(byte)36,(byte)225,(byte)123,\r
+ (byte)8,(byte)12,(byte)189,(byte)177,(byte)74,(byte)120,(byte)136,\r
+ (byte)149,(byte)139,(byte)227,(byte)99,(byte)232,(byte)109,(byte)233,\r
+ (byte)203,(byte)213,(byte)254,(byte)59,(byte)0,(byte)29,(byte)57,\r
+ (byte)242,(byte)239,(byte)183,(byte)14,(byte)102,(byte)88,(byte)208,\r
+ (byte)228,(byte)166,(byte)119,(byte)114,(byte)248,(byte)235,(byte)117,\r
+ (byte)75,(byte)10,(byte)49,(byte)68,(byte)80,(byte)180,(byte)143,\r
+ (byte)237,(byte)31,(byte)26,(byte)219,(byte)153,(byte)141,(byte)51,\r
+ (byte)159,(byte)17,(byte)131,(byte)20\r
+ };\r
+}\r
+
+
+/**
+ * implementation of MD5 as outlined in "Handbook of Applied Cryptography", pages 346 - 347.
+ */
+class MD5Digest
+ extends GeneralDigest
+{
+ private static final int DIGEST_LENGTH = 16;
+
+ private int H1, H2, H3, H4; // IV's
+
+ private int[] X = new int[16];
+ private int xOff;
+
+ /**
+ * Standard constructor
+ */
+ public MD5Digest()
+ {
+ reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public MD5Digest(MD5Digest t)
+ {
+ super(t);
+
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+
+ System.arraycopy(t.X, 0, X, 0, t.X.length);
+ xOff = t.xOff;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "MD5";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ protected void processWord(
+ byte[] in,
+ int inOff)
+ {
+ X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8)
+ | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24);
+
+ if (xOff == 16)
+ {
+ processBlock();
+ }
+ }
+
+ protected void processLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ processBlock();
+ }
+
+ X[14] = (int)(bitLength & 0xffffffff);
+ X[15] = (int)(bitLength >>> 32);
+ }
+
+ private void unpackWord(
+ int word,
+ byte[] out,
+ int outOff)
+ {
+ out[outOff] = (byte)word;
+ out[outOff + 1] = (byte)(word >>> 8);
+ out[outOff + 2] = (byte)(word >>> 16);
+ out[outOff + 3] = (byte)(word >>> 24);
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ unpackWord(H1, out, outOff);
+ unpackWord(H2, out, outOff + 4);
+ unpackWord(H3, out, outOff + 8);
+ unpackWord(H4, out, outOff + 12);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables to the IV values.
+ */
+ public void reset()
+ {
+ super.reset();
+
+ H1 = 0x67452301;
+ H2 = 0xefcdab89;
+ H3 = 0x98badcfe;
+ H4 = 0x10325476;
+
+ xOff = 0;
+
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ //
+ // round 1 left rotates
+ //
+ private static final int S11 = 7;
+ private static final int S12 = 12;
+ private static final int S13 = 17;
+ private static final int S14 = 22;
+
+ //
+ // round 2 left rotates
+ //
+ private static final int S21 = 5;
+ private static final int S22 = 9;
+ private static final int S23 = 14;
+ private static final int S24 = 20;
+
+ //
+ // round 3 left rotates
+ //
+ private static final int S31 = 4;
+ private static final int S32 = 11;
+ private static final int S33 = 16;
+ private static final int S34 = 23;
+
+ //
+ // round 4 left rotates
+ //
+ private static final int S41 = 6;
+ private static final int S42 = 10;
+ private static final int S43 = 15;
+ private static final int S44 = 21;
+
+ /*
+ * rotate int x left n bits.
+ */
+ private int rotateLeft(
+ int x,
+ int n)
+ {
+ return (x << n) | (x >>> (32 - n));
+ }
+
+ /*
+ * F, G, H and I are the basic MD5 functions.
+ */
+ private int F(
+ int u,
+ int v,
+ int w)
+ {
+ return (u & v) | (~u & w);
+ }
+
+ private int G(
+ int u,
+ int v,
+ int w)
+ {
+ return (u & w) | (v & ~w);
+ }
+
+ private int H(
+ int u,
+ int v,
+ int w)
+ {
+ return u ^ v ^ w;
+ }
+
+ private int K(
+ int u,
+ int v,
+ int w)
+ {
+ return v ^ (u | ~w);
+ }
+
+ protected void processBlock()
+ {
+ int a = H1;
+ int b = H2;
+ int c = H3;
+ int d = H4;
+
+ //
+ // Round 1 - F cycle, 16 times.
+ //
+ a = rotateLeft((a + F(b, c, d) + X[ 0] + 0xd76aa478), S11) + b;
+ d = rotateLeft((d + F(a, b, c) + X[ 1] + 0xe8c7b756), S12) + a;
+ c = rotateLeft((c + F(d, a, b) + X[ 2] + 0x242070db), S13) + d;
+ b = rotateLeft((b + F(c, d, a) + X[ 3] + 0xc1bdceee), S14) + c;
+ a = rotateLeft((a + F(b, c, d) + X[ 4] + 0xf57c0faf), S11) + b;
+ d = rotateLeft((d + F(a, b, c) + X[ 5] + 0x4787c62a), S12) + a;
+ c = rotateLeft((c + F(d, a, b) + X[ 6] + 0xa8304613), S13) + d;
+ b = rotateLeft((b + F(c, d, a) + X[ 7] + 0xfd469501), S14) + c;
+ a = rotateLeft((a + F(b, c, d) + X[ 8] + 0x698098d8), S11) + b;
+ d = rotateLeft((d + F(a, b, c) + X[ 9] + 0x8b44f7af), S12) + a;
+ c = rotateLeft((c + F(d, a, b) + X[10] + 0xffff5bb1), S13) + d;
+ b = rotateLeft((b + F(c, d, a) + X[11] + 0x895cd7be), S14) + c;
+ a = rotateLeft((a + F(b, c, d) + X[12] + 0x6b901122), S11) + b;
+ d = rotateLeft((d + F(a, b, c) + X[13] + 0xfd987193), S12) + a;
+ c = rotateLeft((c + F(d, a, b) + X[14] + 0xa679438e), S13) + d;
+ b = rotateLeft((b + F(c, d, a) + X[15] + 0x49b40821), S14) + c;
+
+ //
+ // Round 2 - G cycle, 16 times.
+ //
+ a = rotateLeft((a + G(b, c, d) + X[ 1] + 0xf61e2562), S21) + b;
+ d = rotateLeft((d + G(a, b, c) + X[ 6] + 0xc040b340), S22) + a;
+ c = rotateLeft((c + G(d, a, b) + X[11] + 0x265e5a51), S23) + d;
+ b = rotateLeft((b + G(c, d, a) + X[ 0] + 0xe9b6c7aa), S24) + c;
+ a = rotateLeft((a + G(b, c, d) + X[ 5] + 0xd62f105d), S21) + b;
+ d = rotateLeft((d + G(a, b, c) + X[10] + 0x02441453), S22) + a;
+ c = rotateLeft((c + G(d, a, b) + X[15] + 0xd8a1e681), S23) + d;
+ b = rotateLeft((b + G(c, d, a) + X[ 4] + 0xe7d3fbc8), S24) + c;
+ a = rotateLeft((a + G(b, c, d) + X[ 9] + 0x21e1cde6), S21) + b;
+ d = rotateLeft((d + G(a, b, c) + X[14] + 0xc33707d6), S22) + a;
+ c = rotateLeft((c + G(d, a, b) + X[ 3] + 0xf4d50d87), S23) + d;
+ b = rotateLeft((b + G(c, d, a) + X[ 8] + 0x455a14ed), S24) + c;
+ a = rotateLeft((a + G(b, c, d) + X[13] + 0xa9e3e905), S21) + b;
+ d = rotateLeft((d + G(a, b, c) + X[ 2] + 0xfcefa3f8), S22) + a;
+ c = rotateLeft((c + G(d, a, b) + X[ 7] + 0x676f02d9), S23) + d;
+ b = rotateLeft((b + G(c, d, a) + X[12] + 0x8d2a4c8a), S24) + c;
+
+ //
+ // Round 3 - H cycle, 16 times.
+ //
+ a = rotateLeft((a + H(b, c, d) + X[ 5] + 0xfffa3942), S31) + b;
+ d = rotateLeft((d + H(a, b, c) + X[ 8] + 0x8771f681), S32) + a;
+ c = rotateLeft((c + H(d, a, b) + X[11] + 0x6d9d6122), S33) + d;
+ b = rotateLeft((b + H(c, d, a) + X[14] + 0xfde5380c), S34) + c;
+ a = rotateLeft((a + H(b, c, d) + X[ 1] + 0xa4beea44), S31) + b;
+ d = rotateLeft((d + H(a, b, c) + X[ 4] + 0x4bdecfa9), S32) + a;
+ c = rotateLeft((c + H(d, a, b) + X[ 7] + 0xf6bb4b60), S33) + d;
+ b = rotateLeft((b + H(c, d, a) + X[10] + 0xbebfbc70), S34) + c;
+ a = rotateLeft((a + H(b, c, d) + X[13] + 0x289b7ec6), S31) + b;
+ d = rotateLeft((d + H(a, b, c) + X[ 0] + 0xeaa127fa), S32) + a;
+ c = rotateLeft((c + H(d, a, b) + X[ 3] + 0xd4ef3085), S33) + d;
+ b = rotateLeft((b + H(c, d, a) + X[ 6] + 0x04881d05), S34) + c;
+ a = rotateLeft((a + H(b, c, d) + X[ 9] + 0xd9d4d039), S31) + b;
+ d = rotateLeft((d + H(a, b, c) + X[12] + 0xe6db99e5), S32) + a;
+ c = rotateLeft((c + H(d, a, b) + X[15] + 0x1fa27cf8), S33) + d;
+ b = rotateLeft((b + H(c, d, a) + X[ 2] + 0xc4ac5665), S34) + c;
+
+ //
+ // Round 4 - K cycle, 16 times.
+ //
+ a = rotateLeft((a + K(b, c, d) + X[ 0] + 0xf4292244), S41) + b;
+ d = rotateLeft((d + K(a, b, c) + X[ 7] + 0x432aff97), S42) + a;
+ c = rotateLeft((c + K(d, a, b) + X[14] + 0xab9423a7), S43) + d;
+ b = rotateLeft((b + K(c, d, a) + X[ 5] + 0xfc93a039), S44) + c;
+ a = rotateLeft((a + K(b, c, d) + X[12] + 0x655b59c3), S41) + b;
+ d = rotateLeft((d + K(a, b, c) + X[ 3] + 0x8f0ccc92), S42) + a;
+ c = rotateLeft((c + K(d, a, b) + X[10] + 0xffeff47d), S43) + d;
+ b = rotateLeft((b + K(c, d, a) + X[ 1] + 0x85845dd1), S44) + c;
+ a = rotateLeft((a + K(b, c, d) + X[ 8] + 0x6fa87e4f), S41) + b;
+ d = rotateLeft((d + K(a, b, c) + X[15] + 0xfe2ce6e0), S42) + a;
+ c = rotateLeft((c + K(d, a, b) + X[ 6] + 0xa3014314), S43) + d;
+ b = rotateLeft((b + K(c, d, a) + X[13] + 0x4e0811a1), S44) + c;
+ a = rotateLeft((a + K(b, c, d) + X[ 4] + 0xf7537e82), S41) + b;
+ d = rotateLeft((d + K(a, b, c) + X[11] + 0xbd3af235), S42) + a;
+ c = rotateLeft((c + K(d, a, b) + X[ 2] + 0x2ad7d2bb), S43) + d;
+ b = rotateLeft((b + K(c, d, a) + X[ 9] + 0xeb86d391), S44) + c;
+
+ H1 += a;
+ H2 += b;
+ H3 += c;
+ H4 += d;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+}
+
+
+/**
+ * implementation of SHA-1 as outlined in "Handbook of Applied Cryptography", pages 346 - 349.
+ *
+ * It is interesting to ponder why the, apart from the extra IV, the other difference here from MD5
+ * is the "endienness" of the word processing!
+ */
+class SHA1Digest
+ extends GeneralDigest
+{
+ private static final int DIGEST_LENGTH = 20;
+
+ private int H1, H2, H3, H4, H5;
+
+ private int[] X = new int[80];
+ private int xOff;
+
+ /**
+ * Standard constructor
+ */
+ public SHA1Digest()
+ {
+ reset();
+ }
+
+ /**
+ * Copy constructor. This will copy the state of the provided
+ * message digest.
+ */
+ public SHA1Digest(SHA1Digest t)
+ {
+ super(t);
+
+ H1 = t.H1;
+ H2 = t.H2;
+ H3 = t.H3;
+ H4 = t.H4;
+ H5 = t.H5;
+
+ System.arraycopy(t.X, 0, X, 0, t.X.length);
+ xOff = t.xOff;
+ }
+
+ public String getAlgorithmName()
+ {
+ return "SHA-1";
+ }
+
+ public int getDigestSize()
+ {
+ return DIGEST_LENGTH;
+ }
+
+ protected void processWord(
+ byte[] in,
+ int inOff)
+ {
+ X[xOff++] = ((in[inOff] & 0xff) << 24) | ((in[inOff + 1] & 0xff) << 16)
+ | ((in[inOff + 2] & 0xff) << 8) | ((in[inOff + 3] & 0xff));
+
+ if (xOff == 16)
+ {
+ processBlock();
+ }
+ }
+
+ private void unpackWord(
+ int word,
+ byte[] out,
+ int outOff)
+ {
+ out[outOff] = (byte)(word >>> 24);
+ out[outOff + 1] = (byte)(word >>> 16);
+ out[outOff + 2] = (byte)(word >>> 8);
+ out[outOff + 3] = (byte)word;
+ }
+
+ protected void processLength(
+ long bitLength)
+ {
+ if (xOff > 14)
+ {
+ processBlock();
+ }
+
+ X[14] = (int)(bitLength >>> 32);
+ X[15] = (int)(bitLength & 0xffffffff);
+ }
+
+ public int doFinal(
+ byte[] out,
+ int outOff)
+ {
+ finish();
+
+ unpackWord(H1, out, outOff);
+ unpackWord(H2, out, outOff + 4);
+ unpackWord(H3, out, outOff + 8);
+ unpackWord(H4, out, outOff + 12);
+ unpackWord(H5, out, outOff + 16);
+
+ reset();
+
+ return DIGEST_LENGTH;
+ }
+
+ /**
+ * reset the chaining variables
+ */
+ public void reset()
+ {
+ super.reset();
+
+ H1 = 0x67452301;
+ H2 = 0xefcdab89;
+ H3 = 0x98badcfe;
+ H4 = 0x10325476;
+ H5 = 0xc3d2e1f0;
+
+ xOff = 0;
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+
+ //
+ // Additive constants
+ //
+ private static final int Y1 = 0x5a827999;
+ private static final int Y2 = 0x6ed9eba1;
+ private static final int Y3 = 0x8f1bbcdc;
+ private static final int Y4 = 0xca62c1d6;
+
+ private int f(
+ int u,
+ int v,
+ int w)
+ {
+ return ((u & v) | ((~u) & w));
+ }
+
+ private int h(
+ int u,
+ int v,
+ int w)
+ {
+ return (u ^ v ^ w);
+ }
+
+ private int g(
+ int u,
+ int v,
+ int w)
+ {
+ return ((u & v) | (u & w) | (v & w));
+ }
+
+ private int rotateLeft(
+ int x,
+ int n)
+ {
+ return (x << n) | (x >>> (32 - n));
+ }
+
+ protected void processBlock()
+ {
+ //
+ // expand 16 word block into 80 word block.
+ //
+ for (int i = 16; i <= 79; i++)
+ {
+ X[i] = rotateLeft((X[i - 3] ^ X[i - 8] ^ X[i - 14] ^ X[i - 16]), 1);
+ }
+
+ //
+ // set up working variables.
+ //
+ int A = H1;
+ int B = H2;
+ int C = H3;
+ int D = H4;
+ int E = H5;
+
+ //
+ // round 1
+ //
+ for (int j = 0; j <= 19; j++)
+ {
+ int t = rotateLeft(A, 5) + f(B, C, D) + E + X[j] + Y1;
+
+ E = D;
+ D = C;
+ C = rotateLeft(B, 30);
+ B = A;
+ A = t;
+ }
+
+ //
+ // round 2
+ //
+ for (int j = 20; j <= 39; j++)
+ {
+ int t = rotateLeft(A, 5) + h(B, C, D) + E + X[j] + Y2;
+
+ E = D;
+ D = C;
+ C = rotateLeft(B, 30);
+ B = A;
+ A = t;
+ }
+
+ //
+ // round 3
+ //
+ for (int j = 40; j <= 59; j++)
+ {
+ int t = rotateLeft(A, 5) + g(B, C, D) + E + X[j] + Y3;
+
+ E = D;
+ D = C;
+ C = rotateLeft(B, 30);
+ B = A;
+ A = t;
+ }
+
+ //
+ // round 4
+ //
+ for (int j = 60; j <= 79; j++)
+ {
+ int t = rotateLeft(A, 5) + h(B, C, D) + E + X[j] + Y4;
+
+ E = D;
+ D = C;
+ C = rotateLeft(B, 30);
+ B = A;
+ A = t;
+ }
+
+ H1 += A;
+ H2 += B;
+ H3 += C;
+ H4 += D;
+ H5 += E;
+
+ //
+ // reset the offset and clean out the word buffer.
+ //
+ xOff = 0;
+ for (int i = 0; i != X.length; i++)
+ {
+ X[i] = 0;
+ }
+ }
+}
+
+
+
+/**
+ * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this
+ * depends on your application - see PKCS1 Version 2 for details.
+ */
+class PKCS1Encoding
+ implements AsymmetricBlockCipher
+{
+ private static int HEADER_LENGTH = 10;
+
+ private Random random;
+ private AsymmetricBlockCipher engine;
+ private boolean forEncryption;
+ private boolean forPrivateKey;
+
+ public PKCS1Encoding(
+ AsymmetricBlockCipher cipher)
+ {
+ this.engine = cipher;
+ }
+
+ public AsymmetricBlockCipher getUnderlyingCipher()
+ {
+ return engine;
+ }
+
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ AsymmetricKeyParameter kParam;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.random = rParam.getRandom();
+ kParam = (AsymmetricKeyParameter)rParam.getParameters();
+ }
+ else
+ {
+ this.random = new Random();
+ kParam = (AsymmetricKeyParameter)param;
+ }
+
+ engine.init(forEncryption, kParam);
+
+ this.forPrivateKey = kParam.isPrivate();
+ this.forEncryption = forEncryption;
+ }
+
+ public int getInputBlockSize()
+ {
+ int baseBlockSize = engine.getInputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize - HEADER_LENGTH;
+ }
+ else
+ {
+ return baseBlockSize;
+ }
+ }
+
+ public int getOutputBlockSize()
+ {
+ int baseBlockSize = engine.getOutputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize;
+ }
+ else
+ {
+ return baseBlockSize - HEADER_LENGTH;
+ }
+ }
+
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forEncryption)
+ {
+ return encodeBlock(in, inOff, inLen);
+ }
+ else
+ {
+ return decodeBlock(in, inOff, inLen);
+ }
+ }
+
+ private byte[] encodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] block = new byte[engine.getInputBlockSize()];
+
+ if (forPrivateKey)
+ {
+ block[0] = 0x01; // type code 1
+
+ for (int i = 1; i != block.length - inLen - 1; i++)
+ {
+ block[i] = (byte)0xFF;
+ }
+ }
+ else
+ {
+ random.nextBytes(block); // random fill
+
+ block[0] = 0x02; // type code 2
+
+ //
+ // a zero byte marks the end of the padding, so all
+ // the pad bytes must be non-zero.
+ //
+ for (int i = 1; i != block.length - inLen - 1; i++)
+ {
+ while (block[i] == 0)
+ {
+ block[i] = (byte)random.nextInt();
+ }
+ }
+ }
+
+ block[block.length - inLen - 1] = 0x00; // mark the end of the padding
+ System.arraycopy(in, inOff, block, block.length - inLen, inLen);
+
+ return engine.processBlock(block, 0, block.length);
+ }
+
+ /**
+ * @exception InvalidCipherTextException if the decrypted block is not in PKCS1 format.
+ */
+ private byte[] decodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ byte[] block = engine.processBlock(in, inOff, inLen);
+
+ if (block.length < getOutputBlockSize())
+ {
+ throw new InvalidCipherTextException("block truncated");
+ }
+
+ if (block[0] != 1 && block[0] != 2)
+ {
+ throw new InvalidCipherTextException("unknown block type");
+ }
+
+ //
+ // find and extract the message block.
+ //
+ int start;
+
+ for (start = 1; start != block.length; start++)
+ {
+ if (block[start] == 0)
+ {
+ break;
+ }
+ }
+
+ start++; // data should start at the next byte
+
+ if (start >= block.length || start < HEADER_LENGTH)
+ {
+ throw new InvalidCipherTextException("no data in block");
+ }
+
+ byte[] result = new byte[block.length - start];
+
+ System.arraycopy(block, start, result, 0, result.length);
+
+ return result;
+ }
+}
+\r
+\r
+class RC4Engine implements StreamCipher\r
+{\r
+ private final static int STATE_LENGTH = 256;\r
+\r
+ /*\r
+ * variables to hold the state of the RC4 engine\r
+ * during encryption and decryption\r
+ */\r
+\r
+ private byte[] engineState = null;\r
+ private int x = 0;\r
+ private int y = 0;\r
+ private byte[] workingKey = null;\r
+\r
+ /**\r
+ * initialise a RC4 cipher.\r
+ *\r
+ * @param forEncryption whether or not we are for encryption.\r
+ * @param params the parameters required to set up the cipher.\r
+ * @exception IllegalArgumentException if the params argument is\r
+ * inappropriate.\r
+ */\r
+ public void init(\r
+ boolean forEncryption, \r
+ CipherParameters params\r
+ )\r
+ {\r
+ if (params instanceof KeyParameter)\r
+ {\r
+ /* \r
+ * RC4 encryption and decryption is completely\r
+ * symmetrical, so the 'forEncryption' is \r
+ * irrelevant.\r
+ */\r
+ workingKey = ((KeyParameter)params).getKey();\r
+ setKey(workingKey);\r
+\r
+ return;\r
+ }\r
+\r
+ throw new IllegalArgumentException("invalid parameter passed to RC4 init - " + params.getClass().getName());\r
+ }\r
+\r
+ public String getAlgorithmName()\r
+ {\r
+ return "RC4";\r
+ }\r
+\r
+ public byte returnByte(byte in)\r
+ {\r
+ x = (x + 1) & 0xff;\r
+ y = (engineState[x] + y) & 0xff;\r
+\r
+ // swap\r
+ byte tmp = engineState[x];\r
+ engineState[x] = engineState[y];\r
+ engineState[y] = tmp;\r
+\r
+ // xor\r
+ return (byte)(in ^ engineState[(engineState[x] + engineState[y]) & 0xff]);\r
+ }\r
+\r
+ public void processBytes(\r
+ byte[] in, \r
+ int inOff, \r
+ int len, \r
+ byte[] out, \r
+ int outOff\r
+ )\r
+ {\r
+ if ((inOff + len) > in.length)\r
+ {\r
+ throw new DataLengthException("input buffer too short");\r
+ }\r
+\r
+ if ((outOff + len) > out.length)\r
+ {\r
+ throw new DataLengthException("output buffer too short");\r
+ }\r
+\r
+ for (int i = 0; i < len ; i++)\r
+ {\r
+ x = (x + 1) & 0xff;\r
+ y = (engineState[x] + y) & 0xff;\r
+\r
+ // swap\r
+ byte tmp = engineState[x];\r
+ engineState[x] = engineState[y];\r
+ engineState[y] = tmp;\r
+\r
+ // xor\r
+ out[i+outOff] = (byte)(in[i + inOff]\r
+ ^ engineState[(engineState[x] + engineState[y]) & 0xff]);\r
+ }\r
+ }\r
+\r
+ public void reset()\r
+ {\r
+ setKey(workingKey);\r
+ }\r
+\r
+ // Private implementation\r
+\r
+ private void setKey(byte[] keyBytes)\r
+ {\r
+ workingKey = keyBytes;\r
+\r
+ // System.out.println("the key length is ; "+ workingKey.length);\r
+\r
+ x = 0;\r
+ y = 0;\r
+\r
+ if (engineState == null)\r
+ {\r
+ engineState = new byte[STATE_LENGTH];\r
+ }\r
+\r
+ // reset the state of the engine\r
+ for (int i=0; i < STATE_LENGTH; i++)\r
+ {\r
+ engineState[i] = (byte)i;\r
+ }\r
+ \r
+ int i1 = 0;\r
+ int i2 = 0;\r
+\r
+ for (int i=0; i < STATE_LENGTH; i++)\r
+ {\r
+ i2 = ((keyBytes[i1] & 0xff) + engineState[i] + i2) & 0xff;\r
+ // do the byte-swap inline\r
+ byte tmp = engineState[i];\r
+ engineState[i] = engineState[i2];\r
+ engineState[i2] = tmp;\r
+ i1 = (i1+1) % keyBytes.length; \r
+ }\r
+ }\r
+}\r
+
+
+
+/**
+ * this does your basic RSA algorithm.
+ */
+class RSAEngine
+ implements AsymmetricBlockCipher
+{
+ private RSAKeyParameters key;
+ private boolean forEncryption;
+
+ /**
+ * initialise the RSA engine.
+ *
+ * @param forEncryption true if we are encrypting, false otherwise.
+ * @param param the necessary RSA key parameters.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ this.key = (RSAKeyParameters)param;
+ this.forEncryption = forEncryption;
+ }
+
+ /**
+ * Return the maximum size for an input block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * encryption, and the same length as the key size on decryption.
+ *
+ * @return maximum size for an input block.
+ */
+ public int getInputBlockSize()
+ {
+ int bitSize = key.getModulus().bitLength();
+
+ if (forEncryption)
+ {
+ return (bitSize + 7) / 8 - 1;
+ }
+ else
+ {
+ return (bitSize + 7) / 8;
+ }
+ }
+
+ /**
+ * Return the maximum size for an output block to this engine.
+ * For RSA this is always one byte less than the key size on
+ * decryption, and the same length as the key size on encryption.
+ *
+ * @return maximum size for an output block.
+ */
+ public int getOutputBlockSize()
+ {
+ int bitSize = key.getModulus().bitLength();
+
+ if (forEncryption)
+ {
+ return (bitSize + 7) / 8;
+ }
+ else
+ {
+ return (bitSize + 7) / 8 - 1;
+ }
+ }
+
+ /**
+ * Process a single block using the basic RSA algorithm.
+ *
+ * @param in the input array.
+ * @param inOff the offset into the input buffer where the data starts.
+ * @param inLen the length of the data to be processed.
+ * @return the result of the RSA process.
+ * @exception DataLengthException the input block is too large.
+ */
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ {
+ if (inLen > (getInputBlockSize() + 1))
+ {
+ throw new DataLengthException("input too large for RSA cipher.\n");
+ }
+ else if (inLen == (getInputBlockSize() + 1) && (in[inOff] & 0x80) != 0)
+ {
+ throw new DataLengthException("input too large for RSA cipher.\n");
+ }
+
+ byte[] block;
+
+ if (inOff != 0 || inLen != in.length)
+ {
+ block = new byte[inLen];
+
+ System.arraycopy(in, inOff, block, 0, inLen);
+ }
+ else
+ {
+ block = in;
+ }
+
+ BigInteger input = new BigInteger(1, block);
+ byte[] output;
+
+ if (key instanceof RSAPrivateCrtKeyParameters)
+ {
+ //
+ // we have the extra factors, use the Chinese Remainder Theorem - the author
+ // wishes to express his thanks to Dirk Bonekaemper at rtsffm.com for
+ // advice regarding the expression of this.
+ //
+ RSAPrivateCrtKeyParameters crtKey = (RSAPrivateCrtKeyParameters)key;
+
+ BigInteger d = crtKey.getExponent();
+ BigInteger p = crtKey.getP();
+ BigInteger q = crtKey.getQ();
+ BigInteger dP = crtKey.getDP();
+ BigInteger dQ = crtKey.getDQ();
+ BigInteger qInv = crtKey.getQInv();
+
+ BigInteger mP, mQ, h, m;
+
+ // mP = ((input mod p) ^ dP)) mod p
+ mP = (input.remainder(p)).modPow(dP, p);
+
+ // mQ = ((input mod q) ^ dQ)) mod q
+ mQ = (input.remainder(q)).modPow(dQ, q);
+
+ // h = qInv * (mP - mQ) mod p
+ h = mP.subtract(mQ);
+ h = h.multiply(qInv);
+ h = h.mod(p); // mod (in Java) returns the positive residual
+
+ // m = h * q + mQ
+ m = h.multiply(q);
+ m = m.add(mQ);
+
+ output = m.toByteArray();
+ }
+ else
+ {
+ output = input.modPow(
+ key.getExponent(), key.getModulus()).toByteArray();
+ }
+
+ if (forEncryption)
+ {
+ if (output[0] == 0 && output.length > getOutputBlockSize()) // have ended up with an extra zero byte, copy down.
+ {
+ byte[] tmp = new byte[output.length - 1];
+
+ System.arraycopy(output, 1, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+
+ if (output.length < getOutputBlockSize()) // have ended up with less bytes than normal, lengthen
+ {
+ byte[] tmp = new byte[getOutputBlockSize()];
+
+ System.arraycopy(output, 0, tmp, tmp.length - output.length, output.length);
+
+ return tmp;
+ }
+ }
+ else
+ {
+ if (output[0] == 0) // have ended up with an extra zero byte, copy down.
+ {
+ byte[] tmp = new byte[output.length - 1];
+
+ System.arraycopy(output, 1, tmp, 0, tmp.length);
+
+ return tmp;
+ }
+ }
+ return output;
+ }
+}
+
+/**
+ * this exception is thrown whenever we find something we don't expect in a
+ * message.
+ */
+class InvalidCipherTextException
+ extends CryptoException
+{
+ /**
+ * base constructor.
+ */
+ public InvalidCipherTextException()
+ {
+ }
+
+ /**
+ * create a InvalidCipherTextException with the given message.
+ *
+ * @param message the message to be carried with the exception.
+ */
+ public InvalidCipherTextException(
+ String message)
+ {
+ super(message);
+ }
+}
+
+
+
+class DigestInputStream
+ extends FilterInputStream
+{
+ protected Digest digest;
+
+ public DigestInputStream(
+ InputStream stream,
+ Digest digest)
+ {
+ super(stream);
+ this.digest = digest;
+ }
+
+ public int read()
+ throws IOException
+ {
+ int b = in.read();
+
+ if (b >= 0)
+ {
+ digest.update((byte)b);
+ }
+ return b;
+ }
+
+ public int read(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ int n = in.read(b, off, len);
+ if (n > 0)
+ {
+ digest.update(b, off, n);
+ }
+ return n;
+ }
+
+ public Digest getDigest()
+ {
+ return digest;
+ }
+}
+
+
+
+class DigestOutputStream
+ extends FilterOutputStream
+{
+ protected Digest digest;
+
+ public DigestOutputStream(
+ OutputStream stream,
+ Digest digest)
+ {
+ super(stream);
+ this.digest = digest;
+ }
+
+ public void write(int b)
+ throws IOException
+ {
+ digest.update((byte)b);
+ out.write(b);
+ }
+
+ public void write(
+ byte[] b,
+ int off,
+ int len)
+ throws IOException
+ {
+ digest.update(b, off, len);
+ out.write(b, off, len);
+ }
+
+ public Digest getDigest()
+ {
+ return digest;
+ }
+}
+
+
+/**
+ * The base class for parameters to key generators.
+ */
+class KeyGenerationParameters
+{
+ private Random random;
+ private int strength;
+
+ /**
+ * initialise the generator with a source of randomness
+ * and a strength (in bits).
+ *
+ * @param random the random byte source.
+ * @param strength the size, in bits, of the keys we want to produce.
+ */
+ public KeyGenerationParameters(
+ Random random,
+ int strength)
+ {
+ this.random = random;
+ this.strength = strength;
+ }
+
+ /**
+ * return the random source associated with this
+ * generator.
+ *
+ * @return the generators random source.
+ */
+ public Random getRandom()
+ {
+ return random;
+ }
+
+ /**
+ * return the bit strength for keys produced by this generator,
+ *
+ * @return the strength of the keys this generator produces (in bits).
+ */
+ public int getStrength()
+ {
+ return strength;
+ }
+}
+
+
+class AsymmetricKeyParameter
+ implements CipherParameters
+{
+ boolean privateKey;
+
+ public AsymmetricKeyParameter(
+ boolean privateKey)
+ {
+ this.privateKey = privateKey;
+ }
+
+ public boolean isPrivate()
+ {
+ return privateKey;
+ }
+}
+
+
+class KeyParameter
+ implements CipherParameters
+{
+ private byte[] key;
+
+ public KeyParameter(
+ byte[] key)
+ {
+ this(key, 0, key.length);
+ }
+
+ public KeyParameter(
+ byte[] key,
+ int keyOff,
+ int keyLen)
+ {
+ this.key = new byte[keyLen];
+
+ System.arraycopy(key, keyOff, this.key, 0, keyLen);
+ }
+
+ public byte[] getKey()
+ {
+ return key;
+ }
+}
+
+
+
+class ParametersWithRandom
+ implements CipherParameters
+{
+ private Random random;
+ private CipherParameters parameters;
+
+ public ParametersWithRandom(
+ CipherParameters parameters,
+ Random random)
+ {
+ this.random = random;
+ this.parameters = parameters;
+ }
+
+ public ParametersWithRandom(
+ CipherParameters parameters)
+ {
+ this.random = null;
+ this.parameters = parameters;
+ }
+
+ public Random getRandom()
+ {
+ if (random == null)
+ {
+ random = new Random();
+ }
+ return random;
+ }
+
+ public CipherParameters getParameters()
+ {
+ return parameters;
+ }
+}
+
+
+
+class RSAKeyParameters
+ extends AsymmetricKeyParameter
+{
+ private BigInteger modulus;
+ private BigInteger exponent;
+
+ public RSAKeyParameters(
+ boolean isPrivate,
+ BigInteger modulus,
+ BigInteger exponent)
+ {
+ super(isPrivate);
+
+ this.modulus = modulus;
+ this.exponent = exponent;
+ }
+
+ public BigInteger getModulus()
+ {
+ return modulus;
+ }
+
+ public BigInteger getExponent()
+ {
+ return exponent;
+ }
+}
+
+
+class RSAPrivateCrtKeyParameters
+ extends RSAKeyParameters
+{
+ private BigInteger e;
+ private BigInteger p;
+ private BigInteger q;
+ private BigInteger dP;
+ private BigInteger dQ;
+ private BigInteger qInv;
+
+ /**
+ *
+ */
+ public RSAPrivateCrtKeyParameters(
+ BigInteger modulus,
+ BigInteger publicExponent,
+ BigInteger privateExponent,
+ BigInteger p,
+ BigInteger q,
+ BigInteger dP,
+ BigInteger dQ,
+ BigInteger qInv)
+ {
+ super(true, modulus, privateExponent);
+
+ this.e = publicExponent;
+ this.p = p;
+ this.q = q;
+ this.dP = dP;
+ this.dQ = dQ;
+ this.qInv = qInv;
+ }
+
+ public BigInteger getPublicExponent()
+ {
+ return e;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public BigInteger getDP()
+ {
+ return dP;
+ }
+
+ public BigInteger getDQ()
+ {
+ return dQ;
+ }
+
+ public BigInteger getQInv()
+ {
+ return qInv;
+ }
+}
+
+/**
+ * the foundation class for the exceptions thrown by the crypto packages.
+ */
+class RuntimeCryptoException
+ extends RuntimeException
+{
+ /**
+ * base constructor.
+ */
+ public RuntimeCryptoException()
+ {
+ }
+
+ /**
+ * create a RuntimeCryptoException with the given message.
+ *
+ * @param message the message to be carried with the exception.
+ */
+ public RuntimeCryptoException(
+ String message)
+ {
+ super(message);
+ }
+}
+
+/**
+ * a wrapper for block ciphers with a single byte block size, so that they
+ * can be treated like stream ciphers.
+ */
+class StreamBlockCipher
+ implements StreamCipher
+{
+ private BlockCipher cipher;
+
+ private byte[] oneByte = new byte[1];
+
+ /**
+ * basic constructor.
+ *
+ * @param cipher the block cipher to be wrapped.
+ * @exception IllegalArgumentException if the cipher has a block size other than
+ * one.
+ */
+ public StreamBlockCipher(
+ BlockCipher cipher)
+ {
+ if (cipher.getBlockSize() != 1)
+ {
+ throw new IllegalArgumentException("block cipher block size != 1.");
+ }
+
+ this.cipher = cipher;
+ }
+
+ /**
+ * initialise the underlying cipher.
+ *
+ * @param forEncryption true if we are setting up for encryption, false otherwise.
+ * @param param the necessary parameters for the underlying cipher to be initialised.
+ */
+ public void init(
+ boolean forEncryption,
+ CipherParameters params)
+ {
+ cipher.init(forEncryption, params);
+ }
+
+ /**
+ * return the name of the algorithm we are wrapping.
+ *
+ * @return the name of the algorithm we are wrapping.
+ */
+ public String getAlgorithmName()
+ {
+ return cipher.getAlgorithmName();
+ }
+
+ /**
+ * encrypt/decrypt a single byte returning the result.
+ *
+ * @param in the byte to be processed.
+ * @return the result of processing the input byte.
+ */
+ public byte returnByte(
+ byte in)
+ {
+ oneByte[0] = in;
+
+ cipher.processBlock(oneByte, 0, oneByte, 0);
+
+ return oneByte[0];
+ }
+
+ /**
+ * process a block of bytes from in putting the result into out.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset into the in array where the data to be processed starts.
+ * @param len the number of bytes to be processed.
+ * @param out the output buffer the processed bytes go into.
+ * @param outOff the offset into the output byte array the processed data stars at.
+ * @exception DataLengthException if the output buffer is too small.
+ */
+ public void processBytes(
+ byte[] in,
+ int inOff,
+ int len,
+ byte[] out,
+ int outOff)
+ throws DataLengthException
+ {
+ if (outOff + len > out.length)
+ {
+ throw new DataLengthException("output buffer too small in processBytes()");
+ }
+
+ for (int i = 0; i != len; i++)
+ {
+ cipher.processBlock(in, inOff + i, out, outOff + i);
+ }
+ }
+
+ /**
+ * reset the underlying cipher. This leaves it in the same state
+ * it was at after the last init (if there was one).
+ */
+ public void reset()
+ {
+ cipher.reset();
+ }
+}
+
+/**
+ * the interface stream ciphers conform to.
+ */
+interface StreamCipher
+{
+ /**
+ * Initialise the cipher.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param param the key and other data required by the cipher.
+ * @exception IllegalArgumentException if the params argument is
+ * inappropriate.
+ */
+ public void init(boolean forEncryption, CipherParameters params)
+ throws IllegalArgumentException;
+
+ /**
+ * Return the name of the algorithm the cipher implements.
+ *
+ * @return the name of the algorithm the cipher implements.
+ */
+ public String getAlgorithmName();
+
+ /**
+ * encrypt/decrypt a single byte returning the result.
+ *
+ * @param in the byte to be processed.
+ * @return the result of processing the input byte.
+ */
+ public byte returnByte(byte in);
+
+ /**
+ * process a block of bytes from in putting the result into out.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset into the in array where the data to be processed starts.
+ * @param len the number of bytes to be processed.
+ * @param out the output buffer the processed bytes go into.
+ * @param outOff the offset into the output byte array the processed data stars at.
+ * @exception DataLengthException if the output buffer is too small.
+ */
+ public void processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
+ throws DataLengthException;
+
+ /**
+ * reset the cipher. This leaves it in the same state
+ * it was at after the last init (if there was one).
+ */
+ public void reset();
+}
+
+class Base64
+{
+ private static final byte[] encodingTable =
+ {
+ (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+ (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+ (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+ (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+ (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+ (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+ (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
+ (byte)'v',
+ (byte)'w', (byte)'x', (byte)'y', (byte)'z',
+ (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6',
+ (byte)'7', (byte)'8', (byte)'9',
+ (byte)'+', (byte)'/'
+ };
+
+ /**
+ * encode the input data producong a base 64 encoded byte array.
+ *
+ * @return a byte array containing the base 64 encoded data.
+ */
+ public static byte[] encode(
+ byte[] data)
+ {
+ byte[] bytes;
+
+ int modulus = data.length % 3;
+ if (modulus == 0)
+ {
+ bytes = new byte[4 * data.length / 3];
+ }
+ else
+ {
+ bytes = new byte[4 * ((data.length / 3) + 1)];
+ }
+
+ int dataLength = (data.length - modulus);
+ int a1, a2, a3;
+ for (int i = 0, j = 0; i < dataLength; i += 3, j += 4)
+ {
+ a1 = data[i] & 0xff;
+ a2 = data[i + 1] & 0xff;
+ a3 = data[i + 2] & 0xff;
+
+ bytes[j] = encodingTable[(a1 >>> 2) & 0x3f];
+ bytes[j + 1] = encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f];
+ bytes[j + 2] = encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f];
+ bytes[j + 3] = encodingTable[a3 & 0x3f];
+ }
+
+ /*
+ * process the tail end.
+ */
+ int b1, b2, b3;
+ int d1, d2;
+
+ switch (modulus)
+ {
+ case 0: /* nothing left to do */
+ break;
+ case 1:
+ d1 = data[data.length - 1] & 0xff;
+ b1 = (d1 >>> 2) & 0x3f;
+ b2 = (d1 << 4) & 0x3f;
+
+ bytes[bytes.length - 4] = encodingTable[b1];
+ bytes[bytes.length - 3] = encodingTable[b2];
+ bytes[bytes.length - 2] = (byte)'=';
+ bytes[bytes.length - 1] = (byte)'=';
+ break;
+ case 2:
+ d1 = data[data.length - 2] & 0xff;
+ d2 = data[data.length - 1] & 0xff;
+
+ b1 = (d1 >>> 2) & 0x3f;
+ b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
+ b3 = (d2 << 2) & 0x3f;
+
+ bytes[bytes.length - 4] = encodingTable[b1];
+ bytes[bytes.length - 3] = encodingTable[b2];
+ bytes[bytes.length - 2] = encodingTable[b3];
+ bytes[bytes.length - 1] = (byte)'=';
+ break;
+ }
+
+ return bytes;
+ }
+
+ /*
+ * set up the decoding table.
+ */
+ private static final byte[] decodingTable;
+
+ static
+ {
+ decodingTable = new byte[128];
+
+ for (int i = 'A'; i <= 'Z'; i++)
+ {
+ decodingTable[i] = (byte)(i - 'A');
+ }
+
+ for (int i = 'a'; i <= 'z'; i++)
+ {
+ decodingTable[i] = (byte)(i - 'a' + 26);
+ }
+
+ for (int i = '0'; i <= '9'; i++)
+ {
+ decodingTable[i] = (byte)(i - '0' + 52);
+ }
+
+ decodingTable['+'] = 62;
+ decodingTable['/'] = 63;
+ }
+
+ /**
+ * decode the base 64 encoded input data.
+ *
+ * @return a byte array representing the decoded data.
+ */
+ public static byte[] decode(
+ byte[] data)
+ {
+ byte[] bytes;
+ byte b1, b2, b3, b4;
+
+ if (data[data.length - 2] == '=')
+ {
+ bytes = new byte[(((data.length / 4) - 1) * 3) + 1];
+ }
+ else if (data[data.length - 1] == '=')
+ {
+ bytes = new byte[(((data.length / 4) - 1) * 3) + 2];
+ }
+ else
+ {
+ bytes = new byte[((data.length / 4) * 3)];
+ }
+
+ for (int i = 0, j = 0; i < data.length - 4; i += 4, j += 3)
+ {
+ b1 = decodingTable[data[i]];
+ b2 = decodingTable[data[i + 1]];
+ b3 = decodingTable[data[i + 2]];
+ b4 = decodingTable[data[i + 3]];
+
+ bytes[j] = (byte)((b1 << 2) | (b2 >> 4));
+ bytes[j + 1] = (byte)((b2 << 4) | (b3 >> 2));
+ bytes[j + 2] = (byte)((b3 << 6) | b4);
+ }
+
+ if (data[data.length - 2] == '=')
+ {
+ b1 = decodingTable[data[data.length - 4]];
+ b2 = decodingTable[data[data.length - 3]];
+
+ bytes[bytes.length - 1] = (byte)((b1 << 2) | (b2 >> 4));
+ }
+ else if (data[data.length - 1] == '=')
+ {
+ b1 = decodingTable[data[data.length - 4]];
+ b2 = decodingTable[data[data.length - 3]];
+ b3 = decodingTable[data[data.length - 2]];
+
+ bytes[bytes.length - 2] = (byte)((b1 << 2) | (b2 >> 4));
+ bytes[bytes.length - 1] = (byte)((b2 << 4) | (b3 >> 2));
+ }
+ else
+ {
+ b1 = decodingTable[data[data.length - 4]];
+ b2 = decodingTable[data[data.length - 3]];
+ b3 = decodingTable[data[data.length - 2]];
+ b4 = decodingTable[data[data.length - 1]];
+
+ bytes[bytes.length - 3] = (byte)((b1 << 2) | (b2 >> 4));
+ bytes[bytes.length - 2] = (byte)((b2 << 4) | (b3 >> 2));
+ bytes[bytes.length - 1] = (byte)((b3 << 6) | b4);
+ }
+
+ return bytes;
+ }
+
+ /**
+ * decode the base 64 encoded String data.
+ *
+ * @return a byte array representing the decoded data.
+ */
+ public static byte[] decode(
+ String data)
+ {
+ byte[] bytes;
+ byte b1, b2, b3, b4;
+
+ if (data.charAt(data.length() - 2) == '=')
+ {
+ bytes = new byte[(((data.length() / 4) - 1) * 3) + 1];
+ }
+ else if (data.charAt(data.length() - 1) == '=')
+ {
+ bytes = new byte[(((data.length() / 4) - 1) * 3) + 2];
+ }
+ else
+ {
+ bytes = new byte[((data.length() / 4) * 3)];
+ }
+
+ for (int i = 0, j = 0; i < data.length() - 4; i += 4, j += 3)
+ {
+ b1 = decodingTable[data.charAt(i)];
+ b2 = decodingTable[data.charAt(i + 1)];
+ b3 = decodingTable[data.charAt(i + 2)];
+ b4 = decodingTable[data.charAt(i + 3)];
+
+ bytes[j] = (byte)((b1 << 2) | (b2 >> 4));
+ bytes[j + 1] = (byte)((b2 << 4) | (b3 >> 2));
+ bytes[j + 2] = (byte)((b3 << 6) | b4);
+ }
+
+ if (data.charAt(data.length() - 2) == '=')
+ {
+ b1 = decodingTable[data.charAt(data.length() - 4)];
+ b2 = decodingTable[data.charAt(data.length() - 3)];
+
+ bytes[bytes.length - 1] = (byte)((b1 << 2) | (b2 >> 4));
+ }
+ else if (data.charAt(data.length() - 1) == '=')
+ {
+ b1 = decodingTable[data.charAt(data.length() - 4)];
+ b2 = decodingTable[data.charAt(data.length() - 3)];
+ b3 = decodingTable[data.charAt(data.length() - 2)];
+
+ bytes[bytes.length - 2] = (byte)((b1 << 2) | (b2 >> 4));
+ bytes[bytes.length - 1] = (byte)((b2 << 4) | (b3 >> 2));
+ }
+ else
+ {
+ b1 = decodingTable[data.charAt(data.length() - 4)];
+ b2 = decodingTable[data.charAt(data.length() - 3)];
+ b3 = decodingTable[data.charAt(data.length() - 2)];
+ b4 = decodingTable[data.charAt(data.length() - 1)];
+
+ bytes[bytes.length - 3] = (byte)((b1 << 2) | (b2 >> 4));
+ bytes[bytes.length - 2] = (byte)((b2 << 4) | (b3 >> 2));
+ bytes[bytes.length - 1] = (byte)((b3 << 6) | b4);
+ }
+
+ return bytes;
+ }
+}