Home | History | Annotate | Download | only in asn1
      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