DERObjectIdentifier.java revision e1142c149e244797ce73b0e7fad40816e447a817
1package org.bouncycastle.asn1; 2 3import java.io.ByteArrayOutputStream; 4import java.io.IOException; 5import java.math.BigInteger; 6 7import org.bouncycastle.util.Arrays; 8 9public class DERObjectIdentifier 10 extends ASN1Primitive 11{ 12 String identifier; 13 14 private byte[] body; 15 16 /** 17 * return an OID from the passed in object 18 * 19 * @exception IllegalArgumentException if the object cannot be converted. 20 */ 21 public static ASN1ObjectIdentifier getInstance( 22 Object obj) 23 { 24 if (obj == null || obj instanceof ASN1ObjectIdentifier) 25 { 26 return (ASN1ObjectIdentifier)obj; 27 } 28 29 if (obj instanceof DERObjectIdentifier) 30 { 31 return new ASN1ObjectIdentifier(((DERObjectIdentifier)obj).getId()); 32 } 33 34 if (obj instanceof ASN1Encodable && ((ASN1Encodable)obj).toASN1Primitive() instanceof ASN1ObjectIdentifier) 35 { 36 return (ASN1ObjectIdentifier)((ASN1Encodable)obj).toASN1Primitive(); 37 } 38 39 if (obj instanceof byte[]) 40 { 41 return ASN1ObjectIdentifier.fromOctetString((byte[])obj); 42 } 43 44 throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); 45 } 46 47 /** 48 * return an Object Identifier from a tagged object. 49 * 50 * @param obj the tagged object holding the object we want 51 * @param explicit true if the object is meant to be explicitly 52 * tagged false otherwise. 53 * @exception IllegalArgumentException if the tagged object cannot 54 * be converted. 55 */ 56 public static ASN1ObjectIdentifier getInstance( 57 ASN1TaggedObject obj, 58 boolean explicit) 59 { 60 ASN1Primitive o = obj.getObject(); 61 62 if (explicit || o instanceof DERObjectIdentifier) 63 { 64 return getInstance(o); 65 } 66 else 67 { 68 return ASN1ObjectIdentifier.fromOctetString(ASN1OctetString.getInstance(obj.getObject()).getOctets()); 69 } 70 } 71 72 private static final long LONG_LIMIT = (Long.MAX_VALUE >> 7) - 0x7f; 73 74 DERObjectIdentifier( 75 byte[] bytes) 76 { 77 StringBuffer objId = new StringBuffer(); 78 long value = 0; 79 BigInteger bigValue = null; 80 boolean first = true; 81 82 for (int i = 0; i != bytes.length; i++) 83 { 84 int b = bytes[i] & 0xff; 85 86 if (value <= LONG_LIMIT) 87 { 88 value += (b & 0x7f); 89 if ((b & 0x80) == 0) // end of number reached 90 { 91 if (first) 92 { 93 if (value < 40) 94 { 95 objId.append('0'); 96 } 97 else if (value < 80) 98 { 99 objId.append('1'); 100 value -= 40; 101 } 102 else 103 { 104 objId.append('2'); 105 value -= 80; 106 } 107 first = false; 108 } 109 110 objId.append('.'); 111 objId.append(value); 112 value = 0; 113 } 114 else 115 { 116 value <<= 7; 117 } 118 } 119 else 120 { 121 if (bigValue == null) 122 { 123 bigValue = BigInteger.valueOf(value); 124 } 125 bigValue = bigValue.or(BigInteger.valueOf(b & 0x7f)); 126 if ((b & 0x80) == 0) 127 { 128 if (first) 129 { 130 objId.append('2'); 131 bigValue = bigValue.subtract(BigInteger.valueOf(80)); 132 first = false; 133 } 134 135 objId.append('.'); 136 objId.append(bigValue); 137 bigValue = null; 138 value = 0; 139 } 140 else 141 { 142 bigValue = bigValue.shiftLeft(7); 143 } 144 } 145 } 146 147 // BEGIN android-changed 148 /* 149 * Intern the identifier so there aren't hundreds of duplicates 150 * (in practice). 151 */ 152 this.identifier = objId.toString().intern(); 153 // END android-changed 154 this.body = Arrays.clone(bytes); 155 } 156 157 public DERObjectIdentifier( 158 String identifier) 159 { 160 if (!isValidIdentifier(identifier)) 161 { 162 throw new IllegalArgumentException("string " + identifier + " not an OID"); 163 } 164 165 // BEGIN android-changed 166 /* 167 * Intern the identifier so there aren't hundreds of duplicates 168 * (in practice). 169 */ 170 this.identifier = identifier.intern(); 171 // END android-changed 172 } 173 174 public String getId() 175 { 176 return identifier; 177 } 178 179 private void writeField( 180 ByteArrayOutputStream out, 181 long fieldValue) 182 { 183 byte[] result = new byte[9]; 184 int pos = 8; 185 result[pos] = (byte)((int)fieldValue & 0x7f); 186 while (fieldValue >= (1L << 7)) 187 { 188 fieldValue >>= 7; 189 result[--pos] = (byte)((int)fieldValue & 0x7f | 0x80); 190 } 191 out.write(result, pos, 9 - pos); 192 } 193 194 private void writeField( 195 ByteArrayOutputStream out, 196 BigInteger fieldValue) 197 { 198 int byteCount = (fieldValue.bitLength()+6)/7; 199 if (byteCount == 0) 200 { 201 out.write(0); 202 } 203 else 204 { 205 BigInteger tmpValue = fieldValue; 206 byte[] tmp = new byte[byteCount]; 207 for (int i = byteCount-1; i >= 0; i--) 208 { 209 tmp[i] = (byte) ((tmpValue.intValue() & 0x7f) | 0x80); 210 tmpValue = tmpValue.shiftRight(7); 211 } 212 tmp[byteCount-1] &= 0x7f; 213 out.write(tmp, 0, tmp.length); 214 } 215 } 216 217 private void doOutput(ByteArrayOutputStream aOut) 218 { 219 OIDTokenizer tok = new OIDTokenizer(identifier); 220 int first = Integer.parseInt(tok.nextToken()) * 40; 221 222 String secondToken = tok.nextToken(); 223 if (secondToken.length() <= 18) 224 { 225 writeField(aOut, first + Long.parseLong(secondToken)); 226 } 227 else 228 { 229 writeField(aOut, new BigInteger(secondToken).add(BigInteger.valueOf(first))); 230 } 231 232 while (tok.hasMoreTokens()) 233 { 234 String token = tok.nextToken(); 235 if (token.length() <= 18) 236 { 237 writeField(aOut, Long.parseLong(token)); 238 } 239 else 240 { 241 writeField(aOut, new BigInteger(token)); 242 } 243 } 244 } 245 246 protected synchronized byte[] getBody() 247 { 248 if (body == null) 249 { 250 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 251 252 doOutput(bOut); 253 254 body = bOut.toByteArray(); 255 } 256 257 return body; 258 } 259 260 boolean isConstructed() 261 { 262 return false; 263 } 264 265 int encodedLength() 266 throws IOException 267 { 268 int length = getBody().length; 269 270 return 1 + StreamUtil.calculateBodyLength(length) + length; 271 } 272 273 void encode( 274 ASN1OutputStream out) 275 throws IOException 276 { 277 byte[] enc = getBody(); 278 279 out.write(BERTags.OBJECT_IDENTIFIER); 280 out.writeLength(enc.length); 281 out.write(enc); 282 } 283 284 public int hashCode() 285 { 286 return identifier.hashCode(); 287 } 288 289 boolean asn1Equals( 290 ASN1Primitive o) 291 { 292 if (!(o instanceof DERObjectIdentifier)) 293 { 294 return false; 295 } 296 297 return identifier.equals(((DERObjectIdentifier)o).identifier); 298 } 299 300 public String toString() 301 { 302 return getId(); 303 } 304 305 private static boolean isValidIdentifier( 306 String identifier) 307 { 308 if (identifier.length() < 3 309 || identifier.charAt(1) != '.') 310 { 311 return false; 312 } 313 314 char first = identifier.charAt(0); 315 if (first < '0' || first > '2') 316 { 317 return false; 318 } 319 320 boolean periodAllowed = false; 321 for (int i = identifier.length() - 1; i >= 2; i--) 322 { 323 char ch = identifier.charAt(i); 324 325 // TODO Leading zeroes? 326 if ('0' <= ch && ch <= '9') 327 { 328 periodAllowed = true; 329 continue; 330 } 331 332 if (ch == '.') 333 { 334 if (!periodAllowed) 335 { 336 return false; 337 } 338 339 periodAllowed = false; 340 continue; 341 } 342 343 return false; 344 } 345 346 return periodAllowed; 347 } 348 349 private static ASN1ObjectIdentifier[][] cache = new ASN1ObjectIdentifier[256][]; 350 351 static ASN1ObjectIdentifier fromOctetString(byte[] enc) 352 { 353 if (enc.length < 3) 354 { 355 return new ASN1ObjectIdentifier(enc); 356 } 357 358 int idx1 = enc[enc.length - 2] & 0xff; 359 // in this case top bit is always zero 360 int idx2 = enc[enc.length - 1] & 0x7f; 361 362 ASN1ObjectIdentifier possibleMatch; 363 364 synchronized (cache) 365 { 366 ASN1ObjectIdentifier[] first = cache[idx1]; 367 if (first == null) 368 { 369 first = cache[idx1] = new ASN1ObjectIdentifier[128]; 370 } 371 372 possibleMatch = first[idx2]; 373 if (possibleMatch == null) 374 { 375 return first[idx2] = new ASN1ObjectIdentifier(enc); 376 } 377 378 if (Arrays.areEqual(enc, possibleMatch.getBody())) 379 { 380 return possibleMatch; 381 } 382 383 idx1 = (idx1 + 1) & 0xff; 384 first = cache[idx1]; 385 if (first == null) 386 { 387 first = cache[idx1] = new ASN1ObjectIdentifier[128]; 388 } 389 390 possibleMatch = first[idx2]; 391 if (possibleMatch == null) 392 { 393 return first[idx2] = new ASN1ObjectIdentifier(enc); 394 } 395 396 if (Arrays.areEqual(enc, possibleMatch.getBody())) 397 { 398 return possibleMatch; 399 } 400 401 idx2 = (idx2 + 1) & 0x7f; 402 possibleMatch = first[idx2]; 403 if (possibleMatch == null) 404 { 405 return first[idx2] = new ASN1ObjectIdentifier(enc); 406 } 407 } 408 409 if (Arrays.areEqual(enc, possibleMatch.getBody())) 410 { 411 return possibleMatch; 412 } 413 414 return new ASN1ObjectIdentifier(enc); 415 } 416} 417