001package org.apache.commons.ssl.org.bouncycastle.asn1; 002 003import java.io.ByteArrayOutputStream; 004import java.io.EOFException; 005import java.io.IOException; 006import java.io.InputStream; 007 008import org.bouncycastle.util.Arrays; 009import org.bouncycastle.util.io.Streams; 010 011public class DERBitString 012 extends ASN1Primitive 013 implements ASN1String 014{ 015 private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 016 017 protected byte[] data; 018 protected int padBits; 019 020 /** 021 * @param bitString an int containing the BIT STRING 022 * @return the correct number of pad bits for a bit string defined in 023 * a 32 bit constant 024 */ 025 static protected int getPadBits( 026 int bitString) 027 { 028 int val = 0; 029 for (int i = 3; i >= 0; i--) 030 { 031 // 032 // this may look a little odd, but if it isn't done like this pre jdk1.2 033 // JVM's break! 034 // 035 if (i != 0) 036 { 037 if ((bitString >> (i * 8)) != 0) 038 { 039 val = (bitString >> (i * 8)) & 0xFF; 040 break; 041 } 042 } 043 else 044 { 045 if (bitString != 0) 046 { 047 val = bitString & 0xFF; 048 break; 049 } 050 } 051 } 052 053 if (val == 0) 054 { 055 return 7; 056 } 057 058 059 int bits = 1; 060 061 while (((val <<= 1) & 0xFF) != 0) 062 { 063 bits++; 064 } 065 066 return 8 - bits; 067 } 068 069 /** 070 * @param bitString an int containing the BIT STRING 071 * @return the correct number of bytes for a bit string defined in 072 * a 32 bit constant 073 */ 074 static protected byte[] getBytes(int bitString) 075 { 076 int bytes = 4; 077 for (int i = 3; i >= 1; i--) 078 { 079 if ((bitString & (0xFF << (i * 8))) != 0) 080 { 081 break; 082 } 083 bytes--; 084 } 085 086 byte[] result = new byte[bytes]; 087 for (int i = 0; i < bytes; i++) 088 { 089 result[i] = (byte) ((bitString >> (i * 8)) & 0xFF); 090 } 091 092 return result; 093 } 094 095 /** 096 * return a Bit String from the passed in object 097 * 098 * @param obj a DERBitString or an object that can be converted into one. 099 * @exception IllegalArgumentException if the object cannot be converted. 100 * @return a DERBitString instance, or null. 101 */ 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 */ 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 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 */ 152 public DERBitString( 153 byte[] data, 154 int padBits) 155 { 156 this.data = data; 157 this.padBits = padBits; 158 } 159 160 public DERBitString( 161 byte[] data) 162 { 163 this(data, 0); 164 } 165 166 public DERBitString( 167 int value) 168 { 169 this.data = getBytes(value); 170 this.padBits = getPadBits(value); 171 } 172 173 public DERBitString( 174 ASN1Encodable obj) 175 throws IOException 176 { 177 this.data = obj.toASN1Primitive().getEncoded(ASN1Encoding.DER); 178 this.padBits = 0; 179 } 180 181 public byte[] getBytes() 182 { 183 return data; 184 } 185 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 */ 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 207 boolean isConstructed() 208 { 209 return false; 210 } 211 212 int encodedLength() 213 { 214 return 1 + StreamUtil.calculateBodyLength(data.length + 1) + data.length + 1; 215 } 216 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 229 public int hashCode() 230 { 231 return padBits ^ Arrays.hashCode(data); 232 } 233 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 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 274 public String toString() 275 { 276 return getString(); 277 } 278 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 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}