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