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.security.cert.CertificateParsingException;
32 import java.security.cert.CertificateExpiredException;
33 import java.security.cert.CertificateNotYetValidException;
34 import java.util.Date;
35 import java.util.Enumeration;
36 
37 import sun.security.util.*;
38 
39 /**
40  * This class defines the Private Key Usage Extension.
41  *
42  * <p>The Private Key Usage Period extension allows the certificate issuer
43  * to specify a different validity period for the private key than the
44  * certificate. This extension is intended for use with digital
45  * signature keys.  This extension consists of two optional components
46  * notBefore and notAfter.  The private key associated with the
47  * certificate should not be used to sign objects before or after the
48  * times specified by the two components, respectively.
49  *
50  * <pre>
51  * PrivateKeyUsagePeriod ::= SEQUENCE {
52  *     notBefore  [0]  GeneralizedTime OPTIONAL,
53  *     notAfter   [1]  GeneralizedTime OPTIONAL }
54  * </pre>
55  *
56  * @author Amit Kapoor
57  * @author Hemma Prafullchandra
58  * @see Extension
59  * @see CertAttrSet
60  */
61 public class PrivateKeyUsageExtension extends Extension
62 implements CertAttrSet<String> {
63     /**
64      * Identifier for this attribute, to be used with the
65      * get, set, delete methods of Certificate, x509 type.
66      */
67     public static final String IDENT = "x509.info.extensions.PrivateKeyUsage";
68     /**
69      * Sub attributes name for this CertAttrSet.
70      */
71     public static final String NAME = "PrivateKeyUsage";
72     public static final String NOT_BEFORE = "not_before";
73     public static final String NOT_AFTER = "not_after";
74 
75     // Private data members
76     private static final byte TAG_BEFORE = 0;
77     private static final byte TAG_AFTER = 1;
78 
79     private Date        notBefore = null;
80     private Date        notAfter = null;
81 
82     // Encode this extension value.
encodeThis()83     private void encodeThis() throws IOException {
84         if (notBefore == null && notAfter == null) {
85             this.extensionValue = null;
86             return;
87         }
88         DerOutputStream seq = new DerOutputStream();
89 
90         DerOutputStream tagged = new DerOutputStream();
91         if (notBefore != null) {
92             DerOutputStream tmp = new DerOutputStream();
93             tmp.putGeneralizedTime(notBefore);
94             tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
95                                  false, TAG_BEFORE), tmp);
96         }
97         if (notAfter != null) {
98             DerOutputStream tmp = new DerOutputStream();
99             tmp.putGeneralizedTime(notAfter);
100             tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
101                                  false, TAG_AFTER), tmp);
102         }
103         seq.write(DerValue.tag_Sequence, tagged);
104         this.extensionValue = seq.toByteArray();
105     }
106 
107     /**
108      * The default constructor for PrivateKeyUsageExtension.
109      *
110      * @param notBefore the date/time before which the private key
111      *         should not be used.
112      * @param notAfter the date/time after which the private key
113      *         should not be used.
114      */
PrivateKeyUsageExtension(Date notBefore, Date notAfter)115     public PrivateKeyUsageExtension(Date notBefore, Date notAfter)
116     throws IOException {
117         this.notBefore = notBefore;
118         this.notAfter = notAfter;
119 
120         this.extensionId = PKIXExtensions.PrivateKeyUsage_Id;
121         this.critical = false;
122         encodeThis();
123     }
124 
125     /**
126      * Create the extension from the passed DER encoded value.
127      *
128      * @param critical true if the extension is to be treated as critical.
129      * @param value an array of DER encoded bytes of the actual value.
130      * @exception ClassCastException if value is not an array of bytes
131      * @exception CertificateException on certificate parsing errors.
132      * @exception IOException on error.
133      */
PrivateKeyUsageExtension(Boolean critical, Object value)134     public PrivateKeyUsageExtension(Boolean critical, Object value)
135     throws CertificateException, IOException {
136         this.extensionId = PKIXExtensions.PrivateKeyUsage_Id;
137         this.critical = critical.booleanValue();
138 
139         this.extensionValue = (byte[]) value;
140         DerInputStream str = new DerInputStream(this.extensionValue);
141         DerValue[] seq = str.getSequence(2);
142 
143         // NB. this is always encoded with the IMPLICIT tag
144         // The checks only make sense if we assume implicit tagging,
145         // with explicit tagging the form is always constructed.
146         for (int i = 0; i < seq.length; i++) {
147             DerValue opt = seq[i];
148 
149             if (opt.isContextSpecific(TAG_BEFORE) &&
150                 !opt.isConstructed()) {
151                 if (notBefore != null) {
152                     throw new CertificateParsingException(
153                         "Duplicate notBefore in PrivateKeyUsage.");
154                 }
155                 opt.resetTag(DerValue.tag_GeneralizedTime);
156                 str = new DerInputStream(opt.toByteArray());
157                 notBefore = str.getGeneralizedTime();
158 
159             } else if (opt.isContextSpecific(TAG_AFTER) &&
160                        !opt.isConstructed()) {
161                 if (notAfter != null) {
162                     throw new CertificateParsingException(
163                         "Duplicate notAfter in PrivateKeyUsage.");
164                 }
165                 opt.resetTag(DerValue.tag_GeneralizedTime);
166                 str = new DerInputStream(opt.toByteArray());
167                 notAfter = str.getGeneralizedTime();
168             } else
169                 throw new IOException("Invalid encoding of " +
170                                       "PrivateKeyUsageExtension");
171         }
172     }
173 
174     /**
175      * Return the printable string.
176      */
toString()177     public String toString() {
178         return(super.toString() +
179                 "PrivateKeyUsage: [\n" +
180                 ((notBefore == null) ? "" : "From: " + notBefore.toString() + ", ")
181                 + ((notAfter == null) ? "" : "To: " + notAfter.toString())
182                 + "]\n");
183     }
184 
185     /**
186      * Verify that that the current time is within the validity period.
187      *
188      * @exception CertificateExpiredException if the certificate has expired.
189      * @exception CertificateNotYetValidException if the certificate is not
190      * yet valid.
191      */
valid()192     public void valid()
193     throws CertificateNotYetValidException, CertificateExpiredException {
194         Date now = new Date();
195         valid(now);
196     }
197 
198     /**
199      * Verify that that the passed time is within the validity period.
200      *
201      * @exception CertificateExpiredException if the certificate has expired
202      * with respect to the <code>Date</code> supplied.
203      * @exception CertificateNotYetValidException if the certificate is not
204      * yet valid with respect to the <code>Date</code> supplied.
205      *
206      */
valid(Date now)207     public void valid(Date now)
208     throws CertificateNotYetValidException, CertificateExpiredException {
209         /*
210          * we use the internal Dates rather than the passed in Date
211          * because someone could override the Date methods after()
212          * and before() to do something entirely different.
213          */
214         if (notBefore.after(now)) {
215             throw new CertificateNotYetValidException("NotBefore: " +
216                                                       notBefore.toString());
217         }
218         if (notAfter.before(now)) {
219             throw new CertificateExpiredException("NotAfter: " +
220                                                   notAfter.toString());
221         }
222     }
223 
224     /**
225      * Write the extension to the OutputStream.
226      *
227      * @param out the OutputStream to write the extension to.
228      * @exception IOException on encoding errors.
229      */
encode(OutputStream out)230     public void encode(OutputStream out) throws IOException {
231         DerOutputStream tmp = new DerOutputStream();
232         if (extensionValue == null) {
233             extensionId = PKIXExtensions.PrivateKeyUsage_Id;
234             critical = false;
235             encodeThis();
236         }
237         super.encode(tmp);
238         out.write(tmp.toByteArray());
239     }
240 
241     /**
242      * Set the attribute value.
243      * @exception CertificateException on attribute handling errors.
244      */
set(String name, Object obj)245     public void set(String name, Object obj)
246     throws CertificateException, IOException {
247         if (!(obj instanceof Date)) {
248             throw new CertificateException("Attribute must be of type Date.");
249         }
250         if (name.equalsIgnoreCase(NOT_BEFORE)) {
251             notBefore = (Date)obj;
252         } else if (name.equalsIgnoreCase(NOT_AFTER)) {
253             notAfter = (Date)obj;
254         } else {
255           throw new CertificateException("Attribute name not recognized by"
256                            + " CertAttrSet:PrivateKeyUsage.");
257         }
258         encodeThis();
259     }
260 
261     /**
262      * Get the attribute value.
263      * @exception CertificateException on attribute handling errors.
264      */
get(String name)265     public Date get(String name) throws CertificateException {
266       if (name.equalsIgnoreCase(NOT_BEFORE)) {
267           return (new Date(notBefore.getTime()));
268       } else if (name.equalsIgnoreCase(NOT_AFTER)) {
269           return (new Date(notAfter.getTime()));
270       } else {
271           throw new CertificateException("Attribute name not recognized by"
272                            + " CertAttrSet:PrivateKeyUsage.");
273       }
274   }
275 
276     /**
277      * Delete the attribute value.
278      * @exception CertificateException on attribute handling errors.
279      */
delete(String name)280     public void delete(String name) throws CertificateException, IOException {
281         if (name.equalsIgnoreCase(NOT_BEFORE)) {
282             notBefore = null;
283         } else if (name.equalsIgnoreCase(NOT_AFTER)) {
284             notAfter = null;
285         } else {
286           throw new CertificateException("Attribute name not recognized by"
287                            + " CertAttrSet:PrivateKeyUsage.");
288         }
289         encodeThis();
290     }
291 
292     /**
293      * Return an enumeration of names of attributes existing within this
294      * attribute.
295      */
getElements()296     public Enumeration<String> getElements() {
297         AttributeNameEnumeration elements = new AttributeNameEnumeration();
298         elements.addElement(NOT_BEFORE);
299         elements.addElement(NOT_AFTER);
300 
301         return(elements.elements());
302     }
303 
304     /**
305      * Return the name of this attribute.
306      */
getName()307     public String getName() {
308       return(NAME);
309     }
310 }
311