001    package org.apache.commons.ssl.asn1;
002    
003    import java.io.ByteArrayOutputStream;
004    import java.io.IOException;
005    
006    /** Base class for an application specific object */
007    public 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    }