1 package org.bouncycastle.asn1; 2 3 import java.io.IOException; 4 5 /** 6 * ASN.1 TaggedObject - in ASN.1 notation this is any object preceded by 7 * a [n] where n is some number - these are assumed to follow the construction 8 * rules (as with sequences). 9 */ 10 public abstract class ASN1TaggedObject 11 extends ASN1Primitive 12 implements ASN1TaggedObjectParser 13 { 14 int tagNo; 15 boolean empty = false; 16 boolean explicit = true; 17 ASN1Encodable obj = null; 18 getInstance( ASN1TaggedObject obj, boolean explicit)19 static public ASN1TaggedObject getInstance( 20 ASN1TaggedObject obj, 21 boolean explicit) 22 { 23 if (explicit) 24 { 25 return (ASN1TaggedObject)obj.getObject(); 26 } 27 28 throw new IllegalArgumentException("implicitly tagged tagged object"); 29 } 30 getInstance( Object obj)31 static public ASN1TaggedObject getInstance( 32 Object obj) 33 { 34 if (obj == null || obj instanceof ASN1TaggedObject) 35 { 36 return (ASN1TaggedObject)obj; 37 } 38 else if (obj instanceof byte[]) 39 { 40 try 41 { 42 return ASN1TaggedObject.getInstance(fromByteArray((byte[])obj)); 43 } 44 catch (IOException e) 45 { 46 throw new IllegalArgumentException("failed to construct tagged object from byte[]: " + e.getMessage()); 47 } 48 } 49 50 throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); 51 } 52 53 /** 54 * Create a tagged object with the style given by the value of explicit. 55 * <p> 56 * If the object implements ASN1Choice the tag style will always be changed 57 * to explicit in accordance with the ASN.1 encoding rules. 58 * </p> 59 * @param explicit true if the object is explicitly tagged. 60 * @param tagNo the tag number for this object. 61 * @param obj the tagged object. 62 */ ASN1TaggedObject( boolean explicit, int tagNo, ASN1Encodable obj)63 public ASN1TaggedObject( 64 boolean explicit, 65 int tagNo, 66 ASN1Encodable obj) 67 { 68 if (obj instanceof ASN1Choice) 69 { 70 this.explicit = true; 71 } 72 else 73 { 74 this.explicit = explicit; 75 } 76 77 this.tagNo = tagNo; 78 79 if (this.explicit) 80 { 81 this.obj = obj; 82 } 83 else 84 { 85 ASN1Primitive prim = obj.toASN1Primitive(); 86 87 if (prim instanceof ASN1Set) 88 { 89 ASN1Set s = null; 90 } 91 92 this.obj = obj; 93 } 94 } 95 asn1Equals( ASN1Primitive o)96 boolean asn1Equals( 97 ASN1Primitive o) 98 { 99 if (!(o instanceof ASN1TaggedObject)) 100 { 101 return false; 102 } 103 104 ASN1TaggedObject other = (ASN1TaggedObject)o; 105 106 if (tagNo != other.tagNo || empty != other.empty || explicit != other.explicit) 107 { 108 return false; 109 } 110 111 if(obj == null) 112 { 113 if (other.obj != null) 114 { 115 return false; 116 } 117 } 118 else 119 { 120 if (!(obj.toASN1Primitive().equals(other.obj.toASN1Primitive()))) 121 { 122 return false; 123 } 124 } 125 126 return true; 127 } 128 hashCode()129 public int hashCode() 130 { 131 int code = tagNo; 132 133 // TODO: actually this is wrong - the problem is that a re-encoded 134 // object may end up with a different hashCode due to implicit 135 // tagging. As implicit tagging is ambiguous if a sequence is involved 136 // it seems the only correct method for both equals and hashCode is to 137 // compare the encodings... 138 if (obj != null) 139 { 140 code ^= obj.hashCode(); 141 } 142 143 return code; 144 } 145 146 /** 147 * Return the tag number associated with this object. 148 * 149 * @return the tag number. 150 */ getTagNo()151 public int getTagNo() 152 { 153 return tagNo; 154 } 155 156 /** 157 * return whether or not the object may be explicitly tagged. 158 * <p> 159 * Note: if the object has been read from an input stream, the only 160 * time you can be sure if isExplicit is returning the true state of 161 * affairs is if it returns false. An implicitly tagged object may appear 162 * to be explicitly tagged, so you need to understand the context under 163 * which the reading was done as well, see getObject below. 164 */ isExplicit()165 public boolean isExplicit() 166 { 167 return explicit; 168 } 169 isEmpty()170 public boolean isEmpty() 171 { 172 return empty; 173 } 174 175 /** 176 * return whatever was following the tag. 177 * <p> 178 * Note: tagged objects are generally context dependent if you're 179 * trying to extract a tagged object you should be going via the 180 * appropriate getInstance method. 181 */ getObject()182 public ASN1Primitive getObject() 183 { 184 if (obj != null) 185 { 186 return obj.toASN1Primitive(); 187 } 188 189 return null; 190 } 191 192 /** 193 * Return the object held in this tagged object as a parser assuming it has 194 * the type of the passed in tag. If the object doesn't have a parser 195 * associated with it, the base object is returned. 196 */ getObjectParser( int tag, boolean isExplicit)197 public ASN1Encodable getObjectParser( 198 int tag, 199 boolean isExplicit) 200 throws IOException 201 { 202 switch (tag) 203 { 204 case BERTags.SET: 205 return ASN1Set.getInstance(this, isExplicit).parser(); 206 case BERTags.SEQUENCE: 207 return ASN1Sequence.getInstance(this, isExplicit).parser(); 208 case BERTags.OCTET_STRING: 209 return ASN1OctetString.getInstance(this, isExplicit).parser(); 210 } 211 212 if (isExplicit) 213 { 214 return getObject(); 215 } 216 217 throw new ASN1Exception("implicit tagging not implemented for tag: " + tag); 218 } 219 getLoadedObject()220 public ASN1Primitive getLoadedObject() 221 { 222 return this.toASN1Primitive(); 223 } 224 toDERObject()225 ASN1Primitive toDERObject() 226 { 227 return new DERTaggedObject(explicit, tagNo, obj); 228 } 229 toDLObject()230 ASN1Primitive toDLObject() 231 { 232 return new DLTaggedObject(explicit, tagNo, obj); 233 } 234 encode(ASN1OutputStream out)235 abstract void encode(ASN1OutputStream out) 236 throws IOException; 237 toString()238 public String toString() 239 { 240 return "[" + tagNo + "]" + obj; 241 } 242 } 243