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}