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