001/*
002 * Copyright 2017-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2017-2019 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.math.BigInteger;
026import java.util.ArrayList;
027
028import com.unboundid.asn1.ASN1BigInteger;
029import com.unboundid.asn1.ASN1Element;
030import com.unboundid.asn1.ASN1OctetString;
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 authority key identifier 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.1.
047 * The OID for this extension is 2.5.29.35 and the value has the following
048 * encoding:
049 * <PRE>
050 *   AuthorityKeyIdentifier ::= SEQUENCE {
051 *      keyIdentifier             [0] KeyIdentifier           OPTIONAL,
052 *      authorityCertIssuer       [1] GeneralNames            OPTIONAL,
053 *      authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL  }
054 * </PRE>
055 * The actual format of the key identifier is not specified, although RFC 5280
056 * does specify a couple of possibilities.
057 */
058@NotMutable()
059@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
060public final class AuthorityKeyIdentifierExtension
061       extends X509CertificateExtension
062{
063  /**
064   * The OID (2.5.29.35) for authority key identifier extensions.
065   */
066  public static final OID AUTHORITY_KEY_IDENTIFIER_OID = new OID("2.5.29.35");
067
068
069
070  /**
071   * The DER type for the key identifier element in the value sequence.
072   */
073  private static final byte TYPE_KEY_IDENTIFIER = (byte) 0x80;
074
075
076
077  /**
078   * The DER type for the authority cert issuer element in the value sequence.
079   */
080  private static final byte TYPE_AUTHORITY_CERT_ISSUER = (byte) 0xA1;
081
082
083
084  /**
085   * The DER type for the authority cert serial number element in the value
086   * sequence.
087   */
088  private static final byte TYPE_AUTHORITY_CERT_SERIAL_NUMBER = (byte) 0x82;
089
090
091
092  /**
093   * The serial version UID for this serializable class.
094   */
095  private static final long serialVersionUID = 8913323557731547122L;
096
097
098
099  // The key identifier for this extension.
100  private final ASN1OctetString keyIdentifier;
101
102  // The serial number for the authority certificate.
103  private final BigInteger authorityCertSerialNumber;
104
105  // General names for the authority certificate.
106  private final GeneralNames authorityCertIssuer;
107
108
109
110  /**
111   * Creates a new authority key identifier extension with the provided
112   * information.
113   *
114   * @param  isCritical                 Indicates whether this extension should
115   *                                    be considered critical.
116   * @param  keyIdentifier              The key identifier.  This may be
117   *                                    {@code null} if it should not be
118   *                                    included in the extension.
119   * @param  authorityCertIssuer        The authority certificate issuer.  This
120   *                                    may be {@code null} if it should not be
121   *                                    included in the extension.
122   * @param  authorityCertSerialNumber  The authority certificate serial number.
123   *                                    This may be {@code null} if it should
124   *                                    not be included in the extension.
125   *
126   * @throws  CertException  If a problem is encountered while encoding the
127   *                         value.
128   */
129  AuthorityKeyIdentifierExtension(final boolean isCritical,
130                                  final ASN1OctetString keyIdentifier,
131                                  final GeneralNames authorityCertIssuer,
132                                  final BigInteger authorityCertSerialNumber)
133       throws CertException
134  {
135    super(AUTHORITY_KEY_IDENTIFIER_OID, isCritical,
136         encodeValue(keyIdentifier, authorityCertIssuer,
137              authorityCertSerialNumber));
138
139    this.keyIdentifier = keyIdentifier;
140    this.authorityCertIssuer = authorityCertIssuer;
141    this.authorityCertSerialNumber = authorityCertSerialNumber;
142  }
143
144
145
146  /**
147   * Creates a new authority key identifier extension from the provided generic
148   * extension.
149   *
150   * @param  extension  The extension to decode as a subject key identifier
151   *                    extension.
152   *
153   * @throws  CertException  If the provided extension cannot be decoded as a
154   *                         subject alternative name extension.
155   */
156  AuthorityKeyIdentifierExtension(final X509CertificateExtension extension)
157       throws CertException
158  {
159    super(extension);
160
161    try
162    {
163      ASN1OctetString keyID = null;
164      BigInteger serialNumber = null;
165      GeneralNames generalNames = null;
166
167      for (final ASN1Element element :
168           ASN1Sequence.decodeAsSequence(extension.getValue()).elements())
169      {
170        switch (element.getType())
171        {
172          case TYPE_KEY_IDENTIFIER:
173            keyID = element.decodeAsOctetString();
174            break;
175          case TYPE_AUTHORITY_CERT_ISSUER:
176            final ASN1Element generalNamesElement =
177                 ASN1Element.decode(element.getValue());
178            generalNames = new GeneralNames(generalNamesElement);
179            break;
180          case TYPE_AUTHORITY_CERT_SERIAL_NUMBER:
181            serialNumber = element.decodeAsBigInteger().getBigIntegerValue();
182            break;
183        }
184      }
185
186      keyIdentifier = keyID;
187      authorityCertIssuer = generalNames;
188      authorityCertSerialNumber = serialNumber;
189    }
190    catch (final Exception e)
191    {
192      Debug.debugException(e);
193      throw new CertException(
194           ERR_AUTHORITY_KEY_ID_EXTENSION_CANNOT_PARSE.get(
195                String.valueOf(extension), StaticUtils.getExceptionMessage(e)),
196           e);
197    }
198  }
199
200
201
202  /**
203   * Encodes the provided information for use as the value of this extension.
204   *
205   * @param  keyIdentifier              The key identifier.  This may be
206   *                                    {@code null} if it should not be
207   *                                    included in the extension.
208   * @param  authorityCertIssuer        The authority certificate issuer.  This
209   *                                    may be {@code null} if it should not be
210   *                                    included in the extension.
211   * @param  authorityCertSerialNumber  The authority certificate serial number.
212   *                                    This may be {@code null} if it should
213   *                                    not be included in the extension.
214   *
215   * @return  The encoded value.
216   *
217   * @throws  CertException  If a problem is encountered while encoding the
218   *                         value.
219   */
220  private static byte[] encodeValue(final ASN1OctetString keyIdentifier,
221                                    final GeneralNames authorityCertIssuer,
222                                    final BigInteger authorityCertSerialNumber)
223          throws CertException
224  {
225    final ArrayList<ASN1Element> elements = new ArrayList<>(3);
226    if (keyIdentifier != null)
227    {
228      elements.add(new ASN1OctetString(TYPE_KEY_IDENTIFIER,
229           keyIdentifier.getValue()));
230    }
231
232    if (authorityCertIssuer != null)
233    {
234      elements.add(new ASN1Element(TYPE_AUTHORITY_CERT_ISSUER,
235           authorityCertIssuer.encode().encode()));
236    }
237
238    if (authorityCertSerialNumber != null)
239    {
240      elements.add(new ASN1BigInteger(TYPE_AUTHORITY_CERT_SERIAL_NUMBER,
241           authorityCertSerialNumber));
242    }
243
244    return new ASN1Sequence(elements).encode();
245  }
246
247
248
249  /**
250   * Retrieves the key identifier for this extension, if available.
251   *
252   * @return  The key identifier for this extension, or {@code null} if it
253   *          was not included in the extension.
254   */
255  public ASN1OctetString getKeyIdentifier()
256  {
257    return keyIdentifier;
258  }
259
260
261
262  /**
263   * Retrieves the general names for the authority certificate, if available.
264   *
265   * @return  The general names for the authority certificate, or {@code null}
266   *          if it was not included in the extension.
267   */
268  public GeneralNames getAuthorityCertIssuer()
269  {
270    return authorityCertIssuer;
271  }
272
273
274
275  /**
276   * Retrieves the serial number for the authority certificate, if available.
277   *
278   * @return  The serial number for the authority certificate, or {@code null}
279   *          if it was not included in the extension.
280   */
281  public BigInteger getAuthorityCertSerialNumber()
282  {
283    return authorityCertSerialNumber;
284  }
285
286
287
288  /**
289   * {@inheritDoc}
290   */
291  @Override()
292  public String getExtensionName()
293  {
294    return INFO_AUTHORITY_KEY_ID_EXTENSION_NAME.get();
295  }
296
297
298
299  /**
300   * {@inheritDoc}
301   */
302  @Override()
303  public void toString(final StringBuilder buffer)
304  {
305    buffer.append("AuthorityKeyIdentifierExtension(oid='");
306    buffer.append(getOID());
307    buffer.append("', isCritical=");
308    buffer.append(isCritical());
309
310    if (keyIdentifier != null)
311    {
312      buffer.append(", keyIdentifierBytes='");
313      StaticUtils.toHex(keyIdentifier.getValue(), ":", buffer);
314      buffer.append('\'');
315    }
316
317    if (authorityCertIssuer != null)
318    {
319      buffer.append(", authorityCertIssuer=");
320      authorityCertIssuer.toString(buffer);
321    }
322
323    if (authorityCertSerialNumber != null)
324    {
325      buffer.append(", authorityCertSerialNumber='");
326      StaticUtils.toHex(authorityCertSerialNumber.toByteArray(), ":", buffer);
327      buffer.append('\'');
328    }
329
330
331    buffer.append(')');
332  }
333}