001: /*
002: * @(#)BasicConstraintsExtension.java 1.20 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.security.x509;
029:
030: import java.io.IOException;
031: import java.io.InputStream;
032: import java.io.OutputStream;
033: import java.lang.reflect.Array;
034: import java.util.Enumeration;
035:
036: import sun.security.util.*;
037:
038: /**
039: * This class represents the Basic Constraints Extension.
040: *
041: * <p>The basic constraints extension identifies whether the subject of the
042: * certificate is a CA and how deep a certification path may exist
043: * through that CA.
044: *
045: * <pre>
046: * The ASN.1 syntax for this extension is:
047: * BasicConstraints ::= SEQUENCE {
048: * cA BOOLEAN DEFAULT FALSE,
049: * pathLenConstraint INTEGER (0..MAX) OPTIONAL
050: * }
051: * </pre>
052: * @author Amit Kapoor
053: * @author Hemma Prafullchandra
054: * @version 1.13
055: * @see CertAttrSet
056: * @see Extension
057: */
058: public class BasicConstraintsExtension extends Extension implements
059: CertAttrSet {
060: /**
061: * Identifier for this attribute, to be used with the
062: * get, set, delete methods of Certificate, x509 type.
063: */
064: public static final String IDENT = "x509.info.extensions.BasicConstraints";
065: /**
066: * Attribute names.
067: */
068: public static final String NAME = "BasicConstraints";
069: public static final String IS_CA = "is_ca";
070: public static final String PATH_LEN = "path_len";
071:
072: // Private data members
073: private boolean ca = false;
074: private int pathLen = -1;
075:
076: // Encode this extension value
077: private void encodeThis() throws IOException {
078: if (ca == false && pathLen < 0) {
079: this .extensionValue = null;
080: return;
081: }
082: DerOutputStream out = new DerOutputStream();
083: DerOutputStream tmp = new DerOutputStream();
084:
085: if (ca) {
086: tmp.putBoolean(ca);
087: }
088: if (pathLen >= 0) {
089: tmp.putInteger(pathLen);
090: }
091: out.write(DerValue.tag_Sequence, tmp);
092: this .extensionValue = out.toByteArray();
093: }
094:
095: /**
096: * Default constructor for this object. The extension is marked
097: * critical if the ca flag is true, false otherwise.
098: *
099: * @param ca true, if the subject of the Certificate is a CA.
100: * @param len specifies the depth of the certification path.
101: */
102: public BasicConstraintsExtension(boolean ca, int len)
103: throws IOException {
104: this (new Boolean(ca), ca, len);
105: }
106:
107: /**
108: * Constructor for this object with specified criticality.
109: *
110: * @param critical true, if the extension should be marked critical
111: * @param ca true, if the subject of the Certificate is a CA.
112: * @param len specifies the depth of the certification path.
113: */
114: public BasicConstraintsExtension(Boolean critical, boolean ca,
115: int len) throws IOException {
116: this .ca = ca;
117: this .pathLen = len;
118: this .extensionId = PKIXExtensions.BasicConstraints_Id;
119: this .critical = critical.booleanValue();
120: encodeThis();
121: }
122:
123: /**
124: * Create the extension from the passed DER encoded value of the same.
125: *
126: * @param extension the DER encoded value of the extension.
127: * @exception IOException on error.
128: */
129: public BasicConstraintsExtension(Boolean critical, Object value)
130: throws IOException {
131: this .extensionId = PKIXExtensions.BasicConstraints_Id;
132: this .critical = critical.booleanValue();
133:
134: if (value instanceof byte[]) {
135: int len = Array.getLength(value);
136: byte[] extValue = new byte[len];
137: System.arraycopy(value, 0, extValue, 0, len);
138:
139: this .extensionValue = extValue;
140: DerValue val = new DerValue(extValue);
141: if (val.tag != DerValue.tag_Sequence) {
142: throw new IOException(
143: "Invalid encoding of BasicConstraints");
144: }
145:
146: if (val.data == null) {
147: // non-CA cert ("cA" field is FALSE by default), return -1
148: return;
149: }
150: DerValue opt = val.data.getDerValue();
151: if (opt.tag != DerValue.tag_Boolean) {
152: // non-CA cert ("cA" field is FALSE by default), return -1
153: return;
154: }
155:
156: this .ca = opt.getBoolean();
157: if (val.data.available() == 0) {
158: // From PKIX profile:
159: // Where pathLenConstraint does not appear, there is no
160: // limit to the allowed length of the certification path.
161: this .pathLen = Integer.MAX_VALUE;
162: return;
163: }
164:
165: opt = val.data.getDerValue();
166: if (opt.tag != DerValue.tag_Integer) {
167: throw new IOException(
168: "Invalid encoding of BasicConstraints");
169: }
170: this .pathLen = opt.getInteger();
171: /*
172: * Activate this check once again after PKIX profiling
173: * is a standard and this check no longer imposes an
174: * interoperability barrier.
175: * if (ca) {
176: * if (!this.critical) {
177: * throw new IOException("Criticality cannot be false for CA.");
178: * }
179: * }
180: */
181: } else
182: throw new IOException("Invalid argument type");
183: }
184:
185: /**
186: * Return user readable form of extension.
187: */
188: public String toString() {
189: String s = super .toString() + "BasicConstraints:[\n";
190:
191: s += ((ca) ? ("CA:true") : ("CA:false")) + "\n";
192: if (pathLen >= 0) {
193: s += "PathLen:" + pathLen + "\n";
194: } else {
195: s += "PathLen: undefined\n";
196: }
197: return (s + "]\n");
198: }
199:
200: /**
201: * Decode the extension from the InputStream.
202: *
203: * @param in the InputStream to unmarshal the contents from.
204: * @exception IOException on decoding or validity errors.
205: */
206: public void decode(InputStream in) throws IOException {
207: throw new IOException("Method not to be called directly.");
208: }
209:
210: /**
211: * Encode this extension value to the output stream.
212: *
213: * @param out the DerOutputStream to encode the extension to.
214: */
215: public void encode(OutputStream out) throws IOException {
216: DerOutputStream tmp = new DerOutputStream();
217: if (extensionValue == null) {
218: this .extensionId = PKIXExtensions.BasicConstraints_Id;
219: if (ca) {
220: critical = true;
221: } else {
222: critical = false;
223: }
224: encodeThis();
225: }
226: super .encode(tmp);
227:
228: out.write(tmp.toByteArray());
229: }
230:
231: /**
232: * Set the attribute value.
233: */
234: public void set(String name, Object obj) throws IOException {
235: if (name.equalsIgnoreCase(IS_CA)) {
236: if (!(obj instanceof Boolean)) {
237: throw new IOException(
238: "Attribute value should be of type Boolean.");
239: }
240: ca = ((Boolean) obj).booleanValue();
241: } else if (name.equalsIgnoreCase(PATH_LEN)) {
242: if (!(obj instanceof Integer)) {
243: throw new IOException(
244: "Attribute value should be of type Integer.");
245: }
246: pathLen = ((Integer) obj).intValue();
247: } else {
248: throw new IOException("Attribute name not recognized by "
249: + "CertAttrSet:BasicConstraints.");
250: }
251: encodeThis();
252: }
253:
254: /**
255: * Get the attribute value.
256: */
257: public Object get(String name) throws IOException {
258: if (name.equalsIgnoreCase(IS_CA)) {
259: return (new Boolean(ca));
260: } else if (name.equalsIgnoreCase(PATH_LEN)) {
261: return (new Integer(pathLen));
262: } else {
263: throw new IOException("Attribute name not recognized by "
264: + "CertAttrSet:BasicConstraints.");
265: }
266: }
267:
268: /**
269: * Delete the attribute value.
270: */
271: public void delete(String name) throws IOException {
272: if (name.equalsIgnoreCase(IS_CA)) {
273: ca = false;
274: } else if (name.equalsIgnoreCase(PATH_LEN)) {
275: pathLen = -1;
276: } else {
277: throw new IOException("Attribute name not recognized by "
278: + "CertAttrSet:BasicConstraints.");
279: }
280: encodeThis();
281: }
282:
283: /**
284: * Return an enumeration of names of attributes existing within this
285: * attribute.
286: */
287: public Enumeration getElements() {
288: AttributeNameEnumeration elements = new AttributeNameEnumeration();
289: elements.addElement(IS_CA);
290: elements.addElement(PATH_LEN);
291:
292: return (elements.elements());
293: }
294:
295: /**
296: * Return the name of this attribute.
297: */
298: public String getName() {
299: return (NAME);
300: }
301: }
|