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  /**
128   * Creates a new CRL distribution points extension from the provided generic
129   * extension.
130   *
131   * @param  extension  The extension to decode as a CRL distribution points
132   *                    extension.
133   *
134   * @throws  CertException  If the provided extension cannot be decoded as a
135   *                         CRL distribution points extension.
136   */
137  CRLDistributionPointsExtension(final X509CertificateExtension extension)
138       throws CertException
139  {
140    super(extension);
141
142    try
143    {
144      final ASN1Element[] elements =
145           ASN1Sequence.decodeAsSequence(extension.getValue()).elements();
146      final ArrayList<CRLDistributionPoint> dps =
147           new ArrayList<>(elements.length);
148      for (final ASN1Element e : elements)
149      {
150        dps.add(new CRLDistributionPoint(e));
151      }
152
153      crlDistributionPoints = Collections.unmodifiableList(dps);
154    }
155    catch (final Exception e)
156    {
157      Debug.debugException(e);
158      throw new CertException(
159           ERR_CRL_DP_EXTENSION_CANNOT_PARSE.get(
160                String.valueOf(extension), StaticUtils.getExceptionMessage(e)),
161           e);
162    }
163  }
164
165
166
167  /**
168   * Encodes the provided information into a form for use as the value for this
169   * extension.
170   *
171   * @param  crlDistributionPoints  The distribution points to include in this
172   *                                extension.  It must not be {@code null} or
173   *                                empty.
174   *
175   * @return  The encoded value.
176   *
177   * @throws  CertException  If a problem is encountered while trying to encode
178   *                         this extension.
179   */
180  private static byte[] encodeValue(
181               final List<CRLDistributionPoint> crlDistributionPoints)
182          throws CertException
183  {
184    final ArrayList<ASN1Element> elements =
185         new ArrayList<>(crlDistributionPoints.size());
186    for (final CRLDistributionPoint p : crlDistributionPoints)
187    {
188      elements.add(p.encode());
189    }
190
191    return new ASN1Sequence(elements).encode();
192  }
193
194
195
196  /**
197   * Retrieves the list of CRL distribution points included in this extension.
198   *
199   * @return  The list of CRL distribution points included in this extension.
200   */
201  public List<CRLDistributionPoint> getCRLDistributionPoints()
202  {
203    return crlDistributionPoints;
204  }
205
206
207
208  /**
209   * {@inheritDoc}
210   */
211  @Override()
212  public String getExtensionName()
213  {
214    return INFO_CRL_DP_EXTENSION_NAME.get();
215  }
216
217
218
219  /**
220   * {@inheritDoc}
221   */
222  @Override()
223  public void toString(final StringBuilder buffer)
224  {
225    buffer.append("CRLDistributionPointsExtension(oid='");
226    buffer.append(getOID());
227    buffer.append("', isCritical=");
228    buffer.append(isCritical());
229    buffer.append(", distributionPoints={");
230
231    final Iterator<CRLDistributionPoint> iterator =
232         crlDistributionPoints.iterator();
233    while (iterator.hasNext())
234    {
235      iterator.next().toString(buffer);
236      if (iterator.hasNext())
237      {
238        buffer.append(", ");
239      }
240    }
241
242    buffer.append("})");
243  }
244}