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.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            generalNames = new GeneralNames(element);
177            break;
178          case TYPE_AUTHORITY_CERT_SERIAL_NUMBER:
179            serialNumber = element.decodeAsBigInteger().getBigIntegerValue();
180            break;
181        }
182      }
183
184      keyIdentifier = keyID;
185      authorityCertIssuer = generalNames;
186      authorityCertSerialNumber = serialNumber;
187    }
188    catch (final Exception e)
189    {
190      Debug.debugException(e);
191      throw new CertException(
192           ERR_AUTHORITY_KEY_ID_EXTENSION_CANNOT_PARSE.get(
193                String.valueOf(extension), StaticUtils.getExceptionMessage(e)),
194           e);
195    }
196  }
197
198
199
200  /**
201   * Encodes the provided information for use as the value of this extension.
202   *
203   * @param  keyIdentifier              The key identifier.  This may be
204   *                                    {@code null} if it should not be
205   *                                    included in the extension.
206   * @param  authorityCertIssuer        The authority certificate issuer.  This
207   *                                    may be {@code null} if it should not be
208   *                                    included in the extension.
209   * @param  authorityCertSerialNumber  The authority certificate serial number.
210   *                                    This may be {@code null} if it should
211   *                                    not be included in the extension.
212   *
213   * @return  The encoded value.
214   *
215   * @throws  CertException  If a problem is encountered while encoding the
216   *                         value.
217   */
218  private static byte[] encodeValue(final ASN1OctetString keyIdentifier,
219                                    final GeneralNames authorityCertIssuer,
220                                    final BigInteger authorityCertSerialNumber)
221          throws CertException
222  {
223    final ArrayList<ASN1Element> elements = new ArrayList<>(3);
224    if (keyIdentifier != null)
225    {
226      elements.add(new ASN1OctetString(TYPE_KEY_IDENTIFIER,
227           keyIdentifier.getValue()));
228    }
229
230    if (authorityCertIssuer != null)
231    {
232      elements.add(new ASN1Element(TYPE_AUTHORITY_CERT_ISSUER,
233           authorityCertIssuer.encode().getValue()));
234    }
235
236    if (authorityCertSerialNumber != null)
237    {
238      elements.add(new ASN1BigInteger(TYPE_AUTHORITY_CERT_SERIAL_NUMBER,
239           authorityCertSerialNumber));
240    }
241
242    return new ASN1Sequence(elements).encode();
243  }
244
245
246
247  /**
248   * Retrieves the key identifier for this extension, if available.
249   *
250   * @return  The key identifier for this extension, or {@code null} if it
251   *          was not included in the extension.
252   */
253  public ASN1OctetString getKeyIdentifier()
254  {
255    return keyIdentifier;
256  }
257
258
259
260  /**
261   * Retrieves the general names for the authority certificate, if available.
262   *
263   * @return  The general names for the authority certificate, or {@code null}
264   *          if it was not included in the extension.
265   */
266  public GeneralNames getAuthorityCertIssuer()
267  {
268    return authorityCertIssuer;
269  }
270
271
272
273  /**
274   * Retrieves the serial number for the authority certificate, if available.
275   *
276   * @return  The serial number for the authority certificate, or {@code null}
277   *          if it was not included in the extension.
278   */
279  public BigInteger getAuthorityCertSerialNumber()
280  {
281    return authorityCertSerialNumber;
282  }
283
284
285
286  /**
287   * {@inheritDoc}
288   */
289  @Override()
290  public String getExtensionName()
291  {
292    return INFO_AUTHORITY_KEY_ID_EXTENSION_NAME.get();
293  }
294
295
296
297  /**
298   * {@inheritDoc}
299   */
300  @Override()
301  public void toString(final StringBuilder buffer)
302  {
303    buffer.append("AuthorityKeyIdentifierExtension(oid='");
304    buffer.append(getOID());
305    buffer.append("', isCritical=");
306    buffer.append(isCritical());
307
308    if (keyIdentifier != null)
309    {
310      buffer.append(", keyIdentifierBytes='");
311      StaticUtils.toHex(keyIdentifier.getValue(), ":", buffer);
312      buffer.append('\'');
313    }
314
315    if (authorityCertIssuer != null)
316    {
317      buffer.append(", authorityCertIssuer=");
318      authorityCertIssuer.toString(buffer);
319    }
320
321    if (authorityCertSerialNumber != null)
322    {
323      buffer.append(", authorityCertSerialNumber='");
324      StaticUtils.toHex(authorityCertSerialNumber.toByteArray(), ":", buffer);
325      buffer.append('\'');
326    }
327
328
329    buffer.append(')');
330  }
331}