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