1 /* 2 * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.x509; 27 28 import java.io.IOException; 29 import java.io.OutputStream; 30 31 import java.util.*; 32 33 import sun.security.util.DerInputStream; 34 import sun.security.util.DerOutputStream; 35 import sun.security.util.DerValue; 36 37 /** 38 * Represents the CRL Issuing Distribution Point Extension (OID = 2.5.29.28). 39 * 40 * <p> 41 * The issuing distribution point is a critical CRL extension that 42 * identifies the CRL distribution point and scope for a particular CRL, 43 * and it indicates whether the CRL covers revocation for end entity 44 * certificates only, CA certificates only, attribute certificates only, 45 * or a limited set of reason codes. 46 * 47 * <p> 48 * The extension is defined in Section 5.2.5 of 49 * <a href="http://www.ietf.org/rfc/rfc3280.txt">Internet X.509 PKI Certific 50 ate and Certificate Revocation List (CRL) Profile</a>. 51 * 52 * <p> 53 * Its ASN.1 definition is as follows: 54 * <pre> 55 * id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } 56 * 57 * issuingDistributionPoint ::= SEQUENCE { 58 * distributionPoint [0] DistributionPointName OPTIONAL, 59 * onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, 60 * onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, 61 * onlySomeReasons [3] ReasonFlags OPTIONAL, 62 * indirectCRL [4] BOOLEAN DEFAULT FALSE, 63 * onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE } 64 * </pre> 65 * 66 * @see DistributionPoint 67 * @since 1.6 68 */ 69 public class IssuingDistributionPointExtension extends Extension 70 implements CertAttrSet<String> { 71 72 /** 73 * Identifier for this attribute, to be used with the 74 * get, set, delete methods of Certificate, x509 type. 75 */ 76 public static final String IDENT = 77 "x509.info.extensions.IssuingDistributionPoint"; 78 79 /** 80 * Attribute names. 81 */ 82 public static final String NAME = "IssuingDistributionPoint"; 83 public static final String POINT = "point"; 84 public static final String REASONS = "reasons"; 85 public static final String ONLY_USER_CERTS = "only_user_certs"; 86 public static final String ONLY_CA_CERTS = "only_ca_certs"; 87 public static final String ONLY_ATTRIBUTE_CERTS = "only_attribute_certs"; 88 public static final String INDIRECT_CRL = "indirect_crl"; 89 90 /* 91 * The distribution point name for the CRL. 92 */ 93 private DistributionPointName distributionPoint = null; 94 95 /* 96 * The scope settings for the CRL. 97 */ 98 private ReasonFlags revocationReasons = null; 99 private boolean hasOnlyUserCerts = false; 100 private boolean hasOnlyCACerts = false; 101 private boolean hasOnlyAttributeCerts = false; 102 private boolean isIndirectCRL = false; 103 104 /* 105 * ASN.1 context specific tag values 106 */ 107 private static final byte TAG_DISTRIBUTION_POINT = 0; 108 private static final byte TAG_ONLY_USER_CERTS = 1; 109 private static final byte TAG_ONLY_CA_CERTS = 2; 110 private static final byte TAG_ONLY_SOME_REASONS = 3; 111 private static final byte TAG_INDIRECT_CRL = 4; 112 private static final byte TAG_ONLY_ATTRIBUTE_CERTS = 5; 113 114 /** 115 * Creates a critical IssuingDistributionPointExtension. 116 * 117 * @param distributionPoint the name of the distribution point, or null for 118 * none. 119 * @param revocationReasons the revocation reasons associated with the 120 * distribution point, or null for none. 121 * @param hasOnlyUserCerts if <code>true</code> then scope of the CRL 122 * includes only user certificates. 123 * @param hasOnlyCACerts if <code>true</code> then scope of the CRL 124 * includes only CA certificates. 125 * @param hasOnlyAttributeCerts if <code>true</code> then scope of the CRL 126 * includes only attribute certificates. 127 * @param isIndirectCRL if <code>true</code> then the scope of the CRL 128 * includes certificates issued by authorities other than the CRL 129 * issuer. The responsible authority is indicated by a certificate 130 * issuer CRL entry extension. 131 * @throws IllegalArgumentException if more than one of 132 * <code>hasOnlyUserCerts</code>, <code>hasOnlyCACerts</code>, 133 * <code>hasOnlyAttributeCerts</code> is set to <code>true</code>. 134 * @throws IOException on encoding error. 135 */ 136 public IssuingDistributionPointExtension( 137 DistributionPointName distributionPoint, ReasonFlags revocationReasons, 138 boolean hasOnlyUserCerts, boolean hasOnlyCACerts, 139 boolean hasOnlyAttributeCerts, boolean isIndirectCRL) 140 throws IOException { 141 142 if ((hasOnlyUserCerts && (hasOnlyCACerts || hasOnlyAttributeCerts)) || 143 (hasOnlyCACerts && (hasOnlyUserCerts || hasOnlyAttributeCerts)) || 144 (hasOnlyAttributeCerts && (hasOnlyUserCerts || hasOnlyCACerts))) { 145 throw new IllegalArgumentException( 146 "Only one of hasOnlyUserCerts, hasOnlyCACerts, " + 147 "hasOnlyAttributeCerts may be set to true"); 148 } 149 this.extensionId = PKIXExtensions.IssuingDistributionPoint_Id; 150 this.critical = true; 151 this.distributionPoint = distributionPoint; 152 this.revocationReasons = revocationReasons; 153 this.hasOnlyUserCerts = hasOnlyUserCerts; 154 this.hasOnlyCACerts = hasOnlyCACerts; 155 this.hasOnlyAttributeCerts = hasOnlyAttributeCerts; 156 this.isIndirectCRL = isIndirectCRL; 157 encodeThis(); 158 } 159 160 /** 161 * Creates a critical IssuingDistributionPointExtension from its 162 * DER-encoding. 163 * 164 * @param critical true if the extension is to be treated as critical. 165 * @param value the DER-encoded value. It must be a <code>byte[]</code>. 166 * @exception IOException on decoding error. 167 */ 168 public IssuingDistributionPointExtension(Boolean critical, Object value) 169 throws IOException { 170 this.extensionId = PKIXExtensions.IssuingDistributionPoint_Id; 171 this.critical = critical.booleanValue(); 172 173 if (!(value instanceof byte[])) { 174 throw new IOException("Illegal argument type"); 175 } 176 177 extensionValue = (byte[])value; 178 DerValue val = new DerValue(extensionValue); 179 if (val.tag != DerValue.tag_Sequence) { 180 throw new IOException("Invalid encoding for " + 181 "IssuingDistributionPointExtension."); 182 } 183 184 // All the elements in issuingDistributionPoint are optional 185 if ((val.data == null) || (val.data.available() == 0)) { 186 return; 187 } 188 189 DerInputStream in = val.data; 190 while (in != null && in.available() != 0) { 191 DerValue opt = in.getDerValue(); 192 193 if (opt.isContextSpecific(TAG_DISTRIBUTION_POINT) && 194 opt.isConstructed()) { 195 distributionPoint = 196 new DistributionPointName(opt.data.getDerValue()); 197 } else if (opt.isContextSpecific(TAG_ONLY_USER_CERTS) && 198 !opt.isConstructed()) { 199 opt.resetTag(DerValue.tag_Boolean); 200 hasOnlyUserCerts = opt.getBoolean(); 201 } else if (opt.isContextSpecific(TAG_ONLY_CA_CERTS) && 202 !opt.isConstructed()) { 203 opt.resetTag(DerValue.tag_Boolean); 204 hasOnlyCACerts = opt.getBoolean(); 205 } else if (opt.isContextSpecific(TAG_ONLY_SOME_REASONS) && 206 !opt.isConstructed()) { 207 revocationReasons = new ReasonFlags(opt); // expects tag implicit 208 } else if (opt.isContextSpecific(TAG_INDIRECT_CRL) && 209 !opt.isConstructed()) { 210 opt.resetTag(DerValue.tag_Boolean); 211 isIndirectCRL = opt.getBoolean(); 212 } else if (opt.isContextSpecific(TAG_ONLY_ATTRIBUTE_CERTS) && 213 !opt.isConstructed()) { 214 opt.resetTag(DerValue.tag_Boolean); 215 hasOnlyAttributeCerts = opt.getBoolean(); 216 } else { 217 throw new IOException 218 ("Invalid encoding of IssuingDistributionPoint"); 219 } 220 } 221 } 222 223 /** 224 * Returns the name of this attribute. 225 */ 226 public String getName() { 227 return NAME; 228 } 229 230 /** 231 * Encodes the issuing distribution point extension and writes it to the 232 * DerOutputStream. 233 * 234 * @param out the output stream. 235 * @exception IOException on encoding error. 236 */ 237 public void encode(OutputStream out) throws IOException { 238 DerOutputStream tmp = new DerOutputStream(); 239 if (this.extensionValue == null) { 240 this.extensionId = PKIXExtensions.IssuingDistributionPoint_Id; 241 this.critical = false; 242 encodeThis(); 243 } 244 super.encode(tmp); 245 out.write(tmp.toByteArray()); 246 } 247 248 /** 249 * Sets the attribute value. 250 */ 251 public void set(String name, Object obj) throws IOException { 252 if (name.equalsIgnoreCase(POINT)) { 253 if (!(obj instanceof DistributionPointName)) { 254 throw new IOException( 255 "Attribute value should be of type DistributionPointName."); 256 } 257 distributionPoint = (DistributionPointName)obj; 258 259 } else if (name.equalsIgnoreCase(REASONS)) { 260 if (!(obj instanceof ReasonFlags)) { 261 throw new IOException( 262 "Attribute value should be of type ReasonFlags."); 263 } 264 revocationReasons = (ReasonFlags)obj; 265 266 } else if (name.equalsIgnoreCase(INDIRECT_CRL)) { 267 if (!(obj instanceof Boolean)) { 268 throw new IOException( 269 "Attribute value should be of type Boolean."); 270 } 271 isIndirectCRL = ((Boolean)obj).booleanValue(); 272 273 } else if (name.equalsIgnoreCase(ONLY_USER_CERTS)) { 274 if (!(obj instanceof Boolean)) { 275 throw new IOException( 276 "Attribute value should be of type Boolean."); 277 } 278 hasOnlyUserCerts = ((Boolean)obj).booleanValue(); 279 280 } else if (name.equalsIgnoreCase(ONLY_CA_CERTS)) { 281 if (!(obj instanceof Boolean)) { 282 throw new IOException( 283 "Attribute value should be of type Boolean."); 284 } 285 hasOnlyCACerts = ((Boolean)obj).booleanValue(); 286 287 } else if (name.equalsIgnoreCase(ONLY_ATTRIBUTE_CERTS)) { 288 if (!(obj instanceof Boolean)) { 289 throw new IOException( 290 "Attribute value should be of type Boolean."); 291 } 292 hasOnlyAttributeCerts = ((Boolean)obj).booleanValue(); 293 294 } else { 295 throw new IOException("Attribute name [" + name + 296 "] not recognized by " + 297 "CertAttrSet:IssuingDistributionPointExtension."); 298 } 299 encodeThis(); 300 } 301 302 /** 303 * Gets the attribute value. 304 */ 305 public Object get(String name) throws IOException { 306 if (name.equalsIgnoreCase(POINT)) { 307 return distributionPoint; 308 309 } else if (name.equalsIgnoreCase(INDIRECT_CRL)) { 310 return Boolean.valueOf(isIndirectCRL); 311 312 } else if (name.equalsIgnoreCase(REASONS)) { 313 return revocationReasons; 314 315 } else if (name.equalsIgnoreCase(ONLY_USER_CERTS)) { 316 return Boolean.valueOf(hasOnlyUserCerts); 317 318 } else if (name.equalsIgnoreCase(ONLY_CA_CERTS)) { 319 return Boolean.valueOf(hasOnlyCACerts); 320 321 } else if (name.equalsIgnoreCase(ONLY_ATTRIBUTE_CERTS)) { 322 return Boolean.valueOf(hasOnlyAttributeCerts); 323 324 } else { 325 throw new IOException("Attribute name [" + name + 326 "] not recognized by " + 327 "CertAttrSet:IssuingDistributionPointExtension."); 328 } 329 } 330 331 /** 332 * Deletes the attribute value. 333 */ 334 public void delete(String name) throws IOException { 335 if (name.equalsIgnoreCase(POINT)) { 336 distributionPoint = null; 337 338 } else if (name.equalsIgnoreCase(INDIRECT_CRL)) { 339 isIndirectCRL = false; 340 341 } else if (name.equalsIgnoreCase(REASONS)) { 342 revocationReasons = null; 343 344 } else if (name.equalsIgnoreCase(ONLY_USER_CERTS)) { 345 hasOnlyUserCerts = false; 346 347 } else if (name.equalsIgnoreCase(ONLY_CA_CERTS)) { 348 hasOnlyCACerts = false; 349 350 } else if (name.equalsIgnoreCase(ONLY_ATTRIBUTE_CERTS)) { 351 hasOnlyAttributeCerts = false; 352 353 } else { 354 throw new IOException("Attribute name [" + name + 355 "] not recognized by " + 356 "CertAttrSet:IssuingDistributionPointExtension."); 357 } 358 encodeThis(); 359 } 360 361 /** 362 * Returns an enumeration of names of attributes existing within this 363 * attribute. 364 */ 365 public Enumeration<String> getElements() { 366 AttributeNameEnumeration elements = new AttributeNameEnumeration(); 367 elements.addElement(POINT); 368 elements.addElement(REASONS); 369 elements.addElement(ONLY_USER_CERTS); 370 elements.addElement(ONLY_CA_CERTS); 371 elements.addElement(ONLY_ATTRIBUTE_CERTS); 372 elements.addElement(INDIRECT_CRL); 373 return elements.elements(); 374 } 375 376 // Encodes this extension value 377 private void encodeThis() throws IOException { 378 379 if (distributionPoint == null && 380 revocationReasons == null && 381 !hasOnlyUserCerts && 382 !hasOnlyCACerts && 383 !hasOnlyAttributeCerts && 384 !isIndirectCRL) { 385 386 this.extensionValue = null; 387 return; 388 389 } 390 391 DerOutputStream tagged = new DerOutputStream(); 392 393 if (distributionPoint != null) { 394 DerOutputStream tmp = new DerOutputStream(); 395 distributionPoint.encode(tmp); 396 tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, true, 397 TAG_DISTRIBUTION_POINT), tmp); 398 } 399 400 if (hasOnlyUserCerts) { 401 DerOutputStream tmp = new DerOutputStream(); 402 tmp.putBoolean(hasOnlyUserCerts); 403 tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, false, 404 TAG_ONLY_USER_CERTS), tmp); 405 } 406 407 if (hasOnlyCACerts) { 408 DerOutputStream tmp = new DerOutputStream(); 409 tmp.putBoolean(hasOnlyCACerts); 410 tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, false, 411 TAG_ONLY_CA_CERTS), tmp); 412 } 413 414 if (revocationReasons != null) { 415 DerOutputStream tmp = new DerOutputStream(); 416 revocationReasons.encode(tmp); 417 tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, false, 418 TAG_ONLY_SOME_REASONS), tmp); 419 } 420 421 if (isIndirectCRL) { 422 DerOutputStream tmp = new DerOutputStream(); 423 tmp.putBoolean(isIndirectCRL); 424 tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, false, 425 TAG_INDIRECT_CRL), tmp); 426 } 427 428 if (hasOnlyAttributeCerts) { 429 DerOutputStream tmp = new DerOutputStream(); 430 tmp.putBoolean(hasOnlyAttributeCerts); 431 tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, false, 432 TAG_ONLY_ATTRIBUTE_CERTS), tmp); 433 } 434 435 DerOutputStream seq = new DerOutputStream(); 436 seq.write(DerValue.tag_Sequence, tagged); 437 this.extensionValue = seq.toByteArray(); 438 } 439 440 /** 441 * Returns the extension as user readable string. 442 */ 443 public String toString() { 444 445 StringBuilder sb = new StringBuilder(super.toString()); 446 sb.append("IssuingDistributionPoint [\n "); 447 448 if (distributionPoint != null) { 449 sb.append(distributionPoint); 450 } 451 452 if (revocationReasons != null) { 453 sb.append(revocationReasons); 454 } 455 456 sb.append((hasOnlyUserCerts) 457 ? (" Only contains user certs: true") 458 : (" Only contains user certs: false")).append("\n"); 459 460 sb.append((hasOnlyCACerts) 461 ? (" Only contains CA certs: true") 462 : (" Only contains CA certs: false")).append("\n"); 463 464 sb.append((hasOnlyAttributeCerts) 465 ? (" Only contains attribute certs: true") 466 : (" Only contains attribute certs: false")).append("\n"); 467 468 sb.append((isIndirectCRL) 469 ? (" Indirect CRL: true") 470 : (" Indirect CRL: false")).append("\n"); 471 472 sb.append("]\n"); 473 474 return sb.toString(); 475 } 476 477 } 478