001/* 002 * Copyright 2017-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2017-2018 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.asn1; 022 023 024 025import java.util.ArrayList; 026import java.util.Iterator; 027import java.util.List; 028 029import com.unboundid.util.ByteStringBuffer; 030import com.unboundid.util.NotMutable; 031import com.unboundid.util.OID; 032import com.unboundid.util.ThreadSafety; 033import com.unboundid.util.ThreadSafetyLevel; 034 035import static com.unboundid.asn1.ASN1Constants.*; 036import static com.unboundid.asn1.ASN1Messages.*; 037import static com.unboundid.util.Debug.*; 038 039 040 041/** 042 * This class provides an ASN.1 object identifier element, whose value 043 * represents a numeric OID. Note that ASN.1 object identifier elements must 044 * strictly conform to the numeric OID specification, which has the following 045 * requirements: 046 * <UL> 047 * <LI>All valid OIDs must contain at least two components.</LI> 048 * <LI>The value of the first component must be 0, 1, or 2.</LI> 049 * <LI>If the value of the first component is 0 or 1, then the value of the 050 * second component must not be greater than 39.</LI> 051 * </UL> 052 */ 053@NotMutable() 054@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 055public final class ASN1ObjectIdentifier 056 extends ASN1Element 057{ 058 /** 059 * The serial version UID for this serializable class. 060 */ 061 private static final long serialVersionUID = -777778295086222273L; 062 063 064 065 // The OID represented by this object identifier element. 066 private final OID oid; 067 068 069 070 /** 071 * Creates a new ASN.1 object identifier element with the default BER type and 072 * the provided OID. 073 * 074 * @param oid The OID to represent with this element. It must not be 075 * {@code null}, and it must represent a valid OID. 076 * 077 * @throws ASN1Exception If the provided OID does not strictly adhere to the 078 * numeric OID format. 079 */ 080 public ASN1ObjectIdentifier(final OID oid) 081 throws ASN1Exception 082 { 083 this(UNIVERSAL_OBJECT_IDENTIFIER_TYPE, oid); 084 } 085 086 087 088 /** 089 * Creates a new ASN.1 object identifier element with the specified BER type 090 * and the provided OID. 091 * 092 * @param type The BER type for this element. 093 * @param oid The OID to represent with this element. It must not be 094 * {@code null}, and it must represent a valid OID. 095 * 096 * @throws ASN1Exception If the provided OID does not strictly adhere to the 097 * numeric OID format. 098 */ 099 public ASN1ObjectIdentifier(final byte type, final OID oid) 100 throws ASN1Exception 101 { 102 this(type, oid, encodeValue(oid)); 103 } 104 105 106 107 /** 108 * Creates a new ASN.1 object identifier element with the default BER type and 109 * the provided OID. 110 * 111 * @param oidString The string representation of the OID to represent with 112 * this element. It must not be {@code null}, and it must 113 * represent a valid OID. 114 * 115 * @throws ASN1Exception If the provided OID does not strictly adhere to the 116 * numeric OID format. 117 */ 118 public ASN1ObjectIdentifier(final String oidString) 119 throws ASN1Exception 120 { 121 this(UNIVERSAL_OBJECT_IDENTIFIER_TYPE, oidString); 122 } 123 124 125 126 /** 127 * Creates a new ASN.1 object identifier element with the specified BER type 128 * and the provided OID. 129 * 130 * @param type The BER type for this element. 131 * @param oidString The string representation of the OID to represent with 132 * this element. It must not be {@code null}, and it must 133 * represent a valid OID. 134 * 135 * @throws ASN1Exception If the provided OID does not strictly adhere to the 136 * numeric OID format. 137 */ 138 public ASN1ObjectIdentifier(final byte type, final String oidString) 139 throws ASN1Exception 140 { 141 this(type, new OID(oidString)); 142 } 143 144 145 146 /** 147 * Creates a new ASN.1 object identifier element with the provided 148 * information. 149 * 150 * @param type The BER type to use for this element. 151 * @param oid The OID to represent with this element. 152 * @param encodedValue The encoded value for this element. 153 */ 154 private ASN1ObjectIdentifier(final byte type, final OID oid, 155 final byte[] encodedValue) 156 { 157 super(type, encodedValue); 158 159 this.oid = oid; 160 } 161 162 163 164 /** 165 * Generates an encoded value for an object identifier element with the 166 * provided OID. 167 * 168 * @param oid The OID to represent with this element. It must not be 169 * {@code null}, and it must represent a valid OID. 170 * 171 * @return The encoded value. 172 * 173 * @throws ASN1Exception If the provided OID does not strictly conform to 174 * the requirements for ASN.1 OIDs. 175 */ 176 private static byte[] encodeValue(final OID oid) 177 throws ASN1Exception 178 { 179 // Make sure that the provided UID conforms to the necessary constraints. 180 if (! oid.isValidNumericOID()) 181 { 182 throw new ASN1Exception(ERR_OID_ENCODE_NOT_NUMERIC.get()); 183 } 184 185 final List<Integer> components = oid.getComponents(); 186 if (components.size() < 2) 187 { 188 throw new ASN1Exception(ERR_OID_ENCODE_NOT_ENOUGH_COMPONENTS.get( 189 oid.toString())); 190 } 191 192 final Iterator<Integer> componentIterator = components.iterator(); 193 194 final int firstComponent = componentIterator.next(); 195 if ((firstComponent < 0) || (firstComponent > 2)) 196 { 197 throw new ASN1Exception(ERR_OID_ENCODE_INVALID_FIRST_COMPONENT.get( 198 oid.toString(), firstComponent)); 199 } 200 201 final int secondComponent = componentIterator.next(); 202 if ((secondComponent < 0) || 203 ((firstComponent != 2) && (secondComponent > 39))) 204 { 205 throw new ASN1Exception(ERR_OID_ENCODE_INVALID_SECOND_COMPONENT.get( 206 oid.toString(), firstComponent, secondComponent)); 207 } 208 209 210 // Construct the encoded representation of the OID. Compute it as follows: 211 // - The first and second components are merged together by multiplying the 212 // value of the first component by 40 and adding the value of the second 213 // component. Every other component is handled individually. 214 // - For components (including the merged first and second components) whose 215 // value is less than or equal to 127, the encoded representation of that 216 // component is simply the single-byte encoded representation of that 217 // number. 218 // - For components (including the merged first and second components) whose 219 // value is greater than 127, that component must be encoded in multiple 220 // bytes. In the encoded representation, only the lower seven bits of 221 // each byte will be used to convey the value. The most significant bit 222 // of each byte will be used to indicate whether there are more bytes in 223 // the component. 224 final ByteStringBuffer buffer = new ByteStringBuffer(); 225 final int mergedFirstComponents = (40 * firstComponent) + secondComponent; 226 encodeComponent(mergedFirstComponents, buffer); 227 while (componentIterator.hasNext()) 228 { 229 encodeComponent(componentIterator.next(), buffer); 230 } 231 232 return buffer.toByteArray(); 233 } 234 235 236 237 /** 238 * Appends an encoded representation of the provided component value to the 239 * given buffer. 240 * 241 * @param c The value of the component to encode. 242 * @param b The buffer to which the encoded representation should be 243 * appended. 244 */ 245 private static void encodeComponent(final int c, final ByteStringBuffer b) 246 { 247 final int finalByte = c & 0b1111111; 248 if (finalByte == c) 249 { 250 b.append((byte) finalByte); 251 } 252 else if ((c & 0b1111111_1111111) == c) 253 { 254 b.append((byte) (0b10000000 | ((c >> 7) & 0b1111111))); 255 b.append((byte) finalByte); 256 } 257 else if ((c & 0b1111111_1111111_1111111) == c) 258 { 259 b.append((byte) (0b10000000 | ((c >> 14) & 0b1111111))); 260 b.append((byte) (0b10000000 | ((c >> 7) & 0b1111111))); 261 b.append((byte) finalByte); 262 } 263 else if ((c & 0b1111111_1111111_1111111_1111111) == c) 264 { 265 b.append((byte) (0b10000000 | ((c >> 21) & 0b1111111))); 266 b.append((byte) (0b10000000 | ((c >> 14) & 0b1111111))); 267 b.append((byte) (0b10000000 | ((c >> 7) & 0b1111111))); 268 b.append((byte) finalByte); 269 } 270 else 271 { 272 b.append((byte) (0b10000000 | ((c >> 28) & 0b1111111))); 273 b.append((byte) (0b10000000 | ((c >> 21) & 0b1111111))); 274 b.append((byte) (0b10000000 | ((c >> 14) & 0b1111111))); 275 b.append((byte) (0b10000000 | ((c >> 7) & 0b1111111))); 276 b.append((byte) finalByte); 277 } 278 } 279 280 281 282 /** 283 * Retrieves the OID represented by this object identifier element. 284 * 285 * @return The OID represented by this object identifier element. 286 */ 287 public OID getOID() 288 { 289 return oid; 290 } 291 292 293 294 /** 295 * Decodes the contents of the provided byte array as an object identifier 296 * element. 297 * 298 * @param elementBytes The byte array to decode as an ASN.1 object 299 * identifier element. 300 * 301 * @return The decoded ASN.1 object identifier element. 302 * 303 * @throws ASN1Exception If the provided array cannot be decoded as an 304 * object identifier element. 305 */ 306 public static ASN1ObjectIdentifier decodeAsObjectIdentifier( 307 final byte[] elementBytes) 308 throws ASN1Exception 309 { 310 try 311 { 312 int valueStartPos = 2; 313 int length = (elementBytes[1] & 0x7F); 314 if (length != elementBytes[1]) 315 { 316 final int numLengthBytes = length; 317 318 length = 0; 319 for (int i=0; i < numLengthBytes; i++) 320 { 321 length <<= 8; 322 length |= (elementBytes[valueStartPos++] & 0xFF); 323 } 324 } 325 326 if ((elementBytes.length - valueStartPos) != length) 327 { 328 throw new ASN1Exception(ERR_ELEMENT_LENGTH_MISMATCH.get(length, 329 (elementBytes.length - valueStartPos))); 330 } 331 332 final byte[] elementValue = new byte[length]; 333 System.arraycopy(elementBytes, valueStartPos, elementValue, 0, length); 334 final OID oid = decodeValue(elementValue); 335 return new ASN1ObjectIdentifier(elementBytes[0], oid, elementValue); 336 } 337 catch (final ASN1Exception ae) 338 { 339 debugException(ae); 340 throw ae; 341 } 342 catch (final Exception e) 343 { 344 debugException(e); 345 throw new ASN1Exception(ERR_ELEMENT_DECODE_EXCEPTION.get(e), e); 346 } 347 } 348 349 350 351 /** 352 * Decodes the provided ASN.1 element as an object identifier element. 353 * 354 * @param element The ASN.1 element to be decoded. 355 * 356 * @return The decoded ASN.1 object identifier element. 357 * 358 * @throws ASN1Exception If the provided element cannot be decoded as an 359 * object identifier element. 360 */ 361 public static ASN1ObjectIdentifier decodeAsObjectIdentifier( 362 final ASN1Element element) 363 throws ASN1Exception 364 { 365 final OID oid = decodeValue(element.getValue()); 366 return new ASN1ObjectIdentifier(element.getType(), oid, element.getValue()); 367 } 368 369 370 371 /** 372 * Decodes the provided value as an OID. 373 * 374 * @param elementValue The bytes that comprise the encoded value for an 375 * object identifier element. 376 * 377 * @return The decoded OID. 378 * 379 * @throws ASN1Exception If the provided value cannot be decoded as a valid 380 * OID. 381 */ 382 private static OID decodeValue(final byte[] elementValue) 383 throws ASN1Exception 384 { 385 if (elementValue.length == 0) 386 { 387 throw new ASN1Exception(ERR_OID_DECODE_EMPTY_VALUE.get()); 388 } 389 390 final byte lastByte = elementValue[elementValue.length - 1]; 391 if ((lastByte & 0x80) == 0x80) 392 { 393 throw new ASN1Exception(ERR_OID_DECODE_INCOMPLETE_VALUE.get()); 394 } 395 396 int currentComponent = 0x00; 397 final ArrayList<Integer> components = new ArrayList<>(elementValue.length); 398 for (final byte b : elementValue) 399 { 400 currentComponent <<= 7; 401 currentComponent |= (b & 0x7F); 402 if ((b & 0x80) == 0x00) 403 { 404 if (components.isEmpty()) 405 { 406 if (currentComponent < 40) 407 { 408 components.add(0); 409 components.add(currentComponent); 410 } 411 else if (currentComponent < 80) 412 { 413 components.add(1); 414 components.add(currentComponent - 40); 415 } 416 else 417 { 418 components.add(2); 419 components.add(currentComponent - 80); 420 } 421 } 422 else 423 { 424 components.add(currentComponent); 425 } 426 427 currentComponent = 0x00; 428 } 429 } 430 431 return new OID(components); 432 } 433 434 435 436 /** 437 * {@inheritDoc} 438 */ 439 @Override() 440 public void toString(final StringBuilder buffer) 441 { 442 buffer.append(oid.toString()); 443 } 444}