001package org.apache.commons.ssl.org.bouncycastle.asn1; 002 003import java.io.IOException; 004 005/** 006 * ASN.1 TaggedObject - in ASN.1 notation this is any object preceded by 007 * a [n] where n is some number - these are assumed to follow the construction 008 * rules (as with sequences). 009 */ 010public abstract class ASN1TaggedObject 011 extends ASN1Primitive 012 implements ASN1TaggedObjectParser 013{ 014 int tagNo; 015 boolean empty = false; 016 boolean explicit = true; 017 ASN1Encodable obj = null; 018 019 static public ASN1TaggedObject getInstance( 020 ASN1TaggedObject obj, 021 boolean explicit) 022 { 023 if (explicit) 024 { 025 return (ASN1TaggedObject)obj.getObject(); 026 } 027 028 throw new IllegalArgumentException("implicitly tagged tagged object"); 029 } 030 031 static public ASN1TaggedObject getInstance( 032 Object obj) 033 { 034 if (obj == null || obj instanceof ASN1TaggedObject) 035 { 036 return (ASN1TaggedObject)obj; 037 } 038 else if (obj instanceof byte[]) 039 { 040 try 041 { 042 return ASN1TaggedObject.getInstance(fromByteArray((byte[])obj)); 043 } 044 catch (IOException e) 045 { 046 throw new IllegalArgumentException("failed to construct tagged object from byte[]: " + e.getMessage()); 047 } 048 } 049 050 throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); 051 } 052 053 /** 054 * Create a tagged object with the style given by the value of explicit. 055 * <p> 056 * If the object implements ASN1Choice the tag style will always be changed 057 * to explicit in accordance with the ASN.1 encoding rules. 058 * </p> 059 * @param explicit true if the object is explicitly tagged. 060 * @param tagNo the tag number for this object. 061 * @param obj the tagged object. 062 */ 063 public ASN1TaggedObject( 064 boolean explicit, 065 int tagNo, 066 ASN1Encodable obj) 067 { 068 if (obj instanceof ASN1Choice) 069 { 070 this.explicit = true; 071 } 072 else 073 { 074 this.explicit = explicit; 075 } 076 077 this.tagNo = tagNo; 078 079 if (this.explicit) 080 { 081 this.obj = obj; 082 } 083 else 084 { 085 ASN1Primitive prim = obj.toASN1Primitive(); 086 087 if (prim instanceof ASN1Set) 088 { 089 ASN1Set s = null; 090 } 091 092 this.obj = obj; 093 } 094 } 095 096 boolean asn1Equals( 097 ASN1Primitive o) 098 { 099 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 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 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 */ 160 public boolean isExplicit() 161 { 162 return explicit; 163 } 164 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 */ 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 */ 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 214 public ASN1Primitive getLoadedObject() 215 { 216 return this.toASN1Primitive(); 217 } 218 219 ASN1Primitive toDERObject() 220 { 221 return new DERTaggedObject(explicit, tagNo, obj); 222 } 223 224 ASN1Primitive toDLObject() 225 { 226 return new DLTaggedObject(explicit, tagNo, obj); 227 } 228 229 abstract void encode(ASN1OutputStream out) 230 throws IOException; 231 232 public String toString() 233 { 234 return "[" + tagNo + "]" + obj; 235 } 236}