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