001/* 002 * Copyright 2017-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2017-2024 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-2024 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.util.ssl.cert; 037 038 039 040import java.io.Serializable; 041import java.net.InetAddress; 042import java.util.ArrayList; 043import java.util.Collections; 044import java.util.Iterator; 045import java.util.List; 046 047import com.unboundid.asn1.ASN1Element; 048import com.unboundid.asn1.ASN1IA5String; 049import com.unboundid.asn1.ASN1ObjectIdentifier; 050import com.unboundid.asn1.ASN1OctetString; 051import com.unboundid.asn1.ASN1Sequence; 052import com.unboundid.ldap.sdk.DN; 053import com.unboundid.util.Debug; 054import com.unboundid.util.NotMutable; 055import com.unboundid.util.NotNull; 056import com.unboundid.util.OID; 057import com.unboundid.util.ObjectPair; 058import com.unboundid.util.StaticUtils; 059import com.unboundid.util.ThreadSafety; 060import com.unboundid.util.ThreadSafetyLevel; 061 062import static com.unboundid.util.ssl.cert.CertMessages.*; 063 064 065 066/** 067 * This class provides a data structure that represents a {@code GeneralNames} 068 * element that may appear in a number of X.509 certificate extensions, 069 * including {@link SubjectAlternativeNameExtension}, 070 * {@link IssuerAlternativeNameExtension}, 071 * {@link AuthorityKeyIdentifierExtension}, and 072 * {@link CRLDistributionPointsExtension}. The {@code GeneralNames} element has 073 * the following encoding (as described in 074 * <A HREF="https://www.ietf.org/rfc/rfc5280.txt">RFC 5280</A> section 4.2.1.6): 075 * <PRE> 076 * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName 077 * 078 * GeneralName ::= CHOICE { 079 * otherName [0] OtherName, 080 * rfc822Name [1] IA5String, 081 * dNSName [2] IA5String, 082 * x400Address [3] ORAddress, 083 * directoryName [4] Name, 084 * ediPartyName [5] EDIPartyName, 085 * uniformResourceIdentifier [6] IA5String, 086 * iPAddress [7] OCTET STRING, 087 * registeredID [8] OBJECT IDENTIFIER } 088 * 089 * OtherName ::= SEQUENCE { 090 * type-id OBJECT IDENTIFIER, 091 * value [0] EXPLICIT ANY DEFINED BY type-id } 092 * 093 * EDIPartyName ::= SEQUENCE { 094 * nameAssigner [0] DirectoryString OPTIONAL, 095 * partyName [1] DirectoryString } 096 * </PRE> 097 */ 098@NotMutable() 099@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 100public final class GeneralNames 101 implements Serializable 102{ 103 /** 104 * The DER type for otherName elements. 105 */ 106 private static final byte NAME_TYPE_OTHER_NAME = (byte) 0xA0; 107 108 109 110 /** 111 * The DER type for rfc822Name elements. 112 */ 113 private static final byte NAME_TYPE_RFC_822_NAME = (byte) 0x81; 114 115 116 117 /** 118 * The DER type for dNSName elements. 119 */ 120 private static final byte NAME_TYPE_DNS_NAME = (byte) 0x82; 121 122 123 124 /** 125 * The DER type for x400Address elements. 126 */ 127 private static final byte NAME_TYPE_X400_ADDRESS = (byte) 0xA3; 128 129 130 131 /** 132 * The DER type for directoryName elements. 133 */ 134 private static final byte NAME_TYPE_DIRECTORY_NAME = (byte) 0xA4; 135 136 137 138 /** 139 * The DER type for ediPartyName elements. 140 */ 141 private static final byte NAME_TYPE_EDI_PARTY_NAME = (byte) 0xA5; 142 143 144 145 /** 146 * The DER type for uniformResourceIdentifier elements. 147 */ 148 private static final byte NAME_TYPE_UNIFORM_RESOURCE_IDENTIFIER = (byte) 0x86; 149 150 151 152 /** 153 * The DER type for ipAddress elements. 154 */ 155 private static final byte NAME_TYPE_IP_ADDRESS = (byte) 0x87; 156 157 158 159 /** 160 * The DER type for registeredID elements. 161 */ 162 private static final byte NAME_TYPE_REGISTERED_ID = (byte) 0x88; 163 164 165 166 /** 167 * The DER type for the value element in an otherName element. 168 */ 169 private static final byte NAME_TYPE_OTHER_NAME_VALUE = (byte) 0xA0; 170 171 172 173 /** 174 * The serial version UID for this serializable class. 175 */ 176 private static final long serialVersionUID = -8789437423467093314L; 177 178 179 180 // The EDI party names included in the extension. 181 @NotNull private final List<ASN1Element> ediPartyNames; 182 183 // The X.400 names included in the extension. 184 @NotNull private final List<ASN1Element> x400Addresses; 185 186 // The directory names included in the extension. 187 @NotNull private final List<DN> directoryNames; 188 189 // The IP addresses included in the extension. 190 @NotNull private final List<InetAddress> ipAddresses; 191 192 // The other names included in the extension. 193 @NotNull private final List<ObjectPair<OID,ASN1Element>> otherNames; 194 195 // The registered IDs included in the extension. 196 @NotNull private final List<OID> registeredIDs; 197 198 // The DNS names included in the extension. 199 @NotNull private final List<String> dnsNames; 200 201 // The RFC 822 names (email addresses) in the extension. 202 @NotNull private final List<String> rfc822Names; 203 204 // The uniform resource identifiers in the extension. 205 @NotNull private final List<String> uniformResourceIdentifiers; 206 207 208 209 /** 210 * Creates a new general names object from the provided information. 211 * 212 * @param otherNames The list of other names to include in 213 * the object. This must not be 214 * {@code null} but may be empty. 215 * @param rfc822Names The list of RFC 822 names (email 216 * addresses) to include in the object. 217 * This must not be {@code null} but may 218 * be empty. 219 * @param dnsNames The list of DNS name values to include 220 * in the object. This must not be 221 * {@code null} but may be empty. 222 * @param x400Addresses The list of X.400 address values to 223 * include in the object. This must not 224 * be {@code null} but may be empty. 225 * @param directoryNames The list of directory name values to 226 * include in the object. This must not 227 * be {@code null} but may be empty. 228 * @param ediPartyNames The list of EDI party name values to 229 * include in the object. This must not 230 * be {@code null} but may be empty. 231 * @param uniformResourceIdentifiers The list of uniform resource 232 * identifier values to include in the 233 * object. This must not be {@code null} 234 * but may be empty. 235 * @param ipAddresses The list of IP address values to 236 * include in the object. This must not 237 * be {@code null} but may be empty. 238 * @param registeredIDs The list of registered ID values to 239 * include in the object. This must not 240 * be {@code null} but may be empty. 241 */ 242 GeneralNames(@NotNull final List<ObjectPair<OID,ASN1Element>> otherNames, 243 @NotNull final List<String> rfc822Names, 244 @NotNull final List<String> dnsNames, 245 @NotNull final List<ASN1Element> x400Addresses, 246 @NotNull final List<DN> directoryNames, 247 @NotNull final List<ASN1Element> ediPartyNames, 248 @NotNull final List<String> uniformResourceIdentifiers, 249 @NotNull final List<InetAddress> ipAddresses, 250 @NotNull final List<OID> registeredIDs) 251 { 252 this.otherNames = otherNames; 253 this.rfc822Names = rfc822Names; 254 this.dnsNames = dnsNames; 255 this.x400Addresses = x400Addresses; 256 this.directoryNames = directoryNames; 257 this.ediPartyNames = ediPartyNames; 258 this.uniformResourceIdentifiers = uniformResourceIdentifiers; 259 this.ipAddresses = ipAddresses; 260 this.registeredIDs = registeredIDs; 261 } 262 263 264 265 /** 266 * Creates a new general names object that is decoded from the provided ASN.1 267 * element. 268 * 269 * @param element The ASN.1 element to decode as a general names object. 270 * 271 * @throws CertException If the provided element cannot be decoded as a 272 * general names element. 273 */ 274 GeneralNames(@NotNull final ASN1Element element) 275 throws CertException 276 { 277 try 278 { 279 final ASN1Element[] elements = element.decodeAsSequence().elements(); 280 final ArrayList<ASN1Element> ediPartyList = 281 new ArrayList<>(elements.length); 282 final ArrayList<ASN1Element> x400AddressList = 283 new ArrayList<>(elements.length); 284 final ArrayList<DN> directoryNameList = new ArrayList<>(elements.length); 285 final ArrayList<InetAddress> ipAddressList = 286 new ArrayList<>(elements.length); 287 final ArrayList<ObjectPair<OID,ASN1Element>> otherNameList = 288 new ArrayList<>(elements.length); 289 final ArrayList<OID> registeredIDList = 290 new ArrayList<>(elements.length); 291 final ArrayList<String> dnsNameList = new ArrayList<>(elements.length); 292 final ArrayList<String> rfc822NameList = new ArrayList<>(elements.length); 293 final ArrayList<String> uriList = new ArrayList<>(elements.length); 294 295 for (final ASN1Element e : elements) 296 { 297 switch (e.getType()) 298 { 299 case NAME_TYPE_OTHER_NAME: 300 final ASN1Element[] otherNameElements = 301 ASN1Sequence.decodeAsSequence(e).elements(); 302 final OID otherNameOID = 303 ASN1ObjectIdentifier.decodeAsObjectIdentifier( 304 otherNameElements[0]).getOID(); 305 final ASN1Element otherNameValue = 306 ASN1Element.decode(otherNameElements[1].getValue()); 307 otherNameList.add(new ObjectPair<>(otherNameOID, otherNameValue)); 308 break; 309 case NAME_TYPE_RFC_822_NAME: 310 rfc822NameList.add( 311 ASN1IA5String.decodeAsIA5String(e).stringValue()); 312 break; 313 case NAME_TYPE_DNS_NAME: 314 dnsNameList.add(ASN1IA5String.decodeAsIA5String(e).stringValue()); 315 break; 316 case NAME_TYPE_X400_ADDRESS: 317 x400AddressList.add(e); 318 break; 319 case NAME_TYPE_DIRECTORY_NAME: 320 final ASN1Element innerElement = ASN1Element.decode(e.getValue()); 321 directoryNameList.add(X509Certificate.decodeName(innerElement)); 322 break; 323 case NAME_TYPE_EDI_PARTY_NAME: 324 ediPartyList.add(e); 325 break; 326 case NAME_TYPE_UNIFORM_RESOURCE_IDENTIFIER: 327 uriList.add(ASN1IA5String.decodeAsIA5String(e).stringValue()); 328 break; 329 case NAME_TYPE_IP_ADDRESS: 330 ipAddressList.add(InetAddress.getByAddress(e.getValue())); 331 break; 332 case NAME_TYPE_REGISTERED_ID: 333 registeredIDList.add( 334 ASN1ObjectIdentifier.decodeAsObjectIdentifier(e).getOID()); 335 break; 336 } 337 } 338 339 ediPartyNames = Collections.unmodifiableList(ediPartyList); 340 otherNames = Collections.unmodifiableList(otherNameList); 341 registeredIDs = Collections.unmodifiableList(registeredIDList); 342 x400Addresses = Collections.unmodifiableList(x400AddressList); 343 directoryNames = Collections.unmodifiableList(directoryNameList); 344 ipAddresses = Collections.unmodifiableList(ipAddressList); 345 dnsNames = Collections.unmodifiableList(dnsNameList); 346 rfc822Names = Collections.unmodifiableList(rfc822NameList); 347 uniformResourceIdentifiers = Collections.unmodifiableList(uriList); 348 } 349 catch (final Exception e) 350 { 351 Debug.debugException(e); 352 throw new CertException( 353 ERR_GENERAL_NAMES_CANNOT_PARSE.get( 354 StaticUtils.getExceptionMessage(e)), 355 e); 356 } 357 } 358 359 360 361 /** 362 * Encodes this general names object to an ASN.1 element for use in a 363 * certificate extension. 364 * 365 * @return The encoded general names object. 366 * 367 * @throws CertException If a problem is encountered while encoding the 368 * set of general name values. 369 */ 370 @NotNull() 371 ASN1Element encode() 372 throws CertException 373 { 374 try 375 { 376 final ArrayList<ASN1Element> elements = new ArrayList<>(10); 377 for (final ObjectPair<OID,ASN1Element> otherName : otherNames) 378 { 379 elements.add(new ASN1Sequence(NAME_TYPE_OTHER_NAME, 380 new ASN1ObjectIdentifier(otherName.getFirst()), 381 new ASN1Element(NAME_TYPE_OTHER_NAME_VALUE, 382 otherName.getSecond().encode()))); 383 } 384 385 for (final String rfc822Name : rfc822Names) 386 { 387 elements.add(new ASN1IA5String(NAME_TYPE_RFC_822_NAME, rfc822Name)); 388 } 389 390 for (final String dnsName : dnsNames) 391 { 392 elements.add(new ASN1IA5String(NAME_TYPE_DNS_NAME, dnsName)); 393 } 394 395 for (final ASN1Element x400Address : x400Addresses) 396 { 397 elements.add(new ASN1Element(NAME_TYPE_X400_ADDRESS, 398 x400Address.getValue())); 399 } 400 401 for (final DN directoryName : directoryNames) 402 { 403 elements.add(new ASN1Element(NAME_TYPE_DIRECTORY_NAME, 404 X509Certificate.encodeName(directoryName).encode())); 405 } 406 407 for (final ASN1Element ediPartyName : ediPartyNames) 408 { 409 elements.add(new ASN1Element(NAME_TYPE_EDI_PARTY_NAME, 410 ediPartyName.getValue())); 411 } 412 413 for (final String uri : uniformResourceIdentifiers) 414 { 415 elements.add(new ASN1IA5String(NAME_TYPE_UNIFORM_RESOURCE_IDENTIFIER, 416 uri)); 417 } 418 419 for (final InetAddress ipAddress : ipAddresses) 420 { 421 elements.add(new ASN1OctetString(NAME_TYPE_IP_ADDRESS, 422 ipAddress.getAddress())); 423 } 424 425 for (final OID registeredID : registeredIDs) 426 { 427 elements.add(new ASN1ObjectIdentifier(NAME_TYPE_REGISTERED_ID, 428 registeredID)); 429 } 430 431 return new ASN1Sequence(elements); 432 } 433 catch (final Exception e) 434 { 435 Debug.debugException(e); 436 throw new CertException( 437 ERR_GENERAL_NAMES_CANNOT_ENCODE.get(toString(), 438 StaticUtils.getExceptionMessage(e)), 439 e); 440 } 441 } 442 443 444 445 /** 446 * Retrieves the otherName elements from the extension. 447 * 448 * @return The otherName elements from the extension. 449 */ 450 @NotNull() 451 public List<ObjectPair<OID,ASN1Element>> getOtherNames() 452 { 453 return otherNames; 454 } 455 456 457 458 /** 459 * Retrieves the RFC 822 names (email addresses) from the extension. 460 * 461 * @return The RFC 822 names from the extension. 462 */ 463 @NotNull() 464 public List<String> getRFC822Names() 465 { 466 return rfc822Names; 467 } 468 469 470 471 /** 472 * Retrieves the DNS names from the extension. 473 * 474 * @return The DNS names from the extension. 475 */ 476 @NotNull() 477 public List<String> getDNSNames() 478 { 479 return dnsNames; 480 } 481 482 483 484 /** 485 * Retrieves the x400Address elements from the extension. 486 * 487 * @return The x400Address elements from the extension. 488 */ 489 @NotNull() 490 public List<ASN1Element> getX400Addresses() 491 { 492 return x400Addresses; 493 } 494 495 496 497 /** 498 * Retrieves the directory names from the extension. 499 * 500 * @return The directory names from the extension. 501 */ 502 @NotNull() 503 public List<DN> getDirectoryNames() 504 { 505 return directoryNames; 506 } 507 508 509 510 /** 511 * Retrieves the ediPartyName elements from the extensions. 512 * 513 * @return The ediPartyName elements from the extension. 514 */ 515 @NotNull() 516 public List<ASN1Element> getEDIPartyNames() 517 { 518 return ediPartyNames; 519 } 520 521 522 523 /** 524 * Retrieves the uniform resource identifiers (URIs) from the extension. 525 * 526 * @return The URIs from the extension. 527 */ 528 @NotNull() 529 public List<String> getUniformResourceIdentifiers() 530 { 531 return uniformResourceIdentifiers; 532 } 533 534 535 536 /** 537 * Retrieves the IP addresses from the extension. 538 * 539 * @return The IP addresses from the extension. 540 */ 541 @NotNull() 542 public List<InetAddress> getIPAddresses() 543 { 544 return ipAddresses; 545 } 546 547 548 549 /** 550 * Retrieves the registeredID elements from the extension. 551 * 552 * @return The registeredID elements from the extension. 553 */ 554 @NotNull() 555 public List<OID> getRegisteredIDs() 556 { 557 return registeredIDs; 558 } 559 560 561 562 /** 563 * Retrieves a string representation of this general names element. 564 * 565 * @return A string representation of this general names element. 566 */ 567 @Override() 568 @NotNull() 569 public String toString() 570 { 571 final StringBuilder buffer = new StringBuilder(); 572 toString(buffer); 573 return buffer.toString(); 574 } 575 576 577 578 /** 579 * Appends a string representation of this general names element to the 580 * provided buffer. 581 * 582 * @param buffer The buffer to which the information should be appended. 583 */ 584 public void toString(@NotNull final StringBuilder buffer) 585 { 586 buffer.append("GeneralNames("); 587 588 boolean appended = false; 589 if (! dnsNames.isEmpty()) 590 { 591 buffer.append("dnsNames={"); 592 593 final Iterator<String> iterator = dnsNames.iterator(); 594 while (iterator.hasNext()) 595 { 596 buffer.append('\''); 597 buffer.append(iterator.next()); 598 buffer.append('\''); 599 600 if (iterator.hasNext()) 601 { 602 buffer.append(','); 603 } 604 } 605 606 buffer.append('}'); 607 appended = true; 608 } 609 610 if (! ipAddresses.isEmpty()) 611 { 612 if (appended) 613 { 614 buffer.append(", "); 615 } 616 617 buffer.append("ipAddresses={"); 618 619 final Iterator<InetAddress> iterator = ipAddresses.iterator(); 620 while (iterator.hasNext()) 621 { 622 buffer.append('\''); 623 buffer.append(iterator.next().getHostAddress()); 624 buffer.append('\''); 625 626 if (iterator.hasNext()) 627 { 628 buffer.append(','); 629 } 630 } 631 632 buffer.append('}'); 633 appended = true; 634 } 635 636 if (! rfc822Names.isEmpty()) 637 { 638 if (appended) 639 { 640 buffer.append(", "); 641 } 642 643 buffer.append("rfc822Names={"); 644 645 final Iterator<String> iterator = rfc822Names.iterator(); 646 while (iterator.hasNext()) 647 { 648 buffer.append('\''); 649 buffer.append(iterator.next()); 650 buffer.append('\''); 651 652 if (iterator.hasNext()) 653 { 654 buffer.append(','); 655 } 656 } 657 658 buffer.append('}'); 659 appended = true; 660 } 661 662 if (! directoryNames.isEmpty()) 663 { 664 if (appended) 665 { 666 buffer.append(", "); 667 } 668 669 buffer.append("directoryNames={"); 670 671 final Iterator<DN> iterator = directoryNames.iterator(); 672 while (iterator.hasNext()) 673 { 674 buffer.append('\''); 675 buffer.append(iterator.next()); 676 buffer.append('\''); 677 678 if (iterator.hasNext()) 679 { 680 buffer.append(','); 681 } 682 } 683 684 buffer.append('}'); 685 appended = true; 686 } 687 688 if (! uniformResourceIdentifiers.isEmpty()) 689 { 690 if (appended) 691 { 692 buffer.append(", "); 693 } 694 695 buffer.append("uniformResourceIdentifiers={"); 696 697 final Iterator<String> iterator = uniformResourceIdentifiers.iterator(); 698 while (iterator.hasNext()) 699 { 700 buffer.append('\''); 701 buffer.append(iterator.next()); 702 buffer.append('\''); 703 704 if (iterator.hasNext()) 705 { 706 buffer.append(','); 707 } 708 } 709 710 buffer.append('}'); 711 appended = true; 712 } 713 714 if (! registeredIDs.isEmpty()) 715 { 716 if (appended) 717 { 718 buffer.append(", "); 719 } 720 721 buffer.append("registeredIDs={"); 722 723 final Iterator<OID> iterator = registeredIDs.iterator(); 724 while (iterator.hasNext()) 725 { 726 buffer.append('\''); 727 buffer.append(iterator.next()); 728 buffer.append('\''); 729 730 if (iterator.hasNext()) 731 { 732 buffer.append(','); 733 } 734 } 735 736 buffer.append('}'); 737 appended = true; 738 } 739 740 if (! otherNames.isEmpty()) 741 { 742 if (appended) 743 { 744 buffer.append(", "); 745 } 746 747 buffer.append("otherNameCount="); 748 buffer.append(otherNames.size()); 749 } 750 751 if (! x400Addresses.isEmpty()) 752 { 753 if (appended) 754 { 755 buffer.append(", "); 756 } 757 758 buffer.append("x400AddressCount="); 759 buffer.append(x400Addresses.size()); 760 } 761 762 if (! ediPartyNames.isEmpty()) 763 { 764 if (appended) 765 { 766 buffer.append(", "); 767 } 768 769 buffer.append("ediPartyNameCount="); 770 buffer.append(ediPartyNames.size()); 771 } 772 773 buffer.append(')'); 774 } 775}