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 getTagNo()146 public int getTagNo() 147 { 148 return tagNo; 149 } 150 151 /** 152 * return whether or not the object may be explicitly tagged. 153 * <p> 154 * Note: if the object has been read from an input stream, the only 155 * time you can be sure if isExplicit is returning the true state of 156 * affairs is if it returns false. An implicitly tagged object may appear 157 * to be explicitly tagged, so you need to understand the context under 158 * which the reading was done as well, see getObject below. 159 */ isExplicit()160 public boolean isExplicit() 161 { 162 return explicit; 163 } 164 isEmpty()165 public boolean isEmpty() 166 { 167 return empty; 168 } 169 170 /** 171 * return whatever was following the tag. 172 * <p> 173 * Note: tagged objects are generally context dependent if you're 174 * trying to extract a tagged object you should be going via the 175 * appropriate getInstance method. 176 */ getObject()177 public ASN1Primitive getObject() 178 { 179 if (obj != null) 180 { 181 return obj.toASN1Primitive(); 182 } 183 184 return null; 185 } 186 187 /** 188 * Return the object held in this tagged object as a parser assuming it has 189 * the type of the passed in tag. If the object doesn't have a parser 190 * associated with it, the base object is returned. 191 */ getObjectParser( int tag, boolean isExplicit)192 public ASN1Encodable getObjectParser( 193 int tag, 194 boolean isExplicit) 195 { 196 switch (tag) 197 { 198 case BERTags.SET: 199 return ASN1Set.getInstance(this, isExplicit).parser(); 200 case BERTags.SEQUENCE: 201 return ASN1Sequence.getInstance(this, isExplicit).parser(); 202 case BERTags.OCTET_STRING: 203 return ASN1OctetString.getInstance(this, isExplicit).parser(); 204 } 205 206 if (isExplicit) 207 { 208 return getObject(); 209 } 210 211 throw new RuntimeException("implicit tagging not implemented for tag: " + tag); 212 } 213 getLoadedObject()214 public ASN1Primitive getLoadedObject() 215 { 216 return this.toASN1Primitive(); 217 } 218 toDERObject()219 ASN1Primitive toDERObject() 220 { 221 return new DERTaggedObject(explicit, tagNo, obj); 222 } 223 toDLObject()224 ASN1Primitive toDLObject() 225 { 226 return new DLTaggedObject(explicit, tagNo, obj); 227 } 228 encode(ASN1OutputStream out)229 abstract void encode(ASN1OutputStream out) 230 throws IOException; 231 toString()232 public String toString() 233 { 234 return "[" + tagNo + "]" + obj; 235 } 236 } 237