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.crypto.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 }