001package org.apache.commons.ssl.asn1;
002
003import java.io.ByteArrayOutputStream;
004import java.io.IOException;
005
006/** Base class for an application specific object */
007public class DERApplicationSpecific
008    extends ASN1Object {
009    private int tag;
010    private byte[] octets;
011
012    public DERApplicationSpecific(
013        int tag,
014        byte[] octets) {
015        this.tag = tag;
016        this.octets = octets;
017    }
018
019    public DERApplicationSpecific(
020        int tag,
021        DEREncodable object)
022        throws IOException {
023        this(true, tag, object);
024    }
025
026    public DERApplicationSpecific(
027        boolean explicit,
028        int tag,
029        DEREncodable object)
030        throws IOException {
031        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
032        DEROutputStream dos = new DEROutputStream(bOut);
033
034        dos.writeObject(object);
035
036        byte[] data = bOut.toByteArray();
037
038        if (tag >= 0x1f) {
039            throw new IOException("unsupported tag number");
040        }
041
042        if (explicit) {
043            this.tag = tag | DERTags.CONSTRUCTED;
044            this.octets = data;
045        } else {
046            this.tag = tag;
047            int lenBytes = getLengthOfLength(data);
048            byte[] tmp = new byte[data.length - lenBytes];
049            System.arraycopy(data, lenBytes, tmp, 0, tmp.length);
050            this.octets = tmp;
051        }
052    }
053
054    private int getLengthOfLength(byte[] data) {
055        int count = 2;               // TODO: assumes only a 1 byte tag number
056
057        while ((data[count - 1] & 0x80) != 0) {
058            count++;
059        }
060
061        return count;
062    }
063
064    public boolean isConstructed() {
065        return (tag & DERTags.CONSTRUCTED) != 0;
066    }
067
068    public byte[] getContents() {
069        return octets;
070    }
071
072    public int getApplicationTag() {
073        return tag;
074    }
075
076    public DERObject getObject()
077        throws IOException {
078        return new ASN1InputStream(getContents()).readObject();
079    }
080
081    /**
082     * Return the enclosed object assuming implicit tagging.
083     *
084     * @param derTagNo the type tag that should be applied to the object's contents.
085     * @return the resulting object
086     * @throws IOException if reconstruction fails.
087     */
088    public DERObject getObject(int derTagNo)
089        throws IOException {
090        if (tag >= 0x1f) {
091            throw new IOException("unsupported tag number");
092        }
093
094        byte[] tmp = this.getEncoded();
095
096        tmp[0] = (byte) derTagNo;
097
098        return new ASN1InputStream(tmp).readObject();
099    }
100
101    /* (non-Javadoc)
102    * @see org.apache.commons.ssl.asn1.DERObject#encode(org.apache.commons.ssl.asn1.DEROutputStream)
103    */
104    void encode(DEROutputStream out) throws IOException {
105        out.writeEncoded(DERTags.APPLICATION | tag, octets);
106    }
107
108    boolean asn1Equals(
109        DERObject o) {
110        if (!(o instanceof DERApplicationSpecific)) {
111            return false;
112        }
113
114        DERApplicationSpecific other = (DERApplicationSpecific) o;
115
116        if (tag != other.tag) {
117            return false;
118        }
119
120        if (octets.length != other.octets.length) {
121            return false;
122        }
123
124        for (int i = 0; i < octets.length; i++) {
125            if (octets[i] != other.octets[i]) {
126                return false;
127            }
128        }
129
130        return true;
131    }
132
133    public int hashCode() {
134        byte[] b = this.getContents();
135        int value = 0;
136
137        for (int i = 0; i != b.length; i++) {
138            value ^= (b[i] & 0xff) << (i % 4);
139        }
140
141        return value ^ this.getApplicationTag();
142    }
143}