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