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 }