1b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallampackage org.bouncycastle.asn1;
2b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
3b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.io.ByteArrayOutputStream;
4b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.io.IOException;
5b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
6c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstromimport org.bouncycastle.util.Arrays;
7c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
8b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam/**
9b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam * Base class for an application specific object
10b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam */
11b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallampublic class DERApplicationSpecific
124c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    extends ASN1Primitive
13b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam{
14c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    private final boolean   isConstructed;
15c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    private final int       tag;
16c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    private final byte[]    octets;
17c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
18c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    DERApplicationSpecific(
19c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        boolean isConstructed,
20c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        int     tag,
21c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        byte[]  octets)
22b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
23c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        this.isConstructed = isConstructed;
24b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        this.tag = tag;
25b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        this.octets = octets;
26b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
27c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
28c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    public DERApplicationSpecific(
29c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        int    tag,
30c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        byte[] octets)
31c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    {
32c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        this(false, tag, octets);
33c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    }
34c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
35b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public DERApplicationSpecific(
36b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int                  tag,
374c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        ASN1Encodable object)
38b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws IOException
39b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
40c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        this(true, tag, object);
41b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
42c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
43c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    public DERApplicationSpecific(
44c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        boolean      explicit,
45c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        int          tag,
464c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        ASN1Encodable object)
47c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        throws IOException
48c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    {
494c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        ASN1Primitive primitive = object.toASN1Primitive();
50c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
514c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        byte[] data = primitive.getEncoded(ASN1Encoding.DER);
524c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
534c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        this.isConstructed = explicit || (primitive instanceof ASN1Set || primitive instanceof ASN1Sequence);
54c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        this.tag = tag;
55c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
56c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        if (explicit)
57c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        {
58c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            this.octets = data;
59c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        }
60c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        else
61c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        {
624c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            int lenBytes = getLengthOfHeader(data);
63c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            byte[] tmp = new byte[data.length - lenBytes];
64c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
65c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            this.octets = tmp;
66c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        }
67c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    }
68c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
69c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    public DERApplicationSpecific(int tagNo, ASN1EncodableVector vec)
70c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    {
71c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        this.tag = tagNo;
72c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        this.isConstructed = true;
73c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
74c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
75c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        for (int i = 0; i != vec.size(); i++)
76c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        {
77c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            try
78c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            {
794c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom                bOut.write(((ASN1Object)vec.get(i)).getEncoded(ASN1Encoding.DER));
80c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            }
81c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            catch (IOException e)
82c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            {
83c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom                throw new ASN1ParsingException("malformed object: " + e, e);
84c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            }
85c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        }
86c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        this.octets = bOut.toByteArray();
87c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    }
88c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
894c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    public static DERApplicationSpecific getInstance(Object obj)
90c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    {
914c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        if (obj == null || obj instanceof DERApplicationSpecific)
924c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        {
934c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            return (DERApplicationSpecific)obj;
944c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        }
954c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        else if (obj instanceof byte[])
964c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        {
974c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            try
984c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            {
994c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom                return DERApplicationSpecific.getInstance(ASN1Primitive.fromByteArray((byte[])obj));
1004c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            }
1014c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            catch (IOException e)
1024c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            {
1034c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom                throw new IllegalArgumentException("failed to construct object from byte[]: " + e.getMessage());
1044c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            }
1054c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        }
1064c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        else if (obj instanceof ASN1Encodable)
1074c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        {
1084c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            ASN1Primitive primitive = ((ASN1Encodable)obj).toASN1Primitive();
1094c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1104c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            if (primitive instanceof ASN1Sequence)
1114c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            {
1124c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom                return (DERApplicationSpecific)primitive;
1134c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            }
1144c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        }
115c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
1164c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName());
1174c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
1184c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1194c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    private int getLengthOfHeader(byte[] data)
1204c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    {
1214c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        int length = data[1] & 0xff; // TODO: assumes 1 byte tag
1224c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1234c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        if (length == 0x80)
1244c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        {
1254c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            return 2;      // indefinite-length encoding
1264c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        }
1274c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1284c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        if (length > 127)
129c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        {
1304c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            int size = length & 0x7f;
1314c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1324c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
1334c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            if (size > 4)
1344c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            {
1354c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom                throw new IllegalStateException("DER length more than 4 bytes: " + size);
1364c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            }
1374c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1384c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            return size + 2;
139c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        }
140c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
1414c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        return 2;
142c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    }
143c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
144b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public boolean isConstructed()
145b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
146c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        return isConstructed;
147b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
148b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
149b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public byte[] getContents()
150b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
151b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return octets;
152b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
153b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
154b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public int getApplicationTag()
155b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
156c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        return tag;
157b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
158c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
159c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    /**
160c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom     * Return the enclosed object assuming explicit tagging.
161c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom     *
162c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom     * @return  the resulting object
163c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom     * @throws IOException if reconstruction fails.
164c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom     */
1654c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    public ASN1Primitive getObject()
166b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws IOException
167b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
168b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return new ASN1InputStream(getContents()).readObject();
169b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
170c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
171c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    /**
172c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom     * Return the enclosed object assuming implicit tagging.
173c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom     *
174c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom     * @param derTagNo the type tag that should be applied to the object's contents.
175c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom     * @return  the resulting object
176c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom     * @throws IOException if reconstruction fails.
177c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom     */
1784c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    public ASN1Primitive getObject(int derTagNo)
179c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        throws IOException
180c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    {
181c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        if (derTagNo >= 0x1f)
182c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        {
183c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            throw new IOException("unsupported tag number");
184c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        }
185c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
186c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        byte[] orig = this.getEncoded();
187c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        byte[] tmp = replaceTagNumber(derTagNo, orig);
188c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
1894c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        if ((orig[0] & BERTags.CONSTRUCTED) != 0)
190c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        {
1914c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            tmp[0] |= BERTags.CONSTRUCTED;
192c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        }
193c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
194c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        return new ASN1InputStream(tmp).readObject();
195c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    }
1964c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
1974c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    int encodedLength()
1984c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        throws IOException
1994c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    {
2004c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        return StreamUtil.calculateTagLength(tag) + StreamUtil.calculateBodyLength(octets.length) + octets.length;
2014c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    }
2024c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom
203b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    /* (non-Javadoc)
2044c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom     * @see org.bouncycastle.asn1.ASN1Primitive#encode(org.bouncycastle.asn1.DEROutputStream)
205b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     */
2064c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom    void encode(ASN1OutputStream out) throws IOException
207b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
2084c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        int classBits = BERTags.APPLICATION;
209c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        if (isConstructed)
210c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        {
2114c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom            classBits |= BERTags.CONSTRUCTED;
212c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        }
213c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
214c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        out.writeEncoded(classBits, tag, octets);
215b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
216b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
217c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    boolean asn1Equals(
2184c111300c39cb2e27f07fc2ae3b00e23ed4443b2Brian Carlstrom        ASN1Primitive o)
219b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
220c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        if (!(o instanceof DERApplicationSpecific))
221b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
222b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return false;
223b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
224c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
225b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        DERApplicationSpecific other = (DERApplicationSpecific)o;
226c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
227c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        return isConstructed == other.isConstructed
228c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            && tag == other.tag
229c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            && Arrays.areEqual(octets, other.octets);
230b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
231c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
232b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public int hashCode()
233b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
234c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        return (isConstructed ? 1 : 0) ^ tag ^ Arrays.hashCode(octets);
235c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    }
236b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
237c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    private byte[] replaceTagNumber(int newTag, byte[] input)
238c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        throws IOException
239c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom    {
240c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        int tagNo = input[0] & 0x1f;
241c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        int index = 1;
242c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        //
243c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        // with tagged object tag number is bottom 5 bits, or stored at the start of the content
244c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        //
245c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        if (tagNo == 0x1f)
246b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
247c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            tagNo = 0;
248c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
249c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            int b = input[index++] & 0xff;
250c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
251c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            // X.690-0207 8.1.2.4.2
252c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
253c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            if ((b & 0x7f) == 0) // Note: -1 will pass
254c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            {
255c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom                throw new ASN1ParsingException("corrupted stream - invalid high tag number found");
256c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            }
257c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
258c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            while ((b >= 0) && ((b & 0x80) != 0))
259c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            {
260c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom                tagNo |= (b & 0x7f);
261c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom                tagNo <<= 7;
262c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom                b = input[index++] & 0xff;
263c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            }
264c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
265c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom            tagNo |= (b & 0x7f);
266b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
267b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
268c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        byte[] tmp = new byte[input.length - index + 1];
269c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
270c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        System.arraycopy(input, index, tmp, 1, tmp.length - 1);
271c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
272c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        tmp[0] = (byte)newTag;
273c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom
274c37f4a04ef89e73a39a59f3c5a179af8c8ab5974Brian Carlstrom        return tmp;
275b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
276b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam}
277