aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/lowagie/bc
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/lowagie/bc')
-rw-r--r--src/main/java/com/lowagie/bc/asn1/ASN1Encodable.java65
-rw-r--r--src/main/java/com/lowagie/bc/asn1/ASN1EncodableVector.java9
-rw-r--r--src/main/java/com/lowagie/bc/asn1/ASN1InputStream.java450
-rw-r--r--src/main/java/com/lowagie/bc/asn1/ASN1Null.java33
-rw-r--r--src/main/java/com/lowagie/bc/asn1/ASN1OctetString.java141
-rw-r--r--src/main/java/com/lowagie/bc/asn1/ASN1OutputStream.java36
-rw-r--r--src/main/java/com/lowagie/bc/asn1/ASN1Sequence.java185
-rw-r--r--src/main/java/com/lowagie/bc/asn1/ASN1Set.java184
-rw-r--r--src/main/java/com/lowagie/bc/asn1/ASN1TaggedObject.java146
-rw-r--r--src/main/java/com/lowagie/bc/asn1/BERConstructedOctetString.java168
-rw-r--r--src/main/java/com/lowagie/bc/asn1/BERConstructedSequence.java34
-rw-r--r--src/main/java/com/lowagie/bc/asn1/BERInputStream.java197
-rw-r--r--src/main/java/com/lowagie/bc/asn1/BERNull.java30
-rw-r--r--src/main/java/com/lowagie/bc/asn1/BEROutputStream.java36
-rw-r--r--src/main/java/com/lowagie/bc/asn1/BERSequence.java59
-rw-r--r--src/main/java/com/lowagie/bc/asn1/BERSet.java59
-rw-r--r--src/main/java/com/lowagie/bc/asn1/BERTaggedObject.java119
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERApplicationSpecific.java67
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERBMPString.java121
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERBitString.java268
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERBoolean.java103
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERConstructedSequence.java53
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERConstructedSet.java75
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DEREncodable.java6
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DEREncodableVector.java30
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DEREnumerated.java108
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERGeneralString.java86
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERGeneralizedTime.java188
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERIA5String.java123
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERInputStream.java258
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERInteger.java129
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERNull.java34
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERNumericString.java123
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERObject.java15
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERObjectIdentifier.java170
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DEROctetString.java29
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DEROutputStream.java81
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERPrintableString.java123
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERSequence.java67
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERSet.java70
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERString.java9
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERT61String.java116
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERTaggedObject.java88
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERTags.java36
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERUTCTime.java183
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERUTF8String.java177
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERUniversalString.java110
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERUnknownTag.java73
-rw-r--r--src/main/java/com/lowagie/bc/asn1/DERVisibleString.java116
-rw-r--r--src/main/java/com/lowagie/bc/asn1/OIDTokenizer.java48
50 files changed, 5234 insertions, 0 deletions
diff --git a/src/main/java/com/lowagie/bc/asn1/ASN1Encodable.java b/src/main/java/com/lowagie/bc/asn1/ASN1Encodable.java
new file mode 100644
index 0000000..01e0bdc
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/ASN1Encodable.java
@@ -0,0 +1,65 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Abstract Syntax Notation One (ASN.1) is a formal language for abstractly describing messages to be exchanged between distributed computer systems.
+ */
+public abstract class ASN1Encodable
+ implements DEREncodable
+{
+ /**
+ * Encodes the ASN1Encodable object.
+ * @return an encoded bytearray
+ * @throws IOException
+ */
+ public byte[] getEncoded()
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ aOut.writeObject(this);
+
+ return bOut.toByteArray();
+ }
+
+ /**
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode()
+ {
+ return this.getDERObject().hashCode();
+ }
+
+ /**
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DEREncodable))
+ {
+ return false;
+ }
+
+ DEREncodable other = (DEREncodable)o;
+
+ return this.getDERObject().equals(other.getDERObject());
+ }
+
+ /**
+ * @see com.lowagie.bc.asn1.DEREncodable#getDERObject()
+ */
+ public DERObject getDERObject()
+ {
+ return this.toASN1Object();
+ }
+
+ /**
+ * Abstract method that returns the object as an ASN1 object.
+ * @return an encodable object
+ */
+ public abstract DERObject toASN1Object();
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/ASN1EncodableVector.java b/src/main/java/com/lowagie/bc/asn1/ASN1EncodableVector.java
new file mode 100644
index 0000000..b702437
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/ASN1EncodableVector.java
@@ -0,0 +1,9 @@
+package com.lowagie.bc.asn1;
+
+/**
+ * the parent class for this will eventually disappear. Use this one!
+ */
+public class ASN1EncodableVector
+ extends DEREncodableVector
+{
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/ASN1InputStream.java b/src/main/java/com/lowagie/bc/asn1/ASN1InputStream.java
new file mode 100644
index 0000000..1980af3
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/ASN1InputStream.java
@@ -0,0 +1,450 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Vector;
+
+/**
+ * a general purpose ASN.1 decoder - note: this class differs from the
+ * others in that it returns null after it has read the last object in
+ * the stream. If an ASN.1 NULL is encountered a DER/BER Null object is
+ * returned.
+ */
+public class ASN1InputStream
+ extends DERInputStream
+{
+ private DERObject END_OF_STREAM = new DERObject() {
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ throw new IOException("Eeek!");
+ }
+
+ };
+ boolean eofFound = false;
+
+ public ASN1InputStream(
+ 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;
+ int len;
+
+ if (left == 0)
+ {
+ return;
+ }
+
+ while ((len = read(bytes, bytes.length - left, left)) > 0)
+ {
+ if ((left -= len) == 0)
+ {
+ return;
+ }
+ }
+
+ if (left != 0)
+ {
+ throw new EOFException("EOF encountered in middle of object");
+ }
+ }
+
+ /**
+ * build an object given its tag and a byte stream to construct it
+ * from.
+ */
+ protected DERObject buildObject(
+ int tag,
+ byte[] bytes)
+ throws IOException
+ {
+ if ((tag & APPLICATION) != 0)
+ {
+ return new DERApplicationSpecific(tag, bytes);
+ }
+
+ switch (tag)
+ {
+ case NULL:
+ return new DERNull();
+ case SEQUENCE | CONSTRUCTED:
+ ByteArrayInputStream bIn = new ByteArrayInputStream(bytes);
+ ASN1InputStream aIn = new ASN1InputStream(bIn);
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ DERObject obj = aIn.readObject();
+
+ while (obj != null)
+ {
+ v.add(obj);
+ obj = aIn.readObject();
+ }
+
+ return new DERSequence(v);
+ case SET | CONSTRUCTED:
+ bIn = new ByteArrayInputStream(bytes);
+ aIn = new ASN1InputStream(bIn);
+ v = new ASN1EncodableVector();
+
+ obj = aIn.readObject();
+
+ while (obj != null)
+ {
+ v.add(obj);
+ obj = aIn.readObject();
+ }
+
+ return new DERSet(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 GENERAL_STRING:
+ return new DERGeneralString(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)
+ {
+ int tagNo = tag & 0x1f;
+
+ if (tagNo == 0x1f)
+ {
+ int idx = 0;
+
+ tagNo = 0;
+
+ while ((bytes[idx] & 0x80) != 0)
+ {
+ tagNo |= (bytes[idx++] & 0x7f);
+ tagNo <<= 7;
+ }
+
+ tagNo |= (bytes[idx] & 0x7f);
+
+ byte[] tmp = bytes;
+
+ bytes = new byte[tmp.length - (idx + 1)];
+ System.arraycopy(tmp, idx + 1, bytes, 0, bytes.length);
+ }
+
+ if (bytes.length == 0) // empty tag!
+ {
+ if ((tag & CONSTRUCTED) == 0)
+ {
+ return new DERTaggedObject(false, tagNo, new DERNull());
+ }
+ else
+ {
+ return new DERTaggedObject(false, tagNo, new DERSequence());
+ }
+ }
+
+ //
+ // simple type - implicit... return an octet string
+ //
+ if ((tag & CONSTRUCTED) == 0)
+ {
+ return new DERTaggedObject(false, tagNo, new DEROctetString(bytes));
+ }
+
+ bIn = new ByteArrayInputStream(bytes);
+ aIn = new ASN1InputStream(bIn);
+
+ DEREncodable dObj = aIn.readObject();
+
+ //
+ // explicitly tagged (probably!) - if it isn't we'd have to
+ // tell from the context
+ //
+ if (aIn.available() == 0)
+ {
+ return new DERTaggedObject(tagNo, dObj);
+ }
+
+ //
+ // another implicit object, we'll create a sequence...
+ //
+ v = new ASN1EncodableVector();
+
+ while (dObj != null)
+ {
+ v.add(dObj);
+ dObj = aIn.readObject();
+ }
+
+ return new DERTaggedObject(false, tagNo, new DERSequence(v));
+ }
+
+ return new DERUnknownTag(tag, bytes);
+ }
+ }
+
+ /**
+ * 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)
+ {
+ if (eofFound)
+ {
+ throw new EOFException("attempt to read past end of file.");
+ }
+
+ eofFound = true;
+
+ return null;
+ }
+
+ int length = readLength();
+
+ if (length < 0) // indefinite length method
+ {
+ switch (tag)
+ {
+ case NULL:
+ return new BERNull();
+ case SEQUENCE | CONSTRUCTED:
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ for (;;)
+ {
+ DERObject obj = readObject();
+
+ if (obj == END_OF_STREAM)
+ {
+ break;
+ }
+
+ v.add(obj);
+ }
+ return new BERSequence(v);
+ case SET | CONSTRUCTED:
+ v = new ASN1EncodableVector();
+
+ for (;;)
+ {
+ DERObject obj = readObject();
+
+ if (obj == END_OF_STREAM)
+ {
+ break;
+ }
+
+ v.add(obj);
+ }
+ return new BERSet(v);
+ case OCTET_STRING | CONSTRUCTED:
+ return buildConstructedOctetString();
+ default:
+ //
+ // with tagged object tag number is bottom 5 bits
+ //
+ if ((tag & TAGGED) != 0)
+ {
+ int tagNo = tag & 0x1f;
+
+ if (tagNo == 0x1f)
+ {
+ int b = read();
+
+ tagNo = 0;
+
+ while ((b >= 0) && ((b & 0x80) != 0))
+ {
+ tagNo |= (b & 0x7f);
+ tagNo <<= 7;
+ b = read();
+ }
+
+ tagNo |= (b & 0x7f);
+ }
+
+ //
+ // simple type - implicit... return an octet string
+ //
+ if ((tag & CONSTRUCTED) == 0)
+ {
+ byte[] bytes = readIndefiniteLengthFully();
+
+ return new BERTaggedObject(false, tagNo, new DEROctetString(bytes));
+ }
+
+ //
+ // either constructed or explicitly tagged
+ //
+ DERObject dObj = readObject();
+
+ if (dObj == END_OF_STREAM) // empty tag!
+ {
+ return new DERTaggedObject(tagNo);
+ }
+
+ 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(tagNo, dObj);
+ }
+
+ //
+ // another implicit object, we'll create a sequence...
+ //
+ v = new ASN1EncodableVector();
+
+ v.add(dObj);
+
+ do
+ {
+ v.add(next);
+ next = readObject();
+ }
+ while (next != END_OF_STREAM);
+
+ return new BERTaggedObject(false, tagNo, new BERSequence(v));
+ }
+
+ 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);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/ASN1Null.java b/src/main/java/com/lowagie/bc/asn1/ASN1Null.java
new file mode 100644
index 0000000..4d490d0
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/ASN1Null.java
@@ -0,0 +1,33 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * A NULL object.
+ */
+public abstract class ASN1Null
+ extends DERObject
+{
+ public ASN1Null()
+ {
+ }
+
+ public int hashCode()
+ {
+ return 0;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof ASN1Null))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ abstract void encode(DEROutputStream out)
+ throws IOException;
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/ASN1OctetString.java b/src/main/java/com/lowagie/bc/asn1/ASN1OctetString.java
new file mode 100644
index 0000000..bdd6a1b
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/ASN1OctetString.java
@@ -0,0 +1,141 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+
+public 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;
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/ASN1OutputStream.java b/src/main/java/com/lowagie/bc/asn1/ASN1OutputStream.java
new file mode 100644
index 0000000..36a5d39
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/ASN1OutputStream.java
@@ -0,0 +1,36 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public 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");
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/ASN1Sequence.java b/src/main/java/com/lowagie/bc/asn1/ASN1Sequence.java
new file mode 100644
index 0000000..167d45f
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/ASN1Sequence.java
@@ -0,0 +1,185 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+
+public 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())
+ {
+ if (obj instanceof BERTaggedObject)
+ {
+ return new BERSequence(obj.getObject());
+ }
+ else
+ {
+ return new DERSequence(obj.getObject());
+ }
+ }
+ else
+ {
+ 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 index 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())
+ {
+ Object o = e.nextElement();
+
+ if (o != null)
+ {
+ hashCode ^= o.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())
+ {
+ Object o1 = s1.nextElement();
+ Object o2 = s2.nextElement();
+
+ if (o1 != null && o2 != null)
+ {
+ if (!o1.equals(o2))
+ {
+ return false;
+ }
+ }
+ else if (o1 == null && o2 == null)
+ {
+ continue;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected void addObject(
+ DEREncodable obj)
+ {
+ seq.addElement(obj);
+ }
+
+ abstract void encode(DEROutputStream out)
+ throws IOException;
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/ASN1Set.java b/src/main/java/com/lowagie/bc/asn1/ASN1Set.java
new file mode 100644
index 0000000..f0d6c97
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/ASN1Set.java
@@ -0,0 +1,184 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+
+abstract public 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
+ {
+ if (obj.getObject() instanceof ASN1Set)
+ {
+ return (ASN1Set)obj.getObject();
+ }
+
+ //
+ // in this case the parser returns a sequence, convert it
+ // into a set.
+ //
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ 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 index 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;
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/ASN1TaggedObject.java b/src/main/java/com/lowagie/bc/asn1/ASN1TaggedObject.java
new file mode 100644
index 0000000..c70cb42
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/ASN1TaggedObject.java
@@ -0,0 +1,146 @@
+package com.lowagie.bc.asn1;
+
+import java.io.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).
+ */
+public abstract class ASN1TaggedObject
+ extends DERObject
+{
+ int tagNo;
+ boolean empty = false;
+ boolean explicit = true;
+ DEREncodable obj = null;
+
+ static public ASN1TaggedObject getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ if (explicit)
+ {
+ return (ASN1TaggedObject)obj.getObject();
+ }
+
+ throw new IllegalArgumentException("implicitly tagged tagged object");
+ }
+
+ /**
+ * @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 hashCode()
+ {
+ int code = tagNo;
+
+ if (obj != null)
+ {
+ code ^= obj.hashCode();
+ }
+
+ return code;
+ }
+
+ 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;
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/BERConstructedOctetString.java b/src/main/java/com/lowagie/bc/asn1/BERConstructedOctetString.java
new file mode 100644
index 0000000..5c44187
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/BERConstructedOctetString.java
@@ -0,0 +1,168 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+
+public 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)
+ {
+ return generateOcts().elements();
+ }
+
+ 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];
+
+ System.arraycopy(string, start, nStr, 0, nStr.length);
+
+ vec.addElement(new DEROctetString(nStr));
+ start = end + 1;
+ }
+ end++;
+ }
+
+ byte[] nStr = new byte[string.length - start];
+
+ System.arraycopy(string, start, nStr, 0, nStr.length);
+
+ 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);
+
+ //
+ // write out the octet array
+ //
+ if (octs != null)
+ {
+ for (int i = 0; i != octs.size(); i++)
+ {
+ out.writeObject(octs.elementAt(i));
+ }
+ }
+ else
+ {
+ int start = 0;
+ int end = 0;
+
+ while ((end + 1) < string.length)
+ {
+ if (string[end] == 0 && string[end + 1] == 0)
+ {
+ byte[] nStr = new byte[end - start + 1];
+
+ System.arraycopy(string, start, nStr, 0, nStr.length);
+
+ out.writeObject(new DEROctetString(nStr));
+ start = end + 1;
+ }
+ end++;
+ }
+
+ byte[] nStr = new byte[string.length - start];
+
+ System.arraycopy(string, start, nStr, 0, nStr.length);
+
+ out.writeObject(new DEROctetString(nStr));
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+ else
+ {
+ super.encode(out);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/BERConstructedSequence.java b/src/main/java/com/lowagie/bc/asn1/BERConstructedSequence.java
new file mode 100644
index 0000000..2d5d081
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/BERConstructedSequence.java
@@ -0,0 +1,34 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+public 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);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/BERInputStream.java b/src/main/java/com/lowagie/bc/asn1/BERInputStream.java
new file mode 100644
index 0000000..395c1e7
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/BERInputStream.java
@@ -0,0 +1,197 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Vector;
+
+public 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:
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ 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);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/BERNull.java b/src/main/java/com/lowagie/bc/asn1/BERNull.java
new file mode 100644
index 0000000..868691f
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/BERNull.java
@@ -0,0 +1,30 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * A BER NULL object.
+ */
+public class BERNull
+ extends DERNull
+{
+ public BERNull()
+ {
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ if (out instanceof ASN1OutputStream || out instanceof BEROutputStream)
+ {
+ out.write(NULL);
+ out.write(0);
+ out.write(0);
+ }
+ else
+ {
+ super.encode(out);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/BEROutputStream.java b/src/main/java/com/lowagie/bc/asn1/BEROutputStream.java
new file mode 100644
index 0000000..9ae1126
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/BEROutputStream.java
@@ -0,0 +1,36 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public 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");
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/BERSequence.java b/src/main/java/com/lowagie/bc/asn1/BERSequence.java
new file mode 100644
index 0000000..5654568
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/BERSequence.java
@@ -0,0 +1,59 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+public class BERSequence
+ extends DERSequence
+{
+ /**
+ * create an empty sequence
+ */
+ public BERSequence()
+ {
+ }
+
+ /**
+ * create a sequence containing one object
+ */
+ public BERSequence(
+ DEREncodable obj)
+ {
+ super(obj);
+ }
+
+ /**
+ * create a sequence containing a vector of objects.
+ */
+ public BERSequence(
+ DEREncodableVector v)
+ {
+ super(v);
+ }
+
+ /*
+ */
+ 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);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/BERSet.java b/src/main/java/com/lowagie/bc/asn1/BERSet.java
new file mode 100644
index 0000000..0f608dc
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/BERSet.java
@@ -0,0 +1,59 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+public 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);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/BERTaggedObject.java b/src/main/java/com/lowagie/bc/asn1/BERTaggedObject.java
new file mode 100644
index 0000000..203bc22
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/BERTaggedObject.java
@@ -0,0 +1,119 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+/**
+ * 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).
+ */
+public 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 ASN1OctetString)
+ {
+ Enumeration e;
+
+ if (obj instanceof BERConstructedOctetString)
+ {
+ e = ((BERConstructedOctetString)obj).getObjects();
+ }
+ else
+ {
+ ASN1OctetString octs = (ASN1OctetString)obj;
+ BERConstructedOctetString berO = new BERConstructedOctetString(octs.getOctets());
+
+ e = berO.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);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERApplicationSpecific.java b/src/main/java/com/lowagie/bc/asn1/DERApplicationSpecific.java
new file mode 100644
index 0000000..2ef11d4
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERApplicationSpecific.java
@@ -0,0 +1,67 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Base class for an application specific object
+ */
+public class DERApplicationSpecific
+ extends DERObject
+{
+ private int tag;
+ private byte[] octets;
+
+ public DERApplicationSpecific(
+ int tag,
+ byte[] octets)
+ {
+ this.tag = tag;
+ this.octets = octets;
+ }
+
+ public DERApplicationSpecific(
+ int tag,
+ DEREncodable object)
+ throws IOException
+ {
+ this.tag = tag | DERTags.CONSTRUCTED;
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DEROutputStream dos = new DEROutputStream(baos);
+
+ dos.writeObject(object);
+
+ this.octets = baos.toByteArray();
+ }
+
+ public boolean isConstructed()
+ {
+ return (tag & DERTags.CONSTRUCTED) != 0;
+ }
+
+ public byte[] getContents()
+ {
+ return octets;
+ }
+
+ public int getApplicationTag()
+ {
+ return tag & 0x1F;
+ }
+
+ public DERObject getObject()
+ throws IOException
+ {
+ return new ASN1InputStream(new ByteArrayInputStream(getContents())).readObject();
+ }
+
+ /* (non-Javadoc)
+ * @see org.bouncycastle.asn1.DERObject#encode(org.bouncycastle.asn1.DEROutputStream)
+ */
+ void encode(DEROutputStream out) throws IOException
+ {
+ out.writeEncoded(DERTags.APPLICATION | tag, octets);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERBMPString.java b/src/main/java/com/lowagie/bc/asn1/DERBMPString.java
new file mode 100644
index 0000000..2e712e8
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERBMPString.java
@@ -0,0 +1,121 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * DER BMPString object.
+ */
+public 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;
+ }
+
+ DERBMPString s = (DERBMPString)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);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERBitString.java b/src/main/java/com/lowagie/bc/asn1/DERBitString.java
new file mode 100644
index 0000000..a5acd39
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERBitString.java
@@ -0,0 +1,268 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+public class DERBitString
+ extends DERObject
+ implements DERString
+{
+ private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+ protected byte[] data;
+ protected int padBits;
+
+ /**
+ * return the correct number of pad bits for a bit string defined in
+ * a 32 bit constant
+ */
+ static protected int getPadBits(
+ int bitString)
+ {
+ int val = 0;
+ for (int i = 3; i >= 0; i--)
+ {
+ //
+ // this may look a little odd, but if it isn't done like this pre jdk1.2
+ // JVM's break!
+ //
+ if (i != 0)
+ {
+ if ((bitString >> (i * 8)) != 0)
+ {
+ val = (bitString >> (i * 8)) & 0xFF;
+ break;
+ }
+ }
+ else
+ {
+ if (bitString != 0)
+ {
+ val = bitString & 0xFF;
+ break;
+ }
+ }
+ }
+
+ if (val == 0)
+ {
+ return 7;
+ }
+
+
+ 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 32 bit constant
+ */
+ static protected byte[] getBytes(int bitString)
+ {
+ int bytes = 4;
+ for (int i = 3; i >= 1; i--)
+ {
+ if ((bitString & (0xFF << (i * 8))) != 0)
+ {
+ break;
+ }
+ bytes--;
+ }
+
+ byte[] result = new byte[bytes];
+ for (int i = 0; i < bytes; i++)
+ {
+ result[i] = (byte) ((bitString >> (i * 8)) & 0xFF);
+ }
+
+ return result;
+ }
+
+ /**
+ * 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 int hashCode()
+ {
+ int value = 0;
+
+ for (int i = 0; i != data.length; i++)
+ {
+ value ^= (data[i] & 0xff) << (i % 4);
+ }
+
+ return value;
+ }
+
+ 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);
+ }
+
+ public String getString()
+ {
+ StringBuffer buf = new StringBuffer("#");
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ try
+ {
+ aOut.writeObject(this);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("internal error encoding BitString");
+ }
+
+ byte[] string = bOut.toByteArray();
+
+ for (int i = 0; i != string.length; i++)
+ {
+ buf.append(table[(string[i] >>> 4) % 0xf]);
+ buf.append(table[string[i] & 0xf]);
+ }
+
+ return buf.toString();
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERBoolean.java b/src/main/java/com/lowagie/bc/asn1/DERBoolean.java
new file mode 100644
index 0000000..5b6d1bf
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERBoolean.java
@@ -0,0 +1,103 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+public 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);
+ }
+
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERConstructedSequence.java b/src/main/java/com/lowagie/bc/asn1/DERConstructedSequence.java
new file mode 100644
index 0000000..607e53b
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERConstructedSequence.java
@@ -0,0 +1,53 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+
+/**
+ * //@deprecated use DERSequence.
+ */
+public 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);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERConstructedSet.java b/src/main/java/com/lowagie/bc/asn1/DERConstructedSet.java
new file mode 100644
index 0000000..7f52ca1
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERConstructedSet.java
@@ -0,0 +1,75 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+
+public 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);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DEREncodable.java b/src/main/java/com/lowagie/bc/asn1/DEREncodable.java
new file mode 100644
index 0000000..322ffc0
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DEREncodable.java
@@ -0,0 +1,6 @@
+package com.lowagie.bc.asn1;
+
+public interface DEREncodable
+{
+ public DERObject getDERObject();
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DEREncodableVector.java b/src/main/java/com/lowagie/bc/asn1/DEREncodableVector.java
new file mode 100644
index 0000000..24d4c51
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DEREncodableVector.java
@@ -0,0 +1,30 @@
+package com.lowagie.bc.asn1;
+
+import java.util.Vector;
+
+/**
+ * a general class for building up a vector of DER encodable objects -
+ * this will eventually be superceded by ASN1EncodableVector so you should
+ * use that class in preference.
+ */
+public 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();
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DEREnumerated.java b/src/main/java/com/lowagie/bc/asn1/DEREnumerated.java
new file mode 100644
index 0000000..36b3f98
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DEREnumerated.java
@@ -0,0 +1,108 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+public 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;
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERGeneralString.java b/src/main/java/com/lowagie/bc/asn1/DERGeneralString.java
new file mode 100644
index 0000000..641620b
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERGeneralString.java
@@ -0,0 +1,86 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+public class DERGeneralString
+ extends DERObject implements DERString
+{
+ private String string;
+
+ public static DERGeneralString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERGeneralString)
+ {
+ return (DERGeneralString) obj;
+ }
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERGeneralString(((ASN1OctetString) obj).getOctets());
+ }
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject) obj).getObject());
+ }
+ throw new IllegalArgumentException("illegal object in getInstance: "
+ + obj.getClass().getName());
+ }
+
+ public static DERGeneralString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ public DERGeneralString(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);
+ }
+
+ public DERGeneralString(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(GENERAL_STRING, this.getOctets());
+ }
+
+ public int hashCode()
+ {
+ return this.getString().hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof DERGeneralString))
+ {
+ return false;
+ }
+ DERGeneralString s = (DERGeneralString) o;
+ return this.getString().equals(s.getString());
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERGeneralizedTime.java b/src/main/java/com/lowagie/bc/asn1/DERGeneralizedTime.java
new file mode 100644
index 0000000..28cab51
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERGeneralizedTime.java
@@ -0,0 +1,188 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.SimpleTimeZone;
+
+/**
+ * Generalized time object.
+ */
+public 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.charAt(time.length() - 1) == 'Z')
+ {
+ return time.substring(0, time.length() - 1) + "GMT+00:00";
+ }
+ else
+ {
+ int signPos = time.length() - 5;
+ char sign = time.charAt(signPos);
+ if (sign == '-' || sign == '+')
+ {
+ return time.substring(0, signPos)
+ + "GMT"
+ + time.substring(signPos, signPos + 3)
+ + ":"
+ + time.substring(signPos + 3);
+ }
+ else
+ {
+ signPos = time.length() - 3;
+ sign = time.charAt(signPos);
+ if (sign == '-' || sign == '+')
+ {
+ return time.substring(0, signPos)
+ + "GMT"
+ + time.substring(signPos)
+ + ":00";
+ }
+ }
+ }
+
+ return time;
+ }
+
+ public Date getDate()
+ throws ParseException
+ {
+ SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
+
+ dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
+
+ return dateF.parse(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);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERIA5String.java b/src/main/java/com/lowagie/bc/asn1/DERIA5String.java
new file mode 100644
index 0000000..d60cdc6
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERIA5String.java
@@ -0,0 +1,123 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * DER IA5String object - this is an ascii string.
+ */
+public 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());
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERInputStream.java b/src/main/java/com/lowagie/bc/asn1/DERInputStream.java
new file mode 100644
index 0000000..3148a3f
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERInputStream.java
@@ -0,0 +1,258 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Don't use this class. It will eventually disappear, use ASN1InputStream.
+ * <br>
+ * This class is scheduled for removal.
+ */
+public 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 > 0)
+ {
+ int l = read(bytes, bytes.length - left, left);
+
+ if (l < 0)
+ {
+ throw new EOFException("unexpected end of stream");
+ }
+
+ left -= l;
+ }
+ }
+
+ /**
+ * 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);
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ 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 GENERAL_STRING:
+ return new DERGeneralString(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!
+ {
+ if ((tag & CONSTRUCTED) == 0)
+ {
+ return new DERTaggedObject(false, tag & 0x1f, new DERNull());
+ }
+ else
+ {
+ 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);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERInteger.java b/src/main/java/com/lowagie/bc/asn1/DERInteger.java
new file mode 100644
index 0000000..9df0d62
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERInteger.java
@@ -0,0 +1,129 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+public 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 int hashCode()
+ {
+ int value = 0;
+
+ for (int i = 0; i != bytes.length; i++)
+ {
+ value ^= (bytes[i] & 0xff) << (i % 4);
+ }
+
+ return value;
+ }
+
+ 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;
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERNull.java b/src/main/java/com/lowagie/bc/asn1/DERNull.java
new file mode 100644
index 0000000..3ce810a
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERNull.java
@@ -0,0 +1,34 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * A NULL object.
+ */
+public class DERNull
+ extends ASN1Null
+{
+ byte[] zeroBytes = new byte[0];
+
+ public DERNull()
+ {
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(NULL, zeroBytes);
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERNull))
+ {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERNumericString.java b/src/main/java/com/lowagie/bc/asn1/DERNumericString.java
new file mode 100644
index 0000000..6d58c6c
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERNumericString.java
@@ -0,0 +1,123 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * DER NumericString object - this is an ascii string of characters {0,1,2,3,4,5,6,7,8,9, }.
+ */
+public class DERNumericString
+ extends DERObject
+ implements DERString
+{
+ String string;
+
+ /**
+ * return a Numeric string from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERNumericString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERNumericString)
+ {
+ return (DERNumericString)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERNumericString(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an Numeric 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 DERNumericString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * basic constructor - with bytes.
+ */
+ public DERNumericString(
+ 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 DERNumericString(
+ 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(NUMERIC_STRING, this.getOctets());
+ }
+
+ public int hashCode()
+ {
+ return this.getString().hashCode();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DERNumericString))
+ {
+ return false;
+ }
+
+ DERNumericString s = (DERNumericString)o;
+
+ return this.getString().equals(s.getString());
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERObject.java b/src/main/java/com/lowagie/bc/asn1/DERObject.java
new file mode 100644
index 0000000..abb86d0
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERObject.java
@@ -0,0 +1,15 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+public abstract class DERObject
+ implements DERTags, DEREncodable
+{
+ public DERObject getDERObject()
+ {
+ return this;
+ }
+
+ abstract void encode(DEROutputStream out)
+ throws IOException;
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERObjectIdentifier.java b/src/main/java/com/lowagie/bc/asn1/DERObjectIdentifier.java
new file mode 100644
index 0000000..04c2d86
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERObjectIdentifier.java
@@ -0,0 +1,170 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public 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)
+ {
+ 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);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DEROctetString.java b/src/main/java/com/lowagie/bc/asn1/DEROctetString.java
new file mode 100644
index 0000000..46ab32d
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DEROctetString.java
@@ -0,0 +1,29 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+public 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);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DEROutputStream.java b/src/main/java/com/lowagie/bc/asn1/DEROutputStream.java
new file mode 100644
index 0000000..457cae4
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DEROutputStream.java
@@ -0,0 +1,81 @@
+package com.lowagie.bc.asn1;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public 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");
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERPrintableString.java b/src/main/java/com/lowagie/bc/asn1/DERPrintableString.java
new file mode 100644
index 0000000..ac3f588
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERPrintableString.java
@@ -0,0 +1,123 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * DER PrintableString object.
+ */
+public 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());
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERSequence.java b/src/main/java/com/lowagie/bc/asn1/DERSequence.java
new file mode 100644
index 0000000..5ffd2dd
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERSequence.java
@@ -0,0 +1,67 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+
+public 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);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERSet.java b/src/main/java/com/lowagie/bc/asn1/DERSet.java
new file mode 100644
index 0000000..fe4978c
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERSet.java
@@ -0,0 +1,70 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+
+/**
+ * A DER encoded set object
+ */
+public 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);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERString.java b/src/main/java/com/lowagie/bc/asn1/DERString.java
new file mode 100644
index 0000000..44409b2
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERString.java
@@ -0,0 +1,9 @@
+package com.lowagie.bc.asn1;
+
+/**
+ * basic interface for DER string objects.
+ */
+public interface DERString
+{
+ public String getString();
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERT61String.java b/src/main/java/com/lowagie/bc/asn1/DERT61String.java
new file mode 100644
index 0000000..228fd49
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERT61String.java
@@ -0,0 +1,116 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * DER T61String (also the teletex string)
+ */
+public 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)
+ {
+ 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 DERT61String(
+ String string)
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ return string;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(T61_STRING, this.getOctets());
+ }
+
+ 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;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERT61String))
+ {
+ return false;
+ }
+
+ return this.getString().equals(((DERT61String)o).getString());
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERTaggedObject.java b/src/main/java/com/lowagie/bc/asn1/DERTaggedObject.java
new file mode 100644
index 0000000..dbc9e36
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERTaggedObject.java
@@ -0,0 +1,88 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * 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).
+ */
+public 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]);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERTags.java b/src/main/java/com/lowagie/bc/asn1/DERTags.java
new file mode 100644
index 0000000..7b5ddec
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERTags.java
@@ -0,0 +1,36 @@
+package com.lowagie.bc.asn1;
+
+public 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 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;
+
+ public static final int CONSTRUCTED = 0x20;
+ public static final int APPLICATION = 0x40;
+ public static final int TAGGED = 0x80;
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERUTCTime.java b/src/main/java/com/lowagie/bc/asn1/DERUTCTime.java
new file mode 100644
index 0000000..200b23e
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERUTCTime.java
@@ -0,0 +1,183 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.SimpleTimeZone;
+
+/**
+ * UTC time object.
+ */
+public 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);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERUTF8String.java b/src/main/java/com/lowagie/bc/asn1/DERUTF8String.java
new file mode 100644
index 0000000..13f992b
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERUTF8String.java
@@ -0,0 +1,177 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * DER UTF8String object.
+ */
+public 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());
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERUniversalString.java b/src/main/java/com/lowagie/bc/asn1/DERUniversalString.java
new file mode 100644
index 0000000..eed5f08
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERUniversalString.java
@@ -0,0 +1,110 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * DER UniversalString object.
+ */
+public class DERUniversalString
+ extends DERObject
+ implements DERString
+{
+ private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+ private byte[] string;
+
+ /**
+ * 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;
+ }
+
+ public String getString()
+ {
+ StringBuffer buf = new StringBuffer("#");
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ try
+ {
+ aOut.writeObject(this);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("internal error encoding BitString");
+ }
+
+ byte[] string = bOut.toByteArray();
+
+ 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());
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERUnknownTag.java b/src/main/java/com/lowagie/bc/asn1/DERUnknownTag.java
new file mode 100644
index 0000000..fb653ba
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERUnknownTag.java
@@ -0,0 +1,73 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * We insert one of these when we find a tag we don't recognise.
+ */
+public 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;
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERVisibleString.java b/src/main/java/com/lowagie/bc/asn1/DERVisibleString.java
new file mode 100644
index 0000000..629f677
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERVisibleString.java
@@ -0,0 +1,116 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * DER VisibleString object.
+ */
+public 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());
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/OIDTokenizer.java b/src/main/java/com/lowagie/bc/asn1/OIDTokenizer.java
new file mode 100644
index 0000000..16d3445
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/OIDTokenizer.java
@@ -0,0 +1,48 @@
+package com.lowagie.bc.asn1;
+
+/**
+ * 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.
+ */
+public 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;
+ }
+}