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.net.InetAddress;
041import java.util.Iterator;
042import java.util.List;
043
044import com.unboundid.ldap.sdk.DN;
045import com.unboundid.asn1.ASN1Element;
046import com.unboundid.util.Debug;
047import com.unboundid.util.NotExtensible;
048import com.unboundid.util.NotNull;
049import com.unboundid.util.ObjectPair;
050import com.unboundid.util.OID;
051import com.unboundid.util.StaticUtils;
052import com.unboundid.util.ThreadSafety;
053import com.unboundid.util.ThreadSafetyLevel;
054
055import static com.unboundid.util.ssl.cert.CertMessages.*;
056
057
058
059/**
060 * This class provides support for decoding the values of the
061 * {@link SubjectAlternativeNameExtension} and
062 * {@link IssuerAlternativeNameExtension} extensions as described in
063 * <A HREF="https://www.ietf.org/rfc/rfc5280.txt">RFC 5280</A> sections 4.2.1.6
064 * and 4.2.1.7.
065 * <BR><BR>
066 * Note that this implementation only provides complete decoding for the RFC 822
067 * names (email addresses), DNS names, directory names, uniform resource
068 * identifiers, and IP addresses elements.  The other elements will be left in
069 * their raw forms.
070 * <BR><BR>
071 * The value has the following encoding:
072 * <PRE>
073 *   SubjectAltName ::= GeneralNames
074 *
075 *   IssuerAltName ::= GeneralNames
076 *
077 *   GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
078 *
079 *   GeneralName ::= CHOICE {
080 *        otherName                       [0]     OtherName,
081 *        rfc822Name                      [1]     IA5String,
082 *        dNSName                         [2]     IA5String,
083 *        x400Address                     [3]     ORAddress,
084 *        directoryName                   [4]     Name,
085 *        ediPartyName                    [5]     EDIPartyName,
086 *        uniformResourceIdentifier       [6]     IA5String,
087 *        iPAddress                       [7]     OCTET STRING,
088 *        registeredID                    [8]     OBJECT IDENTIFIER }
089 *
090 *   OtherName ::= SEQUENCE {
091 *        type-id    OBJECT IDENTIFIER,
092 *        value      [0] EXPLICIT ANY DEFINED BY type-id }
093 *
094 *   EDIPartyName ::= SEQUENCE {
095 *        nameAssigner            [0]     DirectoryString OPTIONAL,
096 *        partyName               [1]     DirectoryString }
097 * </PRE>
098 */
099@NotExtensible()
100@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
101public abstract class GeneralAlternativeNameExtension
102       extends X509CertificateExtension
103{
104  /**
105   * The serial version UID for this serializable class.
106   */
107  private static final long serialVersionUID = -1076071031835517176L;
108
109
110
111  // The general names for inclusion in this extension.
112  @NotNull private final GeneralNames generalNames;
113
114
115
116  /**
117   * Creates a new general alternative name extension with the provided
118   * information.
119   *
120   * @param  oid           The OID for this extension.
121   * @param  isCritical    Indicates whether this extension should be
122   *                       considered critical.
123   * @param  generalNames  The general names for inclusion in this extension.
124   *
125   * @throws  CertException  If a problem is encountered while encoding the
126   *                         value for this extension.
127   */
128  protected GeneralAlternativeNameExtension(@NotNull final OID oid,
129                 final boolean isCritical,
130                 @NotNull final GeneralNames generalNames)
131       throws CertException
132  {
133    super(oid, isCritical, generalNames.encode().encode());
134
135    this.generalNames = generalNames;
136  }
137
138
139
140  /**
141   * Creates a new general alternative name extension from the provided generic
142   * extension.
143   *
144   * @param  extension  The extension to decode as a general alternative name
145   *                    extension.
146   *
147   * @throws  CertException  If the provided extension cannot be decoded as a
148   *                         general alternative name extension.
149   */
150  protected GeneralAlternativeNameExtension(
151                 @NotNull final X509CertificateExtension extension)
152            throws CertException
153  {
154    super(extension);
155
156    try
157    {
158      generalNames = new GeneralNames(ASN1Element.decode(extension.getValue()));
159    }
160    catch (final Exception e)
161    {
162      Debug.debugException(e);
163
164      final String name;
165      if (extension.getOID().equals(SubjectAlternativeNameExtension.
166           SUBJECT_ALTERNATIVE_NAME_OID))
167      {
168        name = INFO_SUBJECT_ALT_NAME_EXTENSION_NAME.get();
169      }
170      else if (extension.getOID().equals(IssuerAlternativeNameExtension.
171           ISSUER_ALTERNATIVE_NAME_OID))
172      {
173        name = INFO_ISSUER_ALT_NAME_EXTENSION_NAME.get();
174      }
175      else
176      {
177        name = extension.getOID().toString();
178      }
179
180      throw new CertException(
181           ERR_GENERAL_ALT_NAME_EXTENSION_CANNOT_PARSE.get(
182                String.valueOf(extension), name,
183                StaticUtils.getExceptionMessage(e)),
184           e);
185    }
186  }
187
188
189
190  /**
191   * Retrieves the {@code GeneralNames} object for this alternative name
192   * extension.
193   *
194   * @return  The {@code GeneralNames} object for this alternative name
195   *          extension.
196   */
197  @NotNull()
198  public final GeneralNames getGeneralNames()
199  {
200    return generalNames;
201  }
202
203
204
205  /**
206   * Retrieves the otherName elements from the extension.
207   *
208   * @return  The otherName elements from the extension.
209   */
210  @NotNull()
211  public final List<ObjectPair<OID,ASN1Element>> getOtherNames()
212  {
213    return generalNames.getOtherNames();
214  }
215
216
217
218  /**
219   * Retrieves the RFC 822 names (email addresses) from the extension.
220   *
221   * @return  The RFC 822 names from the extension.
222   */
223  @NotNull()
224  public final List<String> getRFC822Names()
225  {
226    return generalNames.getRFC822Names();
227  }
228
229
230
231  /**
232   * Retrieves the DNS names from the extension.
233   *
234   * @return  The DNS names from the extension.
235   */
236  @NotNull()
237  public final List<String> getDNSNames()
238  {
239    return generalNames.getDNSNames();
240  }
241
242
243
244  /**
245   * Retrieves the x400Address elements from the extension.
246   *
247   * @return  The x400Address elements from the extension.
248   */
249  @NotNull()
250  public final List<ASN1Element> getX400Addresses()
251  {
252    return generalNames.getX400Addresses();
253  }
254
255
256
257  /**
258   * Retrieves the directory names from the extension.
259   *
260   * @return  The directory names from the extension.
261   */
262  @NotNull()
263  public final List<DN> getDirectoryNames()
264  {
265    return generalNames.getDirectoryNames();
266  }
267
268
269
270  /**
271   * Retrieves the ediPartyName elements from the extensions.
272   *
273   * @return  The ediPartyName elements from the extension.
274   */
275  @NotNull()
276  public final List<ASN1Element> getEDIPartyNames()
277  {
278    return generalNames.getEDIPartyNames();
279  }
280
281
282
283  /**
284   * Retrieves the uniform resource identifiers (URIs) from the extension.
285   *
286   * @return  The URIs from the extension.
287   */
288  @NotNull()
289  public final List<String> getUniformResourceIdentifiers()
290  {
291    return generalNames.getUniformResourceIdentifiers();
292  }
293
294
295
296  /**
297   * Retrieves the IP addresses from the extension.
298   *
299   * @return  The IP addresses from the extension.
300   */
301  @NotNull()
302  public final List<InetAddress> getIPAddresses()
303  {
304    return generalNames.getIPAddresses();
305  }
306
307
308
309  /**
310   * Retrieves the registeredID elements from the extension.
311   *
312   * @return  The registeredID elements from the extension.
313   */
314  @NotNull()
315  public final List<OID> getRegisteredIDs()
316  {
317    return generalNames.getRegisteredIDs();
318  }
319
320
321
322  /**
323   * Appends a string representation of this extension to the provided buffer.
324   *
325   * @param  extensionName  The name to use for this extension.
326   * @param  buffer         The buffer to which the information should be
327   *                        appended.
328   */
329  protected void toString(@NotNull final String extensionName,
330                          @NotNull final StringBuilder buffer)
331  {
332    buffer.append(extensionName);
333    buffer.append("(oid='");
334    buffer.append(getOID());
335    buffer.append("', isCritical=");
336    buffer.append(isCritical());
337
338    if (! getDNSNames().isEmpty())
339    {
340      buffer.append(", dnsNames={");
341
342      final Iterator<String> iterator = getDNSNames().iterator();
343      while (iterator.hasNext())
344      {
345        buffer.append('\'');
346        buffer.append(iterator.next());
347        buffer.append('\'');
348
349        if (iterator.hasNext())
350        {
351          buffer.append(',');
352        }
353      }
354
355      buffer.append('}');
356    }
357
358    if (! getIPAddresses().isEmpty())
359    {
360      buffer.append(", ipAddresses={");
361
362      final Iterator<InetAddress> iterator = getIPAddresses().iterator();
363      while (iterator.hasNext())
364      {
365        buffer.append('\'');
366        buffer.append(iterator.next().getHostAddress());
367        buffer.append('\'');
368
369        if (iterator.hasNext())
370        {
371          buffer.append(',');
372        }
373      }
374
375      buffer.append('}');
376    }
377
378    if (! getRFC822Names().isEmpty())
379    {
380      buffer.append(", rfc822Names={");
381
382      final Iterator<String> iterator = getRFC822Names().iterator();
383      while (iterator.hasNext())
384      {
385        buffer.append('\'');
386        buffer.append(iterator.next());
387        buffer.append('\'');
388
389        if (iterator.hasNext())
390        {
391          buffer.append(',');
392        }
393      }
394
395      buffer.append('}');
396    }
397
398    if (! getDirectoryNames().isEmpty())
399    {
400      buffer.append(", directoryNames={");
401
402      final Iterator<DN> iterator = getDirectoryNames().iterator();
403      while (iterator.hasNext())
404      {
405        buffer.append('\'');
406        buffer.append(iterator.next());
407        buffer.append('\'');
408
409        if (iterator.hasNext())
410        {
411          buffer.append(',');
412        }
413      }
414
415      buffer.append('}');
416    }
417
418    if (! getUniformResourceIdentifiers().isEmpty())
419    {
420      buffer.append(", uniformResourceIdentifiers={");
421
422      final Iterator<String> iterator =
423           getUniformResourceIdentifiers().iterator();
424      while (iterator.hasNext())
425      {
426        buffer.append('\'');
427        buffer.append(iterator.next());
428        buffer.append('\'');
429
430        if (iterator.hasNext())
431        {
432          buffer.append(',');
433        }
434      }
435
436      buffer.append('}');
437    }
438
439    if (! getRegisteredIDs().isEmpty())
440    {
441      buffer.append(", registeredIDs={");
442
443      final Iterator<OID> iterator = getRegisteredIDs().iterator();
444      while (iterator.hasNext())
445      {
446        buffer.append('\'');
447        buffer.append(iterator.next());
448        buffer.append('\'');
449
450        if (iterator.hasNext())
451        {
452          buffer.append(',');
453        }
454      }
455
456      buffer.append('}');
457    }
458
459    if (! getOtherNames().isEmpty())
460    {
461      buffer.append(", otherNameCount=");
462      buffer.append(getOtherNames().size());
463    }
464
465    if (! getX400Addresses().isEmpty())
466    {
467      buffer.append(", x400AddressCount=");
468      buffer.append(getX400Addresses().size());
469    }
470
471    if (! getEDIPartyNames().isEmpty())
472    {
473      buffer.append(", ediPartyNameCount=");
474      buffer.append(getEDIPartyNames().size());
475    }
476
477    buffer.append(')');
478  }
479}