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