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