using System; using System.IO; using System.Text; using Org.BouncyCastle.Math; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Asn1 { public class Asn1RelativeOid : Asn1Object { public static Asn1RelativeOid FromContents(byte[] contents) { return CreatePrimitive(contents, true); } public static Asn1RelativeOid GetInstance(object obj) { if (obj == null || obj is Asn1RelativeOid) { return (Asn1RelativeOid)obj; } else if (obj is IAsn1Convertible) { Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); if (asn1Object is Asn1RelativeOid) return (Asn1RelativeOid)asn1Object; } else if (obj is byte[]) { try { return GetInstance(FromByteArray((byte[])obj)); } catch (IOException e) { throw new ArgumentException("failed to construct relative OID from byte[]: " + e.Message); } } throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj), "obj"); } public static Asn1RelativeOid GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { Asn1Object baseObject = taggedObject.GetObject(); if (declaredExplicit || baseObject is Asn1RelativeOid) { return GetInstance(baseObject); } return FromContents(Asn1OctetString.GetInstance(baseObject).GetOctets()); } private const long LongLimit = (Int64.MaxValue >> 7) - 0x7F; private readonly string identifier; private byte[] contents; public Asn1RelativeOid(string identifier) { if (identifier == null) throw new ArgumentNullException("identifier"); if (!IsValidIdentifier(identifier, 0)) throw new FormatException("string " + identifier + " not a relative OID"); this.identifier = identifier; } private Asn1RelativeOid(Asn1RelativeOid oid, string branchID) { if (!IsValidIdentifier(branchID, 0)) throw new FormatException("string " + branchID + " not a valid relative OID branch"); this.identifier = oid.Id + "." + branchID; } private Asn1RelativeOid(byte[] contents, bool clone) { this.identifier = ParseContents(contents); this.contents = clone ? Arrays.Clone(contents) : contents; } public virtual Asn1RelativeOid Branch(string branchID) { return new Asn1RelativeOid(this, branchID); } public string Id { get { return identifier; } } public override string ToString() { return identifier; } protected override bool Asn1Equals(Asn1Object asn1Object) { Asn1RelativeOid that = asn1Object as Asn1RelativeOid; return null != that && this.identifier == that.identifier; } protected override int Asn1GetHashCode() { return identifier.GetHashCode(); } internal override bool EncodeConstructed(int encoding) { return false; } internal override int EncodedLength(int encoding, bool withID) { return Asn1OutputStream.GetLengthOfEncodingDL(withID, GetContents().Length); } internal override void Encode(Asn1OutputStream asn1Out, bool withID) { asn1Out.WriteEncodingDL(withID, Asn1Tags.RelativeOid, GetContents()); } private void DoOutput(MemoryStream bOut) { OidTokenizer tok = new OidTokenizer(identifier); while (tok.HasMoreTokens) { string token = tok.NextToken(); if (token.Length <= 18) { WriteField(bOut, Int64.Parse(token)); } else { WriteField(bOut, new BigInteger(token)); } } } private byte[] GetContents() { lock (this) { if (contents == null) { MemoryStream bOut = new MemoryStream(); DoOutput(bOut); contents = bOut.ToArray(); } return contents; } } internal static Asn1RelativeOid CreatePrimitive(byte[] contents, bool clone) { return new Asn1RelativeOid(contents, clone); } internal static bool IsValidIdentifier(string identifier, int from) { int digitCount = 0; int pos = identifier.Length; while (--pos >= from) { char ch = identifier[pos]; if (ch == '.') { if (0 == digitCount || (digitCount > 1 && identifier[pos + 1] == '0')) return false; digitCount = 0; } else if ('0' <= ch && ch <= '9') { ++digitCount; } else { return false; } } if (0 == digitCount || (digitCount > 1 && identifier[pos + 1] == '0')) return false; return true; } internal static void WriteField(Stream outputStream, long fieldValue) { byte[] result = new byte[9]; int pos = 8; result[pos] = (byte)((int)fieldValue & 0x7F); while (fieldValue >= (1L << 7)) { fieldValue >>= 7; result[--pos] = (byte)((int)fieldValue | 0x80); } outputStream.Write(result, pos, 9 - pos); } internal static void WriteField(Stream outputStream, BigInteger fieldValue) { int byteCount = (fieldValue.BitLength + 6) / 7; if (byteCount == 0) { outputStream.WriteByte(0); } else { BigInteger tmpValue = fieldValue; byte[] tmp = new byte[byteCount]; for (int i = byteCount - 1; i >= 0; i--) { tmp[i] = (byte)(tmpValue.IntValue | 0x80); tmpValue = tmpValue.ShiftRight(7); } tmp[byteCount - 1] &= 0x7F; outputStream.Write(tmp, 0, tmp.Length); } } private static string ParseContents(byte[] contents) { StringBuilder objId = new StringBuilder(); long value = 0; BigInteger bigValue = null; bool first = true; for (int i = 0; i != contents.Length; i++) { int b = contents[i]; if (value <= LongLimit) { value += b & 0x7F; if ((b & 0x80) == 0) { if (first) { first = false; } else { objId.Append('.'); } objId.Append(value); value = 0; } else { value <<= 7; } } else { if (bigValue == null) { bigValue = BigInteger.ValueOf(value); } bigValue = bigValue.Or(BigInteger.ValueOf(b & 0x7F)); if ((b & 0x80) == 0) { if (first) { first = false; } else { objId.Append('.'); } objId.Append(bigValue); bigValue = null; value = 0; } else { bigValue = bigValue.ShiftLeft(7); } } } return objId.ToString(); } } }