001package org.apache.commons.ssl.org.bouncycastle.asn1; 002 003import java.io.ByteArrayOutputStream; 004import java.io.IOException; 005 006import org.bouncycastle.util.Arrays; 007 008/** 009 * Base class for an application specific object 010 */ 011public class DERApplicationSpecific 012 extends ASN1Primitive 013{ 014 private final boolean isConstructed; 015 private final int tag; 016 private final byte[] octets; 017 018 DERApplicationSpecific( 019 boolean isConstructed, 020 int tag, 021 byte[] octets) 022 { 023 this.isConstructed = isConstructed; 024 this.tag = tag; 025 this.octets = octets; 026 } 027 028 public DERApplicationSpecific( 029 int tag, 030 byte[] octets) 031 { 032 this(false, tag, octets); 033 } 034 035 public DERApplicationSpecific( 036 int tag, 037 ASN1Encodable object) 038 throws IOException 039 { 040 this(true, tag, object); 041 } 042 043 public DERApplicationSpecific( 044 boolean explicit, 045 int tag, 046 ASN1Encodable object) 047 throws IOException 048 { 049 ASN1Primitive primitive = object.toASN1Primitive(); 050 051 byte[] data = primitive.getEncoded(ASN1Encoding.DER); 052 053 this.isConstructed = explicit || (primitive instanceof ASN1Set || primitive instanceof ASN1Sequence); 054 this.tag = tag; 055 056 if (explicit) 057 { 058 this.octets = data; 059 } 060 else 061 { 062 int lenBytes = getLengthOfHeader(data); 063 byte[] tmp = new byte[data.length - lenBytes]; 064 System.arraycopy(data, lenBytes, tmp, 0, tmp.length); 065 this.octets = tmp; 066 } 067 } 068 069 public DERApplicationSpecific(int tagNo, ASN1EncodableVector vec) 070 { 071 this.tag = tagNo; 072 this.isConstructed = true; 073 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 074 075 for (int i = 0; i != vec.size(); i++) 076 { 077 try 078 { 079 bOut.write(((ASN1Object)vec.get(i)).getEncoded(ASN1Encoding.DER)); 080 } 081 catch (IOException e) 082 { 083 throw new ASN1ParsingException("malformed object: " + e, e); 084 } 085 } 086 this.octets = bOut.toByteArray(); 087 } 088 089 public static DERApplicationSpecific getInstance(Object obj) 090 { 091 if (obj == null || obj instanceof DERApplicationSpecific) 092 { 093 return (DERApplicationSpecific)obj; 094 } 095 else if (obj instanceof byte[]) 096 { 097 try 098 { 099 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 107 throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); 108 } 109 110 private int getLengthOfHeader(byte[] data) 111 { 112 int length = data[1] & 0xff; // TODO: assumes 1 byte tag 113 114 if (length == 0x80) 115 { 116 return 2; // indefinite-length encoding 117 } 118 119 if (length > 127) 120 { 121 int size = length & 0x7f; 122 123 // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here 124 if (size > 4) 125 { 126 throw new IllegalStateException("DER length more than 4 bytes: " + size); 127 } 128 129 return size + 2; 130 } 131 132 return 2; 133 } 134 135 public boolean isConstructed() 136 { 137 return isConstructed; 138 } 139 140 public byte[] getContents() 141 { 142 return octets; 143 } 144 145 public int getApplicationTag() 146 { 147 return tag; 148 } 149 150 /** 151 * Return the enclosed object assuming explicit tagging. 152 * 153 * @return the resulting object 154 * @throws IOException if reconstruction fails. 155 */ 156 public ASN1Primitive getObject() 157 throws IOException 158 { 159 return new ASN1InputStream(getContents()).readObject(); 160 } 161 162 /** 163 * Return the enclosed object assuming implicit tagging. 164 * 165 * @param derTagNo the type tag that should be applied to the object's contents. 166 * @return the resulting object 167 * @throws IOException if reconstruction fails. 168 */ 169 public ASN1Primitive getObject(int derTagNo) 170 throws IOException 171 { 172 if (derTagNo >= 0x1f) 173 { 174 throw new IOException("unsupported tag number"); 175 } 176 177 byte[] orig = this.getEncoded(); 178 byte[] tmp = replaceTagNumber(derTagNo, orig); 179 180 if ((orig[0] & BERTags.CONSTRUCTED) != 0) 181 { 182 tmp[0] |= BERTags.CONSTRUCTED; 183 } 184 185 return new ASN1InputStream(tmp).readObject(); 186 } 187 188 int encodedLength() 189 throws IOException 190 { 191 return StreamUtil.calculateTagLength(tag) + StreamUtil.calculateBodyLength(octets.length) + octets.length; 192 } 193 194 /* (non-Javadoc) 195 * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream) 196 */ 197 void encode(ASN1OutputStream out) throws IOException 198 { 199 int classBits = BERTags.APPLICATION; 200 if (isConstructed) 201 { 202 classBits |= BERTags.CONSTRUCTED; 203 } 204 205 out.writeEncoded(classBits, tag, octets); 206 } 207 208 boolean asn1Equals( 209 ASN1Primitive o) 210 { 211 if (!(o instanceof DERApplicationSpecific)) 212 { 213 return false; 214 } 215 216 DERApplicationSpecific other = (DERApplicationSpecific)o; 217 218 return isConstructed == other.isConstructed 219 && tag == other.tag 220 && Arrays.areEqual(octets, other.octets); 221 } 222 223 public int hashCode() 224 { 225 return (isConstructed ? 1 : 0) ^ tag ^ Arrays.hashCode(octets); 226 } 227 228 private byte[] replaceTagNumber(int newTag, byte[] input) 229 throws IOException 230 { 231 int tagNo = input[0] & 0x1f; 232 int index = 1; 233 // 234 // with tagged object tag number is bottom 5 bits, or stored at the start of the content 235 // 236 if (tagNo == 0x1f) 237 { 238 tagNo = 0; 239 240 int b = input[index++] & 0xff; 241 242 // X.690-0207 8.1.2.4.2 243 // "c) bits 7 to 1 of the first subsequent octet shall not all be zero." 244 if ((b & 0x7f) == 0) // Note: -1 will pass 245 { 246 throw new ASN1ParsingException("corrupted stream - invalid high tag number found"); 247 } 248 249 while ((b >= 0) && ((b & 0x80) != 0)) 250 { 251 tagNo |= (b & 0x7f); 252 tagNo <<= 7; 253 b = input[index++] & 0xff; 254 } 255 256 tagNo |= (b & 0x7f); 257 } 258 259 byte[] tmp = new byte[input.length - index + 1]; 260 261 System.arraycopy(input, index, tmp, 1, tmp.length - 1); 262 263 tmp[0] = (byte)newTag; 264 265 return tmp; 266 } 267}