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