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