001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package org.apache.geronimo.util.asn1;
019    
020    import java.io.ByteArrayOutputStream;
021    import java.io.IOException;
022    
023    public class DERBitString
024        extends DERObject
025        implements DERString
026    {
027        private static final char[]  table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
028    
029        protected byte[]      data;
030        protected int         padBits;
031    
032        /**
033         * return the correct number of pad bits for a bit string defined in
034         * a 32 bit constant
035         */
036        static protected int getPadBits(
037            int bitString)
038        {
039            int val = 0;
040            for (int i = 3; i >= 0; i--)
041            {
042                //
043                // this may look a little odd, but if it isn't done like this pre jdk1.2
044                // JVM's break!
045                //
046                if (i != 0)
047                {
048                    if ((bitString >> (i * 8)) != 0)
049                    {
050                        val = (bitString >> (i * 8)) & 0xFF;
051                        break;
052                    }
053                }
054                else
055                {
056                    if (bitString != 0)
057                    {
058                        val = bitString & 0xFF;
059                        break;
060                    }
061                }
062            }
063    
064            if (val == 0)
065            {
066                return 7;
067            }
068    
069    
070            int bits = 1;
071    
072            while (((val <<= 1) & 0xFF) != 0)
073            {
074                bits++;
075            }
076    
077            return 8 - bits;
078        }
079    
080        /**
081         * return the correct number of bytes for a bit string defined in
082         * a 32 bit constant
083         */
084        static protected byte[] getBytes(int bitString)
085        {
086            int bytes = 4;
087            for (int i = 3; i >= 1; i--)
088            {
089                if ((bitString & (0xFF << (i * 8))) != 0)
090                {
091                    break;
092                }
093                bytes--;
094            }
095    
096            byte[] result = new byte[bytes];
097            for (int i = 0; i < bytes; i++)
098            {
099                result[i] = (byte) ((bitString >> (i * 8)) & 0xFF);
100            }
101    
102            return result;
103        }
104    
105        /**
106         * return a Bit String from the passed in object
107         *
108         * @exception IllegalArgumentException if the object cannot be converted.
109         */
110        public static DERBitString getInstance(
111            Object  obj)
112        {
113            if (obj == null || obj instanceof DERBitString)
114            {
115                return (DERBitString)obj;
116            }
117    
118            if (obj instanceof ASN1OctetString)
119            {
120                byte[]  bytes = ((ASN1OctetString)obj).getOctets();
121                int     padBits = bytes[0];
122                byte[]  data = new byte[bytes.length - 1];
123    
124                System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
125    
126                return new DERBitString(data, padBits);
127            }
128    
129            if (obj instanceof ASN1TaggedObject)
130            {
131                return getInstance(((ASN1TaggedObject)obj).getObject());
132            }
133    
134            throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
135        }
136    
137        /**
138         * return a Bit String from a tagged object.
139         *
140         * @param obj the tagged object holding the object we want
141         * @param explicit true if the object is meant to be explicitly
142         *              tagged false otherwise.
143         * @exception IllegalArgumentException if the tagged object cannot
144         *               be converted.
145         */
146        public static DERBitString getInstance(
147            ASN1TaggedObject obj,
148            boolean          explicit)
149        {
150            return getInstance(obj.getObject());
151        }
152    
153        protected DERBitString(
154            byte    data,
155            int     padBits)
156        {
157            this.data = new byte[1];
158            this.data[0] = data;
159            this.padBits = padBits;
160        }
161    
162        /**
163         * @param data the octets making up the bit string.
164         * @param padBits the number of extra bits at the end of the string.
165         */
166        public DERBitString(
167            byte[]  data,
168            int     padBits)
169        {
170            this.data = data;
171            this.padBits = padBits;
172        }
173    
174        public DERBitString(
175            byte[]  data)
176        {
177            this(data, 0);
178        }
179    
180        public DERBitString(
181            DEREncodable  obj)
182        {
183            try
184            {
185                ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
186                DEROutputStream         dOut = new DEROutputStream(bOut);
187    
188                dOut.writeObject(obj);
189                dOut.close();
190    
191                this.data = bOut.toByteArray();
192                this.padBits = 0;
193            }
194            catch (IOException e)
195            {
196                throw new IllegalArgumentException("Error processing object : " + e.getMessage(), e);
197            }
198        }
199    
200        public byte[] getBytes()
201        {
202            return data;
203        }
204    
205        public int getPadBits()
206        {
207            return padBits;
208        }
209    
210    
211        /**
212         * @return the value of the bit string as an int (truncating if necessary)
213         */
214        public int intValue()
215        {
216            int value = 0;
217    
218            for (int i = 0; i != data.length && i != 4; i++)
219            {
220                value |= (data[i] & 0xff) << (8 * i);
221            }
222    
223            return value;
224        }
225    
226        void encode(
227            DEROutputStream  out)
228            throws IOException
229        {
230            byte[]  bytes = new byte[getBytes().length + 1];
231    
232            bytes[0] = (byte)getPadBits();
233            System.arraycopy(getBytes(), 0, bytes, 1, bytes.length - 1);
234    
235            out.writeEncoded(BIT_STRING, bytes);
236        }
237    
238        public int hashCode()
239        {
240            int     value = 0;
241    
242            for (int i = 0; i != data.length; i++)
243            {
244                value ^= (data[i] & 0xff) << (i % 4);
245            }
246    
247            return value;
248        }
249    
250        public boolean equals(
251            Object  o)
252        {
253            if (o == null || !(o instanceof DERBitString))
254            {
255                return false;
256            }
257    
258            DERBitString  other = (DERBitString)o;
259    
260            if (data.length != other.data.length)
261            {
262                return false;
263            }
264    
265            for (int i = 0; i != data.length; i++)
266            {
267                if (data[i] != other.data[i])
268                {
269                    return false;
270                }
271            }
272    
273            return (padBits == other.padBits);
274        }
275    
276        public String getString()
277        {
278            StringBuffer          buf = new StringBuffer("#");
279            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
280            ASN1OutputStream      aOut = new ASN1OutputStream(bOut);
281    
282            try
283            {
284                aOut.writeObject(this);
285            }
286            catch (IOException e)
287            {
288               throw new RuntimeException("internal error encoding BitString", e);
289            }
290    
291            byte[]    string = bOut.toByteArray();
292    
293            for (int i = 0; i != string.length; i++)
294            {
295                buf.append(table[(string[i] >>> 4) % 0xf]);
296                buf.append(table[string[i] & 0xf]);
297            }
298    
299            return buf.toString();
300        }
301    }