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