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