1package org.bouncycastle.asn1; 2 3import java.io.ByteArrayOutputStream; 4import java.io.EOFException; 5import java.io.IOException; 6import java.io.InputStream; 7 8import org.bouncycastle.util.Arrays; 9import org.bouncycastle.util.io.Streams; 10 11public class DERBitString 12 extends ASN1Primitive 13 implements ASN1String 14{ 15 private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 16 17 protected byte[] data; 18 protected int padBits; 19 20 /** 21 * return the correct number of pad bits for a bit string defined in 22 * a 32 bit constant 23 */ 24 static protected int getPadBits( 25 int bitString) 26 { 27 int val = 0; 28 for (int i = 3; i >= 0; i--) 29 { 30 // 31 // this may look a little odd, but if it isn't done like this pre jdk1.2 32 // JVM's break! 33 // 34 if (i != 0) 35 { 36 if ((bitString >> (i * 8)) != 0) 37 { 38 val = (bitString >> (i * 8)) & 0xFF; 39 break; 40 } 41 } 42 else 43 { 44 if (bitString != 0) 45 { 46 val = bitString & 0xFF; 47 break; 48 } 49 } 50 } 51 52 if (val == 0) 53 { 54 return 7; 55 } 56 57 58 int bits = 1; 59 60 while (((val <<= 1) & 0xFF) != 0) 61 { 62 bits++; 63 } 64 65 return 8 - bits; 66 } 67 68 /** 69 * return the correct number of bytes for a bit string defined in 70 * a 32 bit constant 71 */ 72 static protected byte[] getBytes(int bitString) 73 { 74 int bytes = 4; 75 for (int i = 3; i >= 1; i--) 76 { 77 if ((bitString & (0xFF << (i * 8))) != 0) 78 { 79 break; 80 } 81 bytes--; 82 } 83 84 byte[] result = new byte[bytes]; 85 for (int i = 0; i < bytes; i++) 86 { 87 result[i] = (byte) ((bitString >> (i * 8)) & 0xFF); 88 } 89 90 return result; 91 } 92 93 /** 94 * return a Bit String from the passed in object 95 * 96 * @exception IllegalArgumentException if the object cannot be converted. 97 */ 98 public static DERBitString getInstance( 99 Object obj) 100 { 101 if (obj == null || obj instanceof DERBitString) 102 { 103 return (DERBitString)obj; 104 } 105 106 throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); 107 } 108 109 /** 110 * return a Bit String from a tagged object. 111 * 112 * @param obj the tagged object holding the object we want 113 * @param explicit true if the object is meant to be explicitly 114 * tagged false otherwise. 115 * @exception IllegalArgumentException if the tagged object cannot 116 * be converted. 117 */ 118 public static DERBitString getInstance( 119 ASN1TaggedObject obj, 120 boolean explicit) 121 { 122 ASN1Primitive o = obj.getObject(); 123 124 if (explicit || o instanceof DERBitString) 125 { 126 return getInstance(o); 127 } 128 else 129 { 130 return fromOctetString(((ASN1OctetString)o).getOctets()); 131 } 132 } 133 134 protected DERBitString( 135 byte data, 136 int padBits) 137 { 138 this.data = new byte[1]; 139 this.data[0] = data; 140 this.padBits = padBits; 141 } 142 143 /** 144 * @param data the octets making up the bit string. 145 * @param padBits the number of extra bits at the end of the string. 146 */ 147 public DERBitString( 148 byte[] data, 149 int padBits) 150 { 151 this.data = data; 152 this.padBits = padBits; 153 } 154 155 public DERBitString( 156 byte[] data) 157 { 158 this(data, 0); 159 } 160 161 public DERBitString( 162 int value) 163 { 164 this.data = getBytes(value); 165 this.padBits = getPadBits(value); 166 } 167 168 public DERBitString( 169 ASN1Encodable obj) 170 throws IOException 171 { 172 this.data = obj.toASN1Primitive().getEncoded(ASN1Encoding.DER); 173 this.padBits = 0; 174 } 175 176 public byte[] getBytes() 177 { 178 return data; 179 } 180 181 public int getPadBits() 182 { 183 return padBits; 184 } 185 186 187 /** 188 * @return the value of the bit string as an int (truncating if necessary) 189 */ 190 public int intValue() 191 { 192 int value = 0; 193 194 for (int i = 0; i != data.length && i != 4; i++) 195 { 196 value |= (data[i] & 0xff) << (8 * i); 197 } 198 199 return value; 200 } 201 202 boolean isConstructed() 203 { 204 return false; 205 } 206 207 int encodedLength() 208 { 209 return 1 + StreamUtil.calculateBodyLength(data.length + 1) + data.length + 1; 210 } 211 212 void encode( 213 ASN1OutputStream out) 214 throws IOException 215 { 216 byte[] bytes = new byte[getBytes().length + 1]; 217 218 bytes[0] = (byte)getPadBits(); 219 System.arraycopy(getBytes(), 0, bytes, 1, bytes.length - 1); 220 221 out.writeEncoded(BERTags.BIT_STRING, bytes); 222 } 223 224 public int hashCode() 225 { 226 return padBits ^ Arrays.hashCode(data); 227 } 228 229 protected boolean asn1Equals( 230 ASN1Primitive o) 231 { 232 if (!(o instanceof DERBitString)) 233 { 234 return false; 235 } 236 237 DERBitString other = (DERBitString)o; 238 239 return this.padBits == other.padBits 240 && Arrays.areEqual(this.data, other.data); 241 } 242 243 public String getString() 244 { 245 StringBuffer buf = new StringBuffer("#"); 246 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 247 ASN1OutputStream aOut = new ASN1OutputStream(bOut); 248 249 try 250 { 251 aOut.writeObject(this); 252 } 253 catch (IOException e) 254 { 255 throw new RuntimeException("internal error encoding BitString"); 256 } 257 258 byte[] string = bOut.toByteArray(); 259 260 for (int i = 0; i != string.length; i++) 261 { 262 buf.append(table[(string[i] >>> 4) & 0xf]); 263 buf.append(table[string[i] & 0xf]); 264 } 265 266 return buf.toString(); 267 } 268 269 public String toString() 270 { 271 return getString(); 272 } 273 274 static DERBitString fromOctetString(byte[] bytes) 275 { 276 if (bytes.length < 1) 277 { 278 throw new IllegalArgumentException("truncated BIT STRING detected"); 279 } 280 281 int padBits = bytes[0]; 282 byte[] data = new byte[bytes.length - 1]; 283 284 if (data.length != 0) 285 { 286 System.arraycopy(bytes, 1, data, 0, bytes.length - 1); 287 } 288 289 return new DERBitString(data, padBits); 290 } 291 292 static DERBitString fromInputStream(int length, InputStream stream) 293 throws IOException 294 { 295 if (length < 1) 296 { 297 throw new IllegalArgumentException("truncated BIT STRING detected"); 298 } 299 300 int padBits = stream.read(); 301 byte[] data = new byte[length - 1]; 302 303 if (data.length != 0) 304 { 305 if (Streams.readFully(stream, data) != data.length) 306 { 307 throw new EOFException("EOF encountered in middle of BIT STRING"); 308 } 309 } 310 311 return new DERBitString(data, padBits); 312 } 313} 314