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 26 package sun.security.x509; 27 28 import java.io.IOException; 29 import java.io.OutputStream; 30 import java.util.Enumeration; 31 32 import 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 */ 53 public class BasicConstraintsExtension extends Extension 54 implements 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 encodeThis()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 */ BasicConstraintsExtension(boolean ca, int len)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 */ BasicConstraintsExtension(Boolean critical, boolean ca, int len)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 */ BasicConstraintsExtension(Boolean critical, Object value)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 */ toString()173 public String toString() { 174 String s = super.toString() + "BasicConstraints:[\n"; 175 176 s += ((ca) ? (" CA:true") : (" CA:false")) + "\n"; 177 if (pathLen >= 0) { 178 s += " PathLen:" + pathLen + "\n"; 179 } else { 180 s += " PathLen: undefined\n"; 181 } 182 return (s + "]\n"); 183 } 184 185 /** 186 * Encode this extension value to the output stream. 187 * 188 * @param out the DerOutputStream to encode the extension to. 189 */ encode(OutputStream out)190 public void encode(OutputStream out) throws IOException { 191 DerOutputStream tmp = new DerOutputStream(); 192 if (extensionValue == null) { 193 this.extensionId = PKIXExtensions.BasicConstraints_Id; 194 if (ca) { 195 critical = true; 196 } else { 197 critical = false; 198 } 199 encodeThis(); 200 } 201 super.encode(tmp); 202 203 out.write(tmp.toByteArray()); 204 } 205 206 /** 207 * Set the attribute value. 208 */ set(String name, Object obj)209 public void set(String name, Object obj) throws IOException { 210 if (name.equalsIgnoreCase(IS_CA)) { 211 if (!(obj instanceof Boolean)) { 212 throw new IOException("Attribute value should be of type Boolean."); 213 } 214 ca = ((Boolean)obj).booleanValue(); 215 } else if (name.equalsIgnoreCase(PATH_LEN)) { 216 if (!(obj instanceof Integer)) { 217 throw new IOException("Attribute value should be of type Integer."); 218 } 219 pathLen = ((Integer)obj).intValue(); 220 } else { 221 throw new IOException("Attribute name not recognized by " + 222 "CertAttrSet:BasicConstraints."); 223 } 224 encodeThis(); 225 } 226 227 /** 228 * Get the attribute value. 229 */ get(String name)230 public Object get(String name) throws IOException { 231 if (name.equalsIgnoreCase(IS_CA)) { 232 return (Boolean.valueOf(ca)); 233 } else if (name.equalsIgnoreCase(PATH_LEN)) { 234 return (Integer.valueOf(pathLen)); 235 } else { 236 throw new IOException("Attribute name not recognized by " + 237 "CertAttrSet:BasicConstraints."); 238 } 239 } 240 241 /** 242 * Delete the attribute value. 243 */ delete(String name)244 public void delete(String name) throws IOException { 245 if (name.equalsIgnoreCase(IS_CA)) { 246 ca = false; 247 } else if (name.equalsIgnoreCase(PATH_LEN)) { 248 pathLen = -1; 249 } else { 250 throw new IOException("Attribute name not recognized by " + 251 "CertAttrSet:BasicConstraints."); 252 } 253 encodeThis(); 254 } 255 256 /** 257 * Return an enumeration of names of attributes existing within this 258 * attribute. 259 */ getElements()260 public Enumeration<String> getElements() { 261 AttributeNameEnumeration elements = new AttributeNameEnumeration(); 262 elements.addElement(IS_CA); 263 elements.addElement(PATH_LEN); 264 265 return (elements.elements()); 266 } 267 268 /** 269 * Return the name of this attribute. 270 */ getName()271 public String getName() { 272 return (NAME); 273 } 274 } 275