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.net.InetAddress; 041import java.util.Iterator; 042import java.util.List; 043 044import com.unboundid.ldap.sdk.DN; 045import com.unboundid.asn1.ASN1Element; 046import com.unboundid.util.Debug; 047import com.unboundid.util.NotExtensible; 048import com.unboundid.util.NotNull; 049import com.unboundid.util.ObjectPair; 050import com.unboundid.util.OID; 051import com.unboundid.util.StaticUtils; 052import com.unboundid.util.ThreadSafety; 053import com.unboundid.util.ThreadSafetyLevel; 054 055import static com.unboundid.util.ssl.cert.CertMessages.*; 056 057 058 059/** 060 * This class provides support for decoding the values of the 061 * {@link SubjectAlternativeNameExtension} and 062 * {@link IssuerAlternativeNameExtension} extensions as described in 063 * <A HREF="https://www.ietf.org/rfc/rfc5280.txt">RFC 5280</A> sections 4.2.1.6 064 * and 4.2.1.7. 065 * <BR><BR> 066 * Note that this implementation only provides complete decoding for the RFC 822 067 * names (email addresses), DNS names, directory names, uniform resource 068 * identifiers, and IP addresses elements. The other elements will be left in 069 * their raw forms. 070 * <BR><BR> 071 * The value has the following encoding: 072 * <PRE> 073 * SubjectAltName ::= GeneralNames 074 * 075 * IssuerAltName ::= GeneralNames 076 * 077 * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName 078 * 079 * GeneralName ::= CHOICE { 080 * otherName [0] OtherName, 081 * rfc822Name [1] IA5String, 082 * dNSName [2] IA5String, 083 * x400Address [3] ORAddress, 084 * directoryName [4] Name, 085 * ediPartyName [5] EDIPartyName, 086 * uniformResourceIdentifier [6] IA5String, 087 * iPAddress [7] OCTET STRING, 088 * registeredID [8] OBJECT IDENTIFIER } 089 * 090 * OtherName ::= SEQUENCE { 091 * type-id OBJECT IDENTIFIER, 092 * value [0] EXPLICIT ANY DEFINED BY type-id } 093 * 094 * EDIPartyName ::= SEQUENCE { 095 * nameAssigner [0] DirectoryString OPTIONAL, 096 * partyName [1] DirectoryString } 097 * </PRE> 098 */ 099@NotExtensible() 100@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 101public abstract class GeneralAlternativeNameExtension 102 extends X509CertificateExtension 103{ 104 /** 105 * The serial version UID for this serializable class. 106 */ 107 private static final long serialVersionUID = -1076071031835517176L; 108 109 110 111 // The general names for inclusion in this extension. 112 @NotNull private final GeneralNames generalNames; 113 114 115 116 /** 117 * Creates a new general alternative name extension with the provided 118 * information. 119 * 120 * @param oid The OID for this extension. 121 * @param isCritical Indicates whether this extension should be 122 * considered critical. 123 * @param generalNames The general names for inclusion in this extension. 124 * 125 * @throws CertException If a problem is encountered while encoding the 126 * value for this extension. 127 */ 128 protected GeneralAlternativeNameExtension(@NotNull final OID oid, 129 final boolean isCritical, 130 @NotNull final GeneralNames generalNames) 131 throws CertException 132 { 133 super(oid, isCritical, generalNames.encode().encode()); 134 135 this.generalNames = generalNames; 136 } 137 138 139 140 /** 141 * Creates a new general alternative name extension from the provided generic 142 * extension. 143 * 144 * @param extension The extension to decode as a general alternative name 145 * extension. 146 * 147 * @throws CertException If the provided extension cannot be decoded as a 148 * general alternative name extension. 149 */ 150 protected GeneralAlternativeNameExtension( 151 @NotNull final X509CertificateExtension extension) 152 throws CertException 153 { 154 super(extension); 155 156 try 157 { 158 generalNames = new GeneralNames(ASN1Element.decode(extension.getValue())); 159 } 160 catch (final Exception e) 161 { 162 Debug.debugException(e); 163 164 final String name; 165 if (extension.getOID().equals(SubjectAlternativeNameExtension. 166 SUBJECT_ALTERNATIVE_NAME_OID)) 167 { 168 name = INFO_SUBJECT_ALT_NAME_EXTENSION_NAME.get(); 169 } 170 else if (extension.getOID().equals(IssuerAlternativeNameExtension. 171 ISSUER_ALTERNATIVE_NAME_OID)) 172 { 173 name = INFO_ISSUER_ALT_NAME_EXTENSION_NAME.get(); 174 } 175 else 176 { 177 name = extension.getOID().toString(); 178 } 179 180 throw new CertException( 181 ERR_GENERAL_ALT_NAME_EXTENSION_CANNOT_PARSE.get( 182 String.valueOf(extension), name, 183 StaticUtils.getExceptionMessage(e)), 184 e); 185 } 186 } 187 188 189 190 /** 191 * Retrieves the {@code GeneralNames} object for this alternative name 192 * extension. 193 * 194 * @return The {@code GeneralNames} object for this alternative name 195 * extension. 196 */ 197 @NotNull() 198 public final GeneralNames getGeneralNames() 199 { 200 return generalNames; 201 } 202 203 204 205 /** 206 * Retrieves the otherName elements from the extension. 207 * 208 * @return The otherName elements from the extension. 209 */ 210 @NotNull() 211 public final List<ObjectPair<OID,ASN1Element>> getOtherNames() 212 { 213 return generalNames.getOtherNames(); 214 } 215 216 217 218 /** 219 * Retrieves the RFC 822 names (email addresses) from the extension. 220 * 221 * @return The RFC 822 names from the extension. 222 */ 223 @NotNull() 224 public final List<String> getRFC822Names() 225 { 226 return generalNames.getRFC822Names(); 227 } 228 229 230 231 /** 232 * Retrieves the DNS names from the extension. 233 * 234 * @return The DNS names from the extension. 235 */ 236 @NotNull() 237 public final List<String> getDNSNames() 238 { 239 return generalNames.getDNSNames(); 240 } 241 242 243 244 /** 245 * Retrieves the x400Address elements from the extension. 246 * 247 * @return The x400Address elements from the extension. 248 */ 249 @NotNull() 250 public final List<ASN1Element> getX400Addresses() 251 { 252 return generalNames.getX400Addresses(); 253 } 254 255 256 257 /** 258 * Retrieves the directory names from the extension. 259 * 260 * @return The directory names from the extension. 261 */ 262 @NotNull() 263 public final List<DN> getDirectoryNames() 264 { 265 return generalNames.getDirectoryNames(); 266 } 267 268 269 270 /** 271 * Retrieves the ediPartyName elements from the extensions. 272 * 273 * @return The ediPartyName elements from the extension. 274 */ 275 @NotNull() 276 public final List<ASN1Element> getEDIPartyNames() 277 { 278 return generalNames.getEDIPartyNames(); 279 } 280 281 282 283 /** 284 * Retrieves the uniform resource identifiers (URIs) from the extension. 285 * 286 * @return The URIs from the extension. 287 */ 288 @NotNull() 289 public final List<String> getUniformResourceIdentifiers() 290 { 291 return generalNames.getUniformResourceIdentifiers(); 292 } 293 294 295 296 /** 297 * Retrieves the IP addresses from the extension. 298 * 299 * @return The IP addresses from the extension. 300 */ 301 @NotNull() 302 public final List<InetAddress> getIPAddresses() 303 { 304 return generalNames.getIPAddresses(); 305 } 306 307 308 309 /** 310 * Retrieves the registeredID elements from the extension. 311 * 312 * @return The registeredID elements from the extension. 313 */ 314 @NotNull() 315 public final List<OID> getRegisteredIDs() 316 { 317 return generalNames.getRegisteredIDs(); 318 } 319 320 321 322 /** 323 * Appends a string representation of this extension to the provided buffer. 324 * 325 * @param extensionName The name to use for this extension. 326 * @param buffer The buffer to which the information should be 327 * appended. 328 */ 329 protected void toString(@NotNull final String extensionName, 330 @NotNull final StringBuilder buffer) 331 { 332 buffer.append(extensionName); 333 buffer.append("(oid='"); 334 buffer.append(getOID()); 335 buffer.append("', isCritical="); 336 buffer.append(isCritical()); 337 338 if (! getDNSNames().isEmpty()) 339 { 340 buffer.append(", dnsNames={"); 341 342 final Iterator<String> iterator = getDNSNames().iterator(); 343 while (iterator.hasNext()) 344 { 345 buffer.append('\''); 346 buffer.append(iterator.next()); 347 buffer.append('\''); 348 349 if (iterator.hasNext()) 350 { 351 buffer.append(','); 352 } 353 } 354 355 buffer.append('}'); 356 } 357 358 if (! getIPAddresses().isEmpty()) 359 { 360 buffer.append(", ipAddresses={"); 361 362 final Iterator<InetAddress> iterator = getIPAddresses().iterator(); 363 while (iterator.hasNext()) 364 { 365 buffer.append('\''); 366 buffer.append(iterator.next().getHostAddress()); 367 buffer.append('\''); 368 369 if (iterator.hasNext()) 370 { 371 buffer.append(','); 372 } 373 } 374 375 buffer.append('}'); 376 } 377 378 if (! getRFC822Names().isEmpty()) 379 { 380 buffer.append(", rfc822Names={"); 381 382 final Iterator<String> iterator = getRFC822Names().iterator(); 383 while (iterator.hasNext()) 384 { 385 buffer.append('\''); 386 buffer.append(iterator.next()); 387 buffer.append('\''); 388 389 if (iterator.hasNext()) 390 { 391 buffer.append(','); 392 } 393 } 394 395 buffer.append('}'); 396 } 397 398 if (! getDirectoryNames().isEmpty()) 399 { 400 buffer.append(", directoryNames={"); 401 402 final Iterator<DN> iterator = getDirectoryNames().iterator(); 403 while (iterator.hasNext()) 404 { 405 buffer.append('\''); 406 buffer.append(iterator.next()); 407 buffer.append('\''); 408 409 if (iterator.hasNext()) 410 { 411 buffer.append(','); 412 } 413 } 414 415 buffer.append('}'); 416 } 417 418 if (! getUniformResourceIdentifiers().isEmpty()) 419 { 420 buffer.append(", uniformResourceIdentifiers={"); 421 422 final Iterator<String> iterator = 423 getUniformResourceIdentifiers().iterator(); 424 while (iterator.hasNext()) 425 { 426 buffer.append('\''); 427 buffer.append(iterator.next()); 428 buffer.append('\''); 429 430 if (iterator.hasNext()) 431 { 432 buffer.append(','); 433 } 434 } 435 436 buffer.append('}'); 437 } 438 439 if (! getRegisteredIDs().isEmpty()) 440 { 441 buffer.append(", registeredIDs={"); 442 443 final Iterator<OID> iterator = getRegisteredIDs().iterator(); 444 while (iterator.hasNext()) 445 { 446 buffer.append('\''); 447 buffer.append(iterator.next()); 448 buffer.append('\''); 449 450 if (iterator.hasNext()) 451 { 452 buffer.append(','); 453 } 454 } 455 456 buffer.append('}'); 457 } 458 459 if (! getOtherNames().isEmpty()) 460 { 461 buffer.append(", otherNameCount="); 462 buffer.append(getOtherNames().size()); 463 } 464 465 if (! getX400Addresses().isEmpty()) 466 { 467 buffer.append(", x400AddressCount="); 468 buffer.append(getX400Addresses().size()); 469 } 470 471 if (! getEDIPartyNames().isEmpty()) 472 { 473 buffer.append(", ediPartyNameCount="); 474 buffer.append(getEDIPartyNames().size()); 475 } 476 477 buffer.append(')'); 478 } 479}