1/* 2 * Copyright (c) 1997, 2009, 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 26package sun.security.x509; 27 28import java.io.IOException; 29import java.io.OutputStream; 30import java.util.Enumeration; 31 32import sun.security.util.*; 33 34/** 35 * This class represents the Basic Constraints Extension. 36 * 37 * <p>The basic constraints extension identifies whether the subject of the 38 * certificate is a CA and how deep a certification path may exist 39 * through that CA. 40 * 41 * <pre> 42 * The ASN.1 syntax for this extension is: 43 * BasicConstraints ::= SEQUENCE { 44 * cA BOOLEAN DEFAULT FALSE, 45 * pathLenConstraint INTEGER (0..MAX) OPTIONAL 46 * } 47 * </pre> 48 * @author Amit Kapoor 49 * @author Hemma Prafullchandra 50 * @see CertAttrSet 51 * @see Extension 52 */ 53public class BasicConstraintsExtension extends Extension 54implements CertAttrSet<String> { 55 /** 56 * Identifier for this attribute, to be used with the 57 * get, set, delete methods of Certificate, x509 type. 58 */ 59 public static final String IDENT = "x509.info.extensions.BasicConstraints"; 60 /** 61 * Attribute names. 62 */ 63 public static final String NAME = "BasicConstraints"; 64 public static final String IS_CA = "is_ca"; 65 public static final String PATH_LEN = "path_len"; 66 67 // Private data members 68 private boolean ca = false; 69 private int pathLen = -1; 70 71 // Encode this extension value 72 private void encodeThis() throws IOException { 73 DerOutputStream out = new DerOutputStream(); 74 DerOutputStream tmp = new DerOutputStream(); 75 76 if (ca) { 77 tmp.putBoolean(ca); 78 // Only encode pathLen when ca == true 79 if (pathLen >= 0) { 80 tmp.putInteger(pathLen); 81 } 82 } 83 out.write(DerValue.tag_Sequence, tmp); 84 this.extensionValue = out.toByteArray(); 85 } 86 87 /** 88 * Default constructor for this object. The extension is marked 89 * critical if the ca flag is true, false otherwise. 90 * 91 * @param ca true, if the subject of the Certificate is a CA. 92 * @param len specifies the depth of the certification path. 93 */ 94 public BasicConstraintsExtension(boolean ca, int len) throws IOException { 95 this(Boolean.valueOf(ca), ca, len); 96 } 97 98 /** 99 * Constructor for this object with specified criticality. 100 * 101 * @param critical true, if the extension should be marked critical 102 * @param ca true, if the subject of the Certificate is a CA. 103 * @param len specifies the depth of the certification path. 104 */ 105 public BasicConstraintsExtension(Boolean critical, boolean ca, int len) 106 throws IOException { 107 this.ca = ca; 108 this.pathLen = len; 109 this.extensionId = PKIXExtensions.BasicConstraints_Id; 110 this.critical = critical.booleanValue(); 111 encodeThis(); 112 } 113 114 /** 115 * Create the extension from the passed DER encoded value of the same. 116 * 117 * @param critical flag indicating if extension is critical or not 118 * @param value an array containing the DER encoded bytes of the extension. 119 * @exception ClassCastException if value is not an array of bytes 120 * @exception IOException on error. 121 */ 122 public BasicConstraintsExtension(Boolean critical, Object value) 123 throws IOException 124 { 125 this.extensionId = PKIXExtensions.BasicConstraints_Id; 126 this.critical = critical.booleanValue(); 127 128 this.extensionValue = (byte[]) value; 129 DerValue val = new DerValue(this.extensionValue); 130 if (val.tag != DerValue.tag_Sequence) { 131 throw new IOException("Invalid encoding of BasicConstraints"); 132 } 133 134 if (val.data == null || val.data.available() == 0) { 135 // non-CA cert ("cA" field is FALSE by default), return -1 136 return; 137 } 138 DerValue opt = val.data.getDerValue(); 139 if (opt.tag != DerValue.tag_Boolean) { 140 // non-CA cert ("cA" field is FALSE by default), return -1 141 return; 142 } 143 144 this.ca = opt.getBoolean(); 145 if (val.data.available() == 0) { 146 // From PKIX profile: 147 // Where pathLenConstraint does not appear, there is no 148 // limit to the allowed length of the certification path. 149 this.pathLen = Integer.MAX_VALUE; 150 return; 151 } 152 153 opt = val.data.getDerValue(); 154 if (opt.tag != DerValue.tag_Integer) { 155 throw new IOException("Invalid encoding of BasicConstraints"); 156 } 157 this.pathLen = opt.getInteger(); 158 /* 159 * Activate this check once again after PKIX profiling 160 * is a standard and this check no longer imposes an 161 * interoperability barrier. 162 * if (ca) { 163 * if (!this.critical) { 164 * throw new IOException("Criticality cannot be false for CA."); 165 * } 166 * } 167 */ 168 } 169 170 /** 171 * Return user readable form of extension. 172 */ 173 public String toString() { 174 return super.toString() + 175 "BasicConstraints:[\n CA:" + ca + 176 "\n PathLen:" + 177 ((pathLen >= 0) ? String.valueOf(pathLen) : " undefined") + 178 "\n]\n"; 179 } 180 181 /** 182 * Encode this extension value to the output stream. 183 * 184 * @param out the DerOutputStream to encode the extension to. 185 */ 186 public void encode(OutputStream out) throws IOException { 187 DerOutputStream tmp = new DerOutputStream(); 188 if (extensionValue == null) { 189 this.extensionId = PKIXExtensions.BasicConstraints_Id; 190 if (ca) { 191 critical = true; 192 } else { 193 critical = false; 194 } 195 encodeThis(); 196 } 197 super.encode(tmp); 198 199 out.write(tmp.toByteArray()); 200 } 201 202 /** 203 * Set the attribute value. 204 */ 205 public void set(String name, Object obj) throws IOException { 206 if (name.equalsIgnoreCase(IS_CA)) { 207 if (!(obj instanceof Boolean)) { 208 throw new IOException("Attribute value should be of type Boolean."); 209 } 210 ca = ((Boolean)obj).booleanValue(); 211 } else if (name.equalsIgnoreCase(PATH_LEN)) { 212 if (!(obj instanceof Integer)) { 213 throw new IOException("Attribute value should be of type Integer."); 214 } 215 pathLen = ((Integer)obj).intValue(); 216 } else { 217 throw new IOException("Attribute name not recognized by " + 218 "CertAttrSet:BasicConstraints."); 219 } 220 encodeThis(); 221 } 222 223 /** 224 * Get the attribute value. 225 */ 226 public Object get(String name) throws IOException { 227 if (name.equalsIgnoreCase(IS_CA)) { 228 return (Boolean.valueOf(ca)); 229 } else if (name.equalsIgnoreCase(PATH_LEN)) { 230 return (Integer.valueOf(pathLen)); 231 } else { 232 throw new IOException("Attribute name not recognized by " + 233 "CertAttrSet:BasicConstraints."); 234 } 235 } 236 237 /** 238 * Delete the attribute value. 239 */ 240 public void delete(String name) throws IOException { 241 if (name.equalsIgnoreCase(IS_CA)) { 242 ca = false; 243 } else if (name.equalsIgnoreCase(PATH_LEN)) { 244 pathLen = -1; 245 } else { 246 throw new IOException("Attribute name not recognized by " + 247 "CertAttrSet:BasicConstraints."); 248 } 249 encodeThis(); 250 } 251 252 /** 253 * Return an enumeration of names of attributes existing within this 254 * attribute. 255 */ 256 public Enumeration<String> getElements() { 257 AttributeNameEnumeration elements = new AttributeNameEnumeration(); 258 elements.addElement(IS_CA); 259 elements.addElement(PATH_LEN); 260 261 return (elements.elements()); 262 } 263 264 /** 265 * Return the name of this attribute. 266 */ 267 public String getName() { 268 return (NAME); 269 } 270} 271