diff options
Diffstat (limited to 'crypto/src/asn1/DerApplicationSpecific.cs')
-rw-r--r-- | crypto/src/asn1/DerApplicationSpecific.cs | 237 |
1 files changed, 237 insertions, 0 deletions
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; + } + } +} |