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