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.util.ArrayList;
026import java.util.Collections;
027import java.util.Iterator;
028import java.util.List;
029
030import com.unboundid.asn1.ASN1Element;
031import com.unboundid.asn1.ASN1Sequence;
032import com.unboundid.util.Debug;
033import com.unboundid.util.NotMutable;
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 an implementation of the CRL distribution points X.509
045 * certificate extension as described in
046 * <A HREF="https://www.ietf.org/rfc/rfc5280.txt">RFC 5280</A> section 4.2.1.13.
047 * This can be used to provide information about the location of certificate
048 * revocation lists (CRLs) that can be examined to check the validity of this
049 * certificate.
050 * <BR><BR>
051 * The OID for this extension is 2.5.29.31 and the value has the following
052 * encoding:
053 * <PRE>
054 *   CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
055 *
056 *   DistributionPoint ::= SEQUENCE {
057 *        distributionPoint       [0]     DistributionPointName OPTIONAL,
058 *        reasons                 [1]     ReasonFlags OPTIONAL,
059 *        cRLIssuer               [2]     GeneralNames OPTIONAL }
060 *
061 *   DistributionPointName ::= CHOICE {
062 *        fullName                [0]     GeneralNames,
063 *        nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
064 *
065 *   ReasonFlags ::= BIT STRING {
066 *        unused                  (0),
067 *        keyCompromise           (1),
068 *        cACompromise            (2),
069 *        affiliationChanged      (3),
070 *        superseded              (4),
071 *        cessationOfOperation    (5),
072 *        certificateHold         (6),
073 *        privilegeWithdrawn      (7),
074 *        aACompromise            (8) }
075 * </PRE>
076 */
077@NotMutable()
078@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
079public final class CRLDistributionPointsExtension
080       extends X509CertificateExtension
081{
082  /**
083   * The OID (2.5.29.31) for CRL distribution points extensions.
084   */
085  public static final OID CRL_DISTRIBUTION_POINTS_OID = new OID("2.5.29.31");
086
087
088
089  /**
090   * The serial version UID for this serializable class.
091   */
092  private static final long serialVersionUID = -4710958813506834961L;
093
094
095
096  // The list of CRL distribution points included in this extension.
097  private final List<CRLDistributionPoint> crlDistributionPoints;
098
099
100
101  /**
102   * Creates a new CRL distribution points extension with the provided
103   * information.
104   *
105   * @param  isCritical             Indicates whether this extension should be
106   *                                considered critical.
107   * @param  crlDistributionPoints  The distribution points to include in this
108   *                                extension.  It must not be {@code null} or
109   *                                empty.
110   *
111   * @throws  CertException  If a problem is encountered while trying to encode
112   *                         the value for this extension.
113   */
114  CRLDistributionPointsExtension(final boolean isCritical,
115       final List<CRLDistributionPoint> crlDistributionPoints)
116       throws CertException
117  {
118    super(CRL_DISTRIBUTION_POINTS_OID, isCritical,
119         encodeValue(crlDistributionPoints));
120
121    this.crlDistributionPoints = crlDistributionPoints;
122  }
123
124
125
126  /**
127   * Creates a new CRL distribution points extension from the provided generic
128   * extension.
129   *
130   * @param  extension  The extension to decode as a CRL distribution points
131   *                    extension.
132   *
133   * @throws  CertException  If the provided extension cannot be decoded as a
134   *                         CRL distribution points extension.
135   */
136  CRLDistributionPointsExtension(final X509CertificateExtension extension)
137       throws CertException
138  {
139    super(extension);
140
141    try
142    {
143      final ASN1Element[] elements =
144           ASN1Sequence.decodeAsSequence(extension.getValue()).elements();
145      final ArrayList<CRLDistributionPoint> dps =
146           new ArrayList<>(elements.length);
147      for (final ASN1Element e : elements)
148      {
149        dps.add(new CRLDistributionPoint(e));
150      }
151
152      crlDistributionPoints = Collections.unmodifiableList(dps);
153    }
154    catch (final Exception e)
155    {
156      Debug.debugException(e);
157      throw new CertException(
158           ERR_CRL_DP_EXTENSION_CANNOT_PARSE.get(
159                String.valueOf(extension), StaticUtils.getExceptionMessage(e)),
160           e);
161    }
162  }
163
164
165
166  /**
167   * Encodes the provided information into a form for use as the value for this
168   * extension.
169   *
170   * @param  crlDistributionPoints  The distribution points to include in this
171   *                                extension.  It must not be {@code null} or
172   *                                empty.
173   *
174   * @return  The encoded value.
175   *
176   * @throws  CertException  If a problem is encountered while trying to encode
177   *                         this extension.
178   */
179  private static byte[] encodeValue(
180               final List<CRLDistributionPoint> crlDistributionPoints)
181          throws CertException
182  {
183    final ArrayList<ASN1Element> elements =
184         new ArrayList<>(crlDistributionPoints.size());
185    for (final CRLDistributionPoint p : crlDistributionPoints)
186    {
187      elements.add(p.encode());
188    }
189
190    return new ASN1Sequence(elements).encode();
191  }
192
193
194
195  /**
196   * Retrieves the list of CRL distribution points included in this extension.
197   *
198   * @return  The list of CRL distribution points included in this extension.
199   */
200  public List<CRLDistributionPoint> getCRLDistributionPoints()
201  {
202    return crlDistributionPoints;
203  }
204
205
206
207  /**
208   * {@inheritDoc}
209   */
210  @Override()
211  public String getExtensionName()
212  {
213    return INFO_CRL_DP_EXTENSION_NAME.get();
214  }
215
216
217
218  /**
219   * {@inheritDoc}
220   */
221  @Override()
222  public void toString(final StringBuilder buffer)
223  {
224    buffer.append("CRLDistributionPointsExtension(oid='");
225    buffer.append(getOID());
226    buffer.append("', isCritical=");
227    buffer.append(isCritical());
228    buffer.append(", distributionPoints={");
229
230    final Iterator<CRLDistributionPoint> iterator =
231         crlDistributionPoints.iterator();
232    while (iterator.hasNext())
233    {
234      iterator.next().toString(buffer);
235      if (iterator.hasNext())
236      {
237        buffer.append(", ");
238      }
239    }
240
241    buffer.append("})");
242  }
243}