1 package org.bouncycastle.asn1; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.IOException; 5 6 import org.bouncycastle.util.Arrays; 7 8 /** 9 * Base class for an application specific object 10 */ 11 public class DERApplicationSpecific 12 extends ASN1Primitive 13 { 14 private final boolean isConstructed; 15 private final int tag; 16 private final byte[] octets; 17 18 DERApplicationSpecific( 19 boolean isConstructed, 20 int tag, 21 byte[] octets) 22 { 23 this.isConstructed = isConstructed; 24 this.tag = tag; 25 this.octets = octets; 26 } 27 28 public DERApplicationSpecific( 29 int tag, 30 byte[] octets) 31 { 32 this(false, tag, octets); 33 } 34 35 public DERApplicationSpecific( 36 int tag, 37 ASN1Encodable object) 38 throws IOException 39 { 40 this(true, tag, object); 41 } 42 43 public DERApplicationSpecific( 44 boolean explicit, 45 int tag, 46 ASN1Encodable object) 47 throws IOException 48 { 49 ASN1Primitive primitive = object.toASN1Primitive(); 50 51 byte[] data = primitive.getEncoded(ASN1Encoding.DER); 52 53 this.isConstructed = explicit || (primitive instanceof ASN1Set || primitive instanceof ASN1Sequence); 54 this.tag = tag; 55 56 if (explicit) 57 { 58 this.octets = data; 59 } 60 else 61 { 62 int lenBytes = getLengthOfHeader(data); 63 byte[] tmp = new byte[data.length - lenBytes]; 64 System.arraycopy(data, lenBytes, tmp, 0, tmp.length); 65 this.octets = tmp; 66 } 67 } 68 69 public DERApplicationSpecific(int tagNo, ASN1EncodableVector vec) 70 { 71 this.tag = tagNo; 72 this.isConstructed = true; 73 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 74 75 for (int i = 0; i != vec.size(); i++) 76 { 77 try 78 { 79 bOut.write(((ASN1Object)vec.get(i)).getEncoded(ASN1Encoding.DER)); 80 } 81 catch (IOException e) 82 { 83 throw new ASN1ParsingException("malformed object: " + e, e); 84 } 85 } 86 this.octets = bOut.toByteArray(); 87 } 88 89 public static DERApplicationSpecific getInstance(Object obj) 90 { 91 if (obj == null || obj instanceof DERApplicationSpecific) 92 { 93 return (DERApplicationSpecific)obj; 94 } 95 else if (obj instanceof byte[]) 96 { 97 try 98 { 99 return DERApplicationSpecific.getInstance(ASN1Primitive.fromByteArray((byte[])obj)); 100 } 101 catch (IOException e) 102 { 103 throw new IllegalArgumentException("failed to construct object from byte[]: " + e.getMessage()); 104 } 105 } 106 else if (obj instanceof ASN1Encodable) 107 { 108 ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive(); 109 110 if (primitive instanceof ASN1Sequence) 111 { 112 return (DERApplicationSpecific)primitive; 113 } 114 } 115 116 throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); 117 } 118 119 private int getLengthOfHeader(byte[] data) 120 { 121 int length = data[1] & 0xff; // TODO: assumes 1 byte tag 122 123 if (length == 0x80) 124 { 125 return 2; // indefinite-length encoding 126 } 127 128 if (length > 127) 129 { 130 int size = length & 0x7f; 131 132 // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here 133 if (size > 4) 134 { 135 throw new IllegalStateException("DER length more than 4 bytes: " + size); 136 } 137 138 return size + 2; 139 } 140 141 return 2; 142 } 143 144 public boolean isConstructed() 145 { 146 return isConstructed; 147 } 148 149 public byte[] getContents() 150 { 151 return octets; 152 } 153 154 public int getApplicationTag() 155 { 156 return tag; 157 } 158 159 /** 160 * Return the enclosed object assuming explicit tagging. 161 * 162 * @return the resulting object 163 * @throws IOException if reconstruction fails. 164 */ 165 public ASN1Primitive getObject() 166 throws IOException 167 { 168 return new ASN1InputStream(getContents()).readObject(); 169 } 170 171 /** 172 * Return the enclosed object assuming implicit tagging. 173 * 174 * @param derTagNo the type tag that should be applied to the object's contents. 175 * @return the resulting object 176 * @throws IOException if reconstruction fails. 177 */ 178 public ASN1Primitive getObject(int derTagNo) 179 throws IOException 180 { 181 if (derTagNo >= 0x1f) 182 { 183 throw new IOException("unsupported tag number"); 184 } 185 186 byte[] orig = this.getEncoded(); 187 byte[] tmp = replaceTagNumber(derTagNo, orig); 188 189 if ((orig[0] & BERTags.CONSTRUCTED) != 0) 190 { 191 tmp[0] |= BERTags.CONSTRUCTED; 192 } 193 194 return new ASN1InputStream(tmp).readObject(); 195 } 196 197 int encodedLength() 198 throws IOException 199 { 200 return StreamUtil.calculateTagLength(tag) + StreamUtil.calculateBodyLength(octets.length) + octets.length; 201 } 202 203 /* (non-Javadoc) 204 * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream) 205 */ 206 void encode(ASN1OutputStream out) throws IOException 207 { 208 int classBits = BERTags.APPLICATION; 209 if (isConstructed) 210 { 211 classBits |= BERTags.CONSTRUCTED; 212 } 213 214 out.writeEncoded(classBits, tag, octets); 215 } 216 217 boolean asn1Equals( 218 ASN1Primitive o) 219 { 220 if (!(o instanceof DERApplicationSpecific)) 221 { 222 return false; 223 } 224 225 DERApplicationSpecific other = (DERApplicationSpecific)o; 226 227 return isConstructed == other.isConstructed 228 && tag == other.tag 229 && Arrays.areEqual(octets, other.octets); 230 } 231 232 public int hashCode() 233 { 234 return (isConstructed ? 1 : 0) ^ tag ^ Arrays.hashCode(octets); 235 } 236 237 private byte[] replaceTagNumber(int newTag, byte[] input) 238 throws IOException 239 { 240 int tagNo = input[0] & 0x1f; 241 int index = 1; 242 // 243 // with tagged object tag number is bottom 5 bits, or stored at the start of the content 244 // 245 if (tagNo == 0x1f) 246 { 247 tagNo = 0; 248 249 int b = input[index++] & 0xff; 250 251 // X.690-0207 8.1.2.4.2 252 // "c) bits 7 to 1 of the first subsequent octet shall not all be zero." 253 if ((b & 0x7f) == 0) // Note: -1 will pass 254 { 255 throw new ASN1ParsingException("corrupted stream - invalid high tag number found"); 256 } 257 258 while ((b >= 0) && ((b & 0x80) != 0)) 259 { 260 tagNo |= (b & 0x7f); 261 tagNo <<= 7; 262 b = input[index++] & 0xff; 263 } 264 265 tagNo |= (b & 0x7f); 266 } 267 268 byte[] tmp = new byte[input.length - index + 1]; 269 270 System.arraycopy(input, index, tmp, 1, tmp.length - 1); 271 272 tmp[0] = (byte)newTag; 273 274 return tmp; 275 } 276 } 277