1 /*
2  * Copyright (c) 1997, 2011, 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.security.cert.CertificateException;
31 import java.util.Enumeration;
32 import java.util.Vector;
33 
34 import sun.security.util.*;
35 
36 /**
37  * This class defines the certificate extension which specifies the
38  * Policy constraints.
39  * <p>
40  * The policy constraints extension can be used in certificates issued
41  * to CAs. The policy constraints extension constrains path validation
42  * in two ways. It can be used to prohibit policy mapping or require
43  * that each certificate in a path contain an acceptable policy
44  * identifier.<p>
45  * The ASN.1 syntax for this is (IMPLICIT tagging is defined in the
46  * module definition):
47  * <pre>
48  * PolicyConstraints ::= SEQUENCE {
49  *     requireExplicitPolicy [0] SkipCerts OPTIONAL,
50  *     inhibitPolicyMapping  [1] SkipCerts OPTIONAL
51  * }
52  * SkipCerts ::= INTEGER (0..MAX)
53  * </pre>
54  * @author Amit Kapoor
55  * @author Hemma Prafullchandra
56  * @see Extension
57  * @see CertAttrSet
58  */
59 public class PolicyConstraintsExtension extends Extension
60 implements CertAttrSet<String> {
61     /**
62      * Identifier for this attribute, to be used with the
63      * get, set, delete methods of Certificate, x509 type.
64      */
65     public static final String IDENT = "x509.info.extensions.PolicyConstraints";
66     /**
67      * Attribute names.
68      */
69     public static final String NAME = "PolicyConstraints";
70     public static final String REQUIRE = "require";
71     public static final String INHIBIT = "inhibit";
72 
73     private static final byte TAG_REQUIRE = 0;
74     private static final byte TAG_INHIBIT = 1;
75 
76     private int require = -1;
77     private int inhibit = -1;
78 
79     // Encode this extension value.
encodeThis()80     private void encodeThis() throws IOException {
81         if (require == -1 && inhibit == -1) {
82             this.extensionValue = null;
83             return;
84         }
85         DerOutputStream tagged = new DerOutputStream();
86         DerOutputStream seq = new DerOutputStream();
87 
88         if (require != -1) {
89             DerOutputStream tmp = new DerOutputStream();
90             tmp.putInteger(require);
91             tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
92                          false, TAG_REQUIRE), tmp);
93         }
94         if (inhibit != -1) {
95             DerOutputStream tmp = new DerOutputStream();
96             tmp.putInteger(inhibit);
97             tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
98                          false, TAG_INHIBIT), tmp);
99         }
100         seq.write(DerValue.tag_Sequence, tagged);
101         this.extensionValue = seq.toByteArray();
102     }
103 
104     /**
105      * Create a PolicyConstraintsExtension object with both
106      * require explicit policy and inhibit policy mapping. The
107      * extension is marked non-critical.
108      *
109      * @param require require explicit policy (-1 for optional).
110      * @param inhibit inhibit policy mapping (-1 for optional).
111      */
PolicyConstraintsExtension(int require, int inhibit)112     public PolicyConstraintsExtension(int require, int inhibit)
113     throws IOException {
114         this(Boolean.FALSE, require, inhibit);
115     }
116 
117     /**
118      * Create a PolicyConstraintsExtension object with specified
119      * criticality and both require explicit policy and inhibit
120      * policy mapping.
121      *
122      * @param critical true if the extension is to be treated as critical.
123      * @param require require explicit policy (-1 for optional).
124      * @param inhibit inhibit policy mapping (-1 for optional).
125      */
PolicyConstraintsExtension(Boolean critical, int require, int inhibit)126     public PolicyConstraintsExtension(Boolean critical, int require, int inhibit)
127     throws IOException {
128         this.require = require;
129         this.inhibit = inhibit;
130         this.extensionId = PKIXExtensions.PolicyConstraints_Id;
131         this.critical = critical.booleanValue();
132         encodeThis();
133     }
134 
135     /**
136      * Create the extension from its DER encoded value and criticality.
137      *
138      * @param critical true if the extension is to be treated as critical.
139      * @param value an array of DER encoded bytes of the actual value.
140      * @exception ClassCastException if value is not an array of bytes
141      * @exception IOException on error.
142      */
PolicyConstraintsExtension(Boolean critical, Object value)143     public PolicyConstraintsExtension(Boolean critical, Object value)
144     throws IOException {
145         this.extensionId = PKIXExtensions.PolicyConstraints_Id;
146         this.critical = critical.booleanValue();
147 
148         this.extensionValue = (byte[]) value;
149         DerValue val = new DerValue(this.extensionValue);
150         if (val.tag != DerValue.tag_Sequence) {
151             throw new IOException("Sequence tag missing for PolicyConstraint.");
152         }
153         DerInputStream in = val.data;
154         while (in != null && in.available() != 0) {
155             DerValue next = in.getDerValue();
156 
157             if (next.isContextSpecific(TAG_REQUIRE) && !next.isConstructed()) {
158                 if (this.require != -1)
159                     throw new IOException("Duplicate requireExplicitPolicy" +
160                           "found in the PolicyConstraintsExtension");
161                 next.resetTag(DerValue.tag_Integer);
162                 this.require = next.getInteger();
163 
164             } else if (next.isContextSpecific(TAG_INHIBIT) &&
165                        !next.isConstructed()) {
166                 if (this.inhibit != -1)
167                     throw new IOException("Duplicate inhibitPolicyMapping" +
168                           "found in the PolicyConstraintsExtension");
169                 next.resetTag(DerValue.tag_Integer);
170                 this.inhibit = next.getInteger();
171             } else
172                 throw new IOException("Invalid encoding of PolicyConstraint");
173         }
174     }
175 
176     /**
177      * Return the extension as user readable string.
178      */
toString()179     public String toString() {
180         String s;
181         s = super.toString() + "PolicyConstraints: [" + "  Require: ";
182         if (require == -1)
183             s += "unspecified;";
184         else
185             s += require + ";";
186         s += "\tInhibit: ";
187         if (inhibit == -1)
188             s += "unspecified";
189         else
190             s += inhibit;
191         s += " ]\n";
192         return s;
193     }
194 
195     /**
196      * Write the extension to the DerOutputStream.
197      *
198      * @param out the DerOutputStream to write the extension to.
199      * @exception IOException on encoding errors.
200      */
encode(OutputStream out)201     public void encode(OutputStream out) throws IOException {
202         DerOutputStream tmp = new DerOutputStream();
203         if (extensionValue == null) {
204           extensionId = PKIXExtensions.PolicyConstraints_Id;
205           critical = false;
206           encodeThis();
207         }
208         super.encode(tmp);
209         out.write(tmp.toByteArray());
210     }
211 
212     /**
213      * Set the attribute value.
214      */
set(String name, Object obj)215     public void set(String name, Object obj) throws IOException {
216         if (!(obj instanceof Integer)) {
217             throw new IOException("Attribute value should be of type Integer.");
218         }
219         if (name.equalsIgnoreCase(REQUIRE)) {
220             require = ((Integer)obj).intValue();
221         } else if (name.equalsIgnoreCase(INHIBIT)) {
222             inhibit = ((Integer)obj).intValue();
223         } else {
224           throw new IOException("Attribute name " + "[" + name + "]" +
225                                 " not recognized by " +
226                                 "CertAttrSet:PolicyConstraints.");
227         }
228         encodeThis();
229     }
230 
231     /**
232      * Get the attribute value.
233      */
get(String name)234     public Integer get(String name) throws IOException {
235         if (name.equalsIgnoreCase(REQUIRE)) {
236             return new Integer(require);
237         } else if (name.equalsIgnoreCase(INHIBIT)) {
238             return new Integer(inhibit);
239         } else {
240           throw new IOException("Attribute name not recognized by " +
241                                 "CertAttrSet:PolicyConstraints.");
242         }
243     }
244 
245     /**
246      * Delete the attribute value.
247      */
delete(String name)248     public void delete(String name) throws IOException {
249         if (name.equalsIgnoreCase(REQUIRE)) {
250             require = -1;
251         } else if (name.equalsIgnoreCase(INHIBIT)) {
252             inhibit = -1;
253         } else {
254           throw new IOException("Attribute name not recognized by " +
255                                 "CertAttrSet:PolicyConstraints.");
256         }
257         encodeThis();
258     }
259 
260     /**
261      * Return an enumeration of names of attributes existing within this
262      * attribute.
263      */
getElements()264     public Enumeration<String> getElements() {
265         AttributeNameEnumeration elements = new AttributeNameEnumeration();
266         elements.addElement(REQUIRE);
267         elements.addElement(INHIBIT);
268 
269         return (elements.elements());
270     }
271 
272     /**
273      * Return the name of this attribute.
274      */
getName()275     public String getName() {
276         return (NAME);
277     }
278 }
279