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