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