From 6025b6016517c6d898d8957d1d7e03ba71431912 Mon Sep 17 00:00:00 2001 From: tknall Date: Fri, 1 Dec 2006 12:20:24 +0000 Subject: Initial import of release 2.2. git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@4 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c --- .../java/com/lowagie/bc/asn1/ASN1Encodable.java | 65 +++ .../com/lowagie/bc/asn1/ASN1EncodableVector.java | 9 + .../java/com/lowagie/bc/asn1/ASN1InputStream.java | 450 +++++++++++++++++++++ src/main/java/com/lowagie/bc/asn1/ASN1Null.java | 33 ++ .../java/com/lowagie/bc/asn1/ASN1OctetString.java | 141 +++++++ .../java/com/lowagie/bc/asn1/ASN1OutputStream.java | 36 ++ .../java/com/lowagie/bc/asn1/ASN1Sequence.java | 185 +++++++++ src/main/java/com/lowagie/bc/asn1/ASN1Set.java | 184 +++++++++ .../java/com/lowagie/bc/asn1/ASN1TaggedObject.java | 146 +++++++ .../lowagie/bc/asn1/BERConstructedOctetString.java | 168 ++++++++ .../lowagie/bc/asn1/BERConstructedSequence.java | 34 ++ .../java/com/lowagie/bc/asn1/BERInputStream.java | 197 +++++++++ src/main/java/com/lowagie/bc/asn1/BERNull.java | 30 ++ .../java/com/lowagie/bc/asn1/BEROutputStream.java | 36 ++ src/main/java/com/lowagie/bc/asn1/BERSequence.java | 59 +++ src/main/java/com/lowagie/bc/asn1/BERSet.java | 59 +++ .../java/com/lowagie/bc/asn1/BERTaggedObject.java | 119 ++++++ .../lowagie/bc/asn1/DERApplicationSpecific.java | 67 +++ .../java/com/lowagie/bc/asn1/DERBMPString.java | 121 ++++++ .../java/com/lowagie/bc/asn1/DERBitString.java | 268 ++++++++++++ src/main/java/com/lowagie/bc/asn1/DERBoolean.java | 103 +++++ .../lowagie/bc/asn1/DERConstructedSequence.java | 53 +++ .../com/lowagie/bc/asn1/DERConstructedSet.java | 75 ++++ .../java/com/lowagie/bc/asn1/DEREncodable.java | 6 + .../com/lowagie/bc/asn1/DEREncodableVector.java | 30 ++ .../java/com/lowagie/bc/asn1/DEREnumerated.java | 108 +++++ .../java/com/lowagie/bc/asn1/DERGeneralString.java | 86 ++++ .../com/lowagie/bc/asn1/DERGeneralizedTime.java | 188 +++++++++ .../java/com/lowagie/bc/asn1/DERIA5String.java | 123 ++++++ .../java/com/lowagie/bc/asn1/DERInputStream.java | 258 ++++++++++++ src/main/java/com/lowagie/bc/asn1/DERInteger.java | 129 ++++++ src/main/java/com/lowagie/bc/asn1/DERNull.java | 34 ++ .../java/com/lowagie/bc/asn1/DERNumericString.java | 123 ++++++ src/main/java/com/lowagie/bc/asn1/DERObject.java | 15 + .../com/lowagie/bc/asn1/DERObjectIdentifier.java | 170 ++++++++ .../java/com/lowagie/bc/asn1/DEROctetString.java | 29 ++ .../java/com/lowagie/bc/asn1/DEROutputStream.java | 81 ++++ .../com/lowagie/bc/asn1/DERPrintableString.java | 123 ++++++ src/main/java/com/lowagie/bc/asn1/DERSequence.java | 67 +++ src/main/java/com/lowagie/bc/asn1/DERSet.java | 70 ++++ src/main/java/com/lowagie/bc/asn1/DERString.java | 9 + .../java/com/lowagie/bc/asn1/DERT61String.java | 116 ++++++ .../java/com/lowagie/bc/asn1/DERTaggedObject.java | 88 ++++ src/main/java/com/lowagie/bc/asn1/DERTags.java | 36 ++ src/main/java/com/lowagie/bc/asn1/DERUTCTime.java | 183 +++++++++ .../java/com/lowagie/bc/asn1/DERUTF8String.java | 177 ++++++++ .../com/lowagie/bc/asn1/DERUniversalString.java | 110 +++++ .../java/com/lowagie/bc/asn1/DERUnknownTag.java | 73 ++++ .../java/com/lowagie/bc/asn1/DERVisibleString.java | 116 ++++++ .../java/com/lowagie/bc/asn1/OIDTokenizer.java | 48 +++ 50 files changed, 5234 insertions(+) create mode 100644 src/main/java/com/lowagie/bc/asn1/ASN1Encodable.java create mode 100644 src/main/java/com/lowagie/bc/asn1/ASN1EncodableVector.java create mode 100644 src/main/java/com/lowagie/bc/asn1/ASN1InputStream.java create mode 100644 src/main/java/com/lowagie/bc/asn1/ASN1Null.java create mode 100644 src/main/java/com/lowagie/bc/asn1/ASN1OctetString.java create mode 100644 src/main/java/com/lowagie/bc/asn1/ASN1OutputStream.java create mode 100644 src/main/java/com/lowagie/bc/asn1/ASN1Sequence.java create mode 100644 src/main/java/com/lowagie/bc/asn1/ASN1Set.java create mode 100644 src/main/java/com/lowagie/bc/asn1/ASN1TaggedObject.java create mode 100644 src/main/java/com/lowagie/bc/asn1/BERConstructedOctetString.java create mode 100644 src/main/java/com/lowagie/bc/asn1/BERConstructedSequence.java create mode 100644 src/main/java/com/lowagie/bc/asn1/BERInputStream.java create mode 100644 src/main/java/com/lowagie/bc/asn1/BERNull.java create mode 100644 src/main/java/com/lowagie/bc/asn1/BEROutputStream.java create mode 100644 src/main/java/com/lowagie/bc/asn1/BERSequence.java create mode 100644 src/main/java/com/lowagie/bc/asn1/BERSet.java create mode 100644 src/main/java/com/lowagie/bc/asn1/BERTaggedObject.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERApplicationSpecific.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERBMPString.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERBitString.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERBoolean.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERConstructedSequence.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERConstructedSet.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DEREncodable.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DEREncodableVector.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DEREnumerated.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERGeneralString.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERGeneralizedTime.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERIA5String.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERInputStream.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERInteger.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERNull.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERNumericString.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERObject.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERObjectIdentifier.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DEROctetString.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DEROutputStream.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERPrintableString.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERSequence.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERSet.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERString.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERT61String.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERTaggedObject.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERTags.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERUTCTime.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERUTF8String.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERUniversalString.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERUnknownTag.java create mode 100644 src/main/java/com/lowagie/bc/asn1/DERVisibleString.java create mode 100644 src/main/java/com/lowagie/bc/asn1/OIDTokenizer.java (limited to 'src/main/java/com/lowagie/bc') 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 should + * 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 should + * 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. + *

+ * 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. + *

+ * 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: + *

+ * 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: + *

+ * 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. + *

+ * + * @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). + *

+ * Normally in a certificate we would expect "Z" rather than "GMT", + * however adding the "GMT" means we can just use: + *

+     *     dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
+     * 
+ * 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. + *
+ * 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: + *

+ * 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: + *

+ * 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). + *

+ * + * @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). + *

+ * Normally in a certificate we would expect "Z" rather than "GMT", + * however adding the "GMT" means we can just use: + *

+     *     dateF = new SimpleDateFormat("yyMMddHHmmssz");
+     * 
+ * To read in the time and get a date which is compatible with our local + * time zone. + *

+ * Note: 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; + } +} -- cgit v1.2.3