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