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.io.Serializable; 026import java.util.ArrayList; 027 028import com.unboundid.asn1.ASN1Boolean; 029import com.unboundid.asn1.ASN1Constants; 030import com.unboundid.asn1.ASN1Element; 031import com.unboundid.asn1.ASN1ObjectIdentifier; 032import com.unboundid.asn1.ASN1OctetString; 033import com.unboundid.asn1.ASN1Sequence; 034import com.unboundid.util.Debug; 035import com.unboundid.util.NotExtensible; 036import com.unboundid.util.NotMutable; 037import com.unboundid.util.OID; 038import com.unboundid.util.StaticUtils; 039import com.unboundid.util.ThreadSafety; 040import com.unboundid.util.ThreadSafetyLevel; 041 042import static com.unboundid.util.ssl.cert.CertMessages.*; 043 044 045 046/** 047 * This class represents a data structure that holds information about an X.509 048 * certificate extension. 049 */ 050@NotExtensible() 051@NotMutable() 052@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 053public class X509CertificateExtension 054 implements Serializable 055{ 056 /** 057 * The serial version UID for this serializable class. 058 */ 059 private static final long serialVersionUID = -4044598072050168580L; 060 061 062 063 // Indicates whether this extension is considered critical. 064 private final boolean isCritical; 065 066 // The value for this extension. 067 private final byte[] value; 068 069 // The OID for this extension. 070 private final OID oid; 071 072 073 074 /** 075 * Creates a new X.509 certificate extension that wraps the provided 076 * extension. 077 * 078 * @param extension The extension to wrap. 079 */ 080 protected X509CertificateExtension(final X509CertificateExtension extension) 081 { 082 oid = extension.oid; 083 isCritical = extension.isCritical; 084 value = extension.value; 085 } 086 087 088 089 /** 090 * Creates a new X.509 certificate extension with the provided information. 091 * 092 * @param oid The OID for this extension. 093 * @param isCritical Indicates whether this extension is considered 094 * critical. 095 * @param value The value for this extension. 096 */ 097 public X509CertificateExtension(final OID oid, final boolean isCritical, 098 final byte[] value) 099 { 100 this.oid = oid; 101 this.isCritical = isCritical; 102 this.value = value; 103 } 104 105 106 107 /** 108 * Decodes the provided ASN.1 element as an X.509 certificate extension. 109 * 110 * @param extensionElement The ASN.1 element containing the encoded 111 * extension. 112 * 113 * @return The decoded extension. 114 * 115 * @throws CertException If a problem is encountered while attempting to 116 * decode the extension. 117 */ 118 static X509CertificateExtension decode(final ASN1Element extensionElement) 119 throws CertException 120 { 121 final OID oid; 122 final X509CertificateExtension extension; 123 try 124 { 125 final ASN1Element[] elements = 126 extensionElement.decodeAsSequence().elements(); 127 oid = elements[0].decodeAsObjectIdentifier().getOID(); 128 129 final boolean isCritical; 130 final byte[] value; 131 if (elements[1].getType() == ASN1Constants.UNIVERSAL_BOOLEAN_TYPE) 132 { 133 isCritical = elements[1].decodeAsBoolean().booleanValue(); 134 value = elements[2].decodeAsOctetString().getValue(); 135 } 136 else 137 { 138 isCritical = false; 139 value = elements[1].decodeAsOctetString().getValue(); 140 } 141 142 extension = new X509CertificateExtension(oid, isCritical, value); 143 } 144 catch (final Exception e) 145 { 146 Debug.debugException(e); 147 throw new CertException( 148 ERR_EXTENSION_DECODE_ERROR.get( 149 StaticUtils.getExceptionMessage(e)), 150 e); 151 } 152 153 if (oid.equals(AuthorityKeyIdentifierExtension. 154 AUTHORITY_KEY_IDENTIFIER_OID)) 155 { 156 try 157 { 158 return new AuthorityKeyIdentifierExtension(extension); 159 } 160 catch (final Exception e) 161 { 162 Debug.debugException(e); 163 } 164 } 165 else if (oid.equals(SubjectKeyIdentifierExtension. 166 SUBJECT_KEY_IDENTIFIER_OID)) 167 { 168 try 169 { 170 return new SubjectKeyIdentifierExtension(extension); 171 } 172 catch (final Exception e) 173 { 174 Debug.debugException(e); 175 } 176 } 177 else if (oid.equals(KeyUsageExtension.KEY_USAGE_OID)) 178 { 179 try 180 { 181 return new KeyUsageExtension(extension); 182 } 183 catch (final Exception e) 184 { 185 Debug.debugException(e); 186 } 187 } 188 else if (oid.equals(SubjectAlternativeNameExtension. 189 SUBJECT_ALTERNATIVE_NAME_OID)) 190 { 191 try 192 { 193 return new SubjectAlternativeNameExtension(extension); 194 } 195 catch (final Exception e) 196 { 197 Debug.debugException(e); 198 } 199 } 200 else if (oid.equals(IssuerAlternativeNameExtension. 201 ISSUER_ALTERNATIVE_NAME_OID)) 202 { 203 try 204 { 205 return new IssuerAlternativeNameExtension(extension); 206 } 207 catch (final Exception e) 208 { 209 Debug.debugException(e); 210 } 211 } 212 else if (oid.equals(BasicConstraintsExtension. 213 BASIC_CONSTRAINTS_OID)) 214 { 215 try 216 { 217 return new BasicConstraintsExtension(extension); 218 } 219 catch (final Exception e) 220 { 221 Debug.debugException(e); 222 } 223 } 224 else if (oid.equals(ExtendedKeyUsageExtension. 225 EXTENDED_KEY_USAGE_OID)) 226 { 227 try 228 { 229 return new ExtendedKeyUsageExtension(extension); 230 } 231 catch (final Exception e) 232 { 233 Debug.debugException(e); 234 } 235 } 236 else if (oid.equals(CRLDistributionPointsExtension. 237 CRL_DISTRIBUTION_POINTS_OID)) 238 { 239 try 240 { 241 return new CRLDistributionPointsExtension(extension); 242 } 243 catch (final Exception e) 244 { 245 Debug.debugException(e); 246 } 247 } 248 249 return extension; 250 } 251 252 253 254 /** 255 * Retrieves the OID for this extension. 256 * 257 * @return The OID for this extension. 258 */ 259 public final OID getOID() 260 { 261 return oid; 262 } 263 264 265 266 /** 267 * Indicates whether this extension is considered critical. 268 * 269 * @return {@code true} if this extension is considered critical, or 270 * {@code false} if not. 271 */ 272 public final boolean isCritical() 273 { 274 return isCritical; 275 } 276 277 278 279 /** 280 * Retrieves the value for this extension. 281 * 282 * @return The value for this extension. 283 */ 284 public final byte[] getValue() 285 { 286 return value; 287 } 288 289 290 291 /** 292 * Encodes this extension to an ASN.1 element suitable for inclusion in an 293 * encoded X.509 certificate. 294 * 295 * @return The encoded representation of this extension. 296 * 297 * @throws CertException If a problem is encountered while encoding the 298 * extension. 299 */ 300 ASN1Sequence encode() 301 throws CertException 302 { 303 try 304 { 305 final ArrayList<ASN1Element> elements = new ArrayList<>(3); 306 elements.add(new ASN1ObjectIdentifier(oid)); 307 308 if (isCritical) 309 { 310 elements.add(ASN1Boolean.UNIVERSAL_BOOLEAN_TRUE_ELEMENT); 311 } 312 313 elements.add(new ASN1OctetString(value)); 314 return new ASN1Sequence(elements); 315 } 316 catch (final Exception e) 317 { 318 Debug.debugException(e); 319 throw new CertException( 320 ERR_EXTENSION_ENCODE_ERROR.get(toString(), 321 StaticUtils.getExceptionMessage(e)), 322 e); 323 } 324 } 325 326 327 328 /** 329 * Retrieves the name for this extension. 330 * 331 * @return The name for this extension. 332 */ 333 public String getExtensionName() 334 { 335 return oid.toString(); 336 } 337 338 339 340 /** 341 * Retrieves a string representation of this extension. 342 * 343 * @return A string representation of this extension. 344 */ 345 public final String toString() 346 { 347 final StringBuilder buffer = new StringBuilder(); 348 toString(buffer); 349 return buffer.toString(); 350 } 351 352 353 354 /** 355 * Appends a string representation of this certificate extension to the 356 * provided buffer. 357 * 358 * @param buffer The buffer to which the information should be appended. 359 */ 360 public void toString(final StringBuilder buffer) 361 { 362 buffer.append("X509CertificateExtension(oid='"); 363 buffer.append(oid.toString()); 364 buffer.append("', isCritical="); 365 buffer.append(isCritical); 366 367 if (StaticUtils.isPrintableString(value)) 368 { 369 buffer.append(", value='"); 370 buffer.append(StaticUtils.toUTF8String(value)); 371 buffer.append('\''); 372 } 373 else 374 { 375 buffer.append(", valueLength="); 376 buffer.append(value.length); 377 } 378 379 buffer.append(')'); 380 } 381}