diff --git a/crypto/src/asn1/DerApplicationSpecific.cs b/crypto/src/asn1/DerApplicationSpecific.cs
new file mode 100644
index 000000000..394c7431e
--- /dev/null
+++ b/crypto/src/asn1/DerApplicationSpecific.cs
@@ -0,0 +1,237 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Asn1
+{
+ /**
+ * Base class for an application specific object
+ */
+ public class DerApplicationSpecific
+ : Asn1Object
+ {
+ private readonly bool isConstructed;
+ private readonly int tag;
+ private readonly byte[] octets;
+
+ internal DerApplicationSpecific(
+ bool isConstructed,
+ int tag,
+ byte[] octets)
+ {
+ this.isConstructed = isConstructed;
+ this.tag = tag;
+ this.octets = octets;
+ }
+
+ public DerApplicationSpecific(
+ int tag,
+ byte[] octets)
+ : this(false, tag, octets)
+ {
+ }
+
+ public DerApplicationSpecific(
+ int tag,
+ Asn1Encodable obj)
+ : this(true, tag, obj)
+ {
+ }
+
+ public DerApplicationSpecific(
+ bool isExplicit,
+ int tag,
+ Asn1Encodable obj)
+ {
+ Asn1Object asn1Obj = obj.ToAsn1Object();
+
+ byte[] data = asn1Obj.GetDerEncoded();
+
+ this.isConstructed = isExplicit || asn1Obj is Asn1Set || asn1Obj is Asn1Sequence;
+ this.tag = tag;
+
+ if (isExplicit)
+ {
+ this.octets = data;
+ }
+ else
+ {
+ int lenBytes = GetLengthOfHeader(data);
+ byte[] tmp = new byte[data.Length - lenBytes];
+ Array.Copy(data, lenBytes, tmp, 0, tmp.Length);
+ this.octets = tmp;
+ }
+ }
+
+ public DerApplicationSpecific(
+ int tagNo,
+ Asn1EncodableVector vec)
+ {
+ this.tag = tagNo;
+ this.isConstructed = true;
+ MemoryStream bOut = new MemoryStream();
+
+ for (int i = 0; i != vec.Count; i++)
+ {
+ try
+ {
+ byte[] bs = vec[i].GetDerEncoded();
+ bOut.Write(bs, 0, bs.Length);
+ }
+ catch (IOException e)
+ {
+ throw new InvalidOperationException("malformed object", e);
+ }
+ }
+ this.octets = bOut.ToArray();
+ }
+
+ private int GetLengthOfHeader(
+ byte[] data)
+ {
+ int length = data[1]; // TODO: assumes 1 byte tag
+
+ if (length == 0x80)
+ {
+ return 2; // indefinite-length encoding
+ }
+
+ if (length > 127)
+ {
+ int size = length & 0x7f;
+
+ // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
+ if (size > 4)
+ {
+ throw new InvalidOperationException("DER length more than 4 bytes: " + size);
+ }
+
+ return size + 2;
+ }
+
+ return 2;
+ }
+
+ public bool IsConstructed()
+ {
+ return isConstructed;
+ }
+
+ public byte[] GetContents()
+ {
+ return octets;
+ }
+
+ public int ApplicationTag
+ {
+ get { return tag; }
+ }
+
+ /**
+ * Return the enclosed object assuming explicit tagging.
+ *
+ * @return the resulting object
+ * @throws IOException if reconstruction fails.
+ */
+ public Asn1Object GetObject()
+ {
+ return FromByteArray(GetContents());
+ }
+
+ /**
+ * Return the enclosed object assuming implicit tagging.
+ *
+ * @param derTagNo the type tag that should be applied to the object's contents.
+ * @return the resulting object
+ * @throws IOException if reconstruction fails.
+ */
+ public Asn1Object GetObject(
+ int derTagNo)
+ {
+ if (derTagNo >= 0x1f)
+ throw new IOException("unsupported tag number");
+
+ byte[] orig = this.GetEncoded();
+ byte[] tmp = ReplaceTagNumber(derTagNo, orig);
+
+ if ((orig[0] & Asn1Tags.Constructed) != 0)
+ {
+ tmp[0] |= Asn1Tags.Constructed;
+ }
+
+ return FromByteArray(tmp);;
+ }
+
+ internal override void Encode(
+ DerOutputStream derOut)
+ {
+ int classBits = Asn1Tags.Application;
+ if (isConstructed)
+ {
+ classBits |= Asn1Tags.Constructed;
+ }
+
+ derOut.WriteEncoded(classBits, tag, octets);
+ }
+
+ protected override bool Asn1Equals(
+ Asn1Object asn1Object)
+ {
+ DerApplicationSpecific other = asn1Object as DerApplicationSpecific;
+
+ if (other == null)
+ return false;
+
+ return this.isConstructed == other.isConstructed
+ && this.tag == other.tag
+ && Arrays.AreEqual(this.octets, other.octets);
+ }
+
+ protected override int Asn1GetHashCode()
+ {
+ return isConstructed.GetHashCode() ^ tag.GetHashCode() ^ Arrays.GetHashCode(octets);
+ }
+
+ private byte[] ReplaceTagNumber(
+ int newTag,
+ byte[] input)
+ {
+ int tagNo = input[0] & 0x1f;
+ int index = 1;
+ //
+ // with tagged object tag number is bottom 5 bits, or stored at the start of the content
+ //
+ if (tagNo == 0x1f)
+ {
+ tagNo = 0;
+
+ int b = input[index++] & 0xff;
+
+ // X.690-0207 8.1.2.4.2
+ // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
+ if ((b & 0x7f) == 0) // Note: -1 will pass
+ {
+ throw new InvalidOperationException("corrupted stream - invalid high tag number found");
+ }
+
+ while ((b >= 0) && ((b & 0x80) != 0))
+ {
+ tagNo |= (b & 0x7f);
+ tagNo <<= 7;
+ b = input[index++] & 0xff;
+ }
+
+ tagNo |= (b & 0x7f);
+ }
+
+ byte[] tmp = new byte[input.Length - index + 1];
+
+ Array.Copy(input, index, tmp, 1, tmp.Length - 1);
+
+ tmp[0] = (byte)newTag;
+
+ return tmp;
+ }
+ }
+}
|