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      */
IssuingDistributionPointExtension( DistributionPointName distributionPoint, ReasonFlags revocationReasons, boolean hasOnlyUserCerts, boolean hasOnlyCACerts, boolean hasOnlyAttributeCerts, boolean isIndirectCRL)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      */
IssuingDistributionPointExtension(Boolean critical, Object value)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      */
getName()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      */
encode(OutputStream out)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      */
set(String name, Object obj)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 
265         } else if (name.equalsIgnoreCase(INDIRECT_CRL)) {
266             if (!(obj instanceof Boolean)) {
267                 throw new IOException(
268                     "Attribute value should be of type Boolean.");
269             }
270             isIndirectCRL = ((Boolean)obj).booleanValue();
271 
272         } else if (name.equalsIgnoreCase(ONLY_USER_CERTS)) {
273             if (!(obj instanceof Boolean)) {
274                 throw new IOException(
275                     "Attribute value should be of type Boolean.");
276             }
277             hasOnlyUserCerts = ((Boolean)obj).booleanValue();
278 
279         } else if (name.equalsIgnoreCase(ONLY_CA_CERTS)) {
280             if (!(obj instanceof Boolean)) {
281                 throw new IOException(
282                     "Attribute value should be of type Boolean.");
283             }
284             hasOnlyCACerts = ((Boolean)obj).booleanValue();
285 
286         } else if (name.equalsIgnoreCase(ONLY_ATTRIBUTE_CERTS)) {
287             if (!(obj instanceof Boolean)) {
288                 throw new IOException(
289                     "Attribute value should be of type Boolean.");
290             }
291             hasOnlyAttributeCerts = ((Boolean)obj).booleanValue();
292 
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      */
get(String name)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      */
delete(String name)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      */
getElements()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
encodeThis()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      */
toString()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