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}