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