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         StringBuilder sb = new StringBuilder();
180         sb.append(super.toString())
181             .append("PrivateKeyUsage: [\n");
182         if (notBefore != null) {
183             sb.append("From: ")
184                 .append(notBefore);
185             if (notAfter != null) {
186                 sb.append(", ");
187             }
188         }
189         if (notAfter != null) {
190             sb.append("To: ")
191                 .append(notAfter);
192         }
193         sb.append("]\n");
194         return sb.toString();
195     }
196 
197     /**
198      * Verify that the current time is within the validity period.
199      *
200      * @exception CertificateExpiredException if the certificate has expired.
201      * @exception CertificateNotYetValidException if the certificate is not
202      * yet valid.
203      */
valid()204     public void valid()
205     throws CertificateNotYetValidException, CertificateExpiredException {
206         Date now = new Date();
207         valid(now);
208     }
209 
210     /**
211      * Verify that the passed time is within the validity period.
212      *
213      * @exception CertificateExpiredException if the certificate has expired
214      * with respect to the <code>Date</code> supplied.
215      * @exception CertificateNotYetValidException if the certificate is not
216      * yet valid with respect to the <code>Date</code> supplied.
217      *
218      */
valid(Date now)219     public void valid(Date now)
220     throws CertificateNotYetValidException, CertificateExpiredException {
221         Objects.requireNonNull(now);
222         /*
223          * we use the internal Dates rather than the passed in Date
224          * because someone could override the Date methods after()
225          * and before() to do something entirely different.
226          */
227         if (notBefore != null && notBefore.after(now)) {
228             throw new CertificateNotYetValidException("NotBefore: " +
229                                                       notBefore.toString());
230         }
231         if (notAfter != null && notAfter.before(now)) {
232             throw new CertificateExpiredException("NotAfter: " +
233                                                   notAfter.toString());
234         }
235     }
236 
237     /**
238      * Write the extension to the OutputStream.
239      *
240      * @param out the OutputStream to write the extension to.
241      * @exception IOException on encoding errors.
242      */
encode(OutputStream out)243     public void encode(OutputStream out) throws IOException {
244         DerOutputStream tmp = new DerOutputStream();
245         if (extensionValue == null) {
246             extensionId = PKIXExtensions.PrivateKeyUsage_Id;
247             critical = false;
248             encodeThis();
249         }
250         super.encode(tmp);
251         out.write(tmp.toByteArray());
252     }
253 
254     /**
255      * Set the attribute value.
256      * @exception CertificateException on attribute handling errors.
257      */
set(String name, Object obj)258     public void set(String name, Object obj)
259     throws CertificateException, IOException {
260         if (!(obj instanceof Date)) {
261             throw new CertificateException("Attribute must be of type Date.");
262         }
263         if (name.equalsIgnoreCase(NOT_BEFORE)) {
264             notBefore = (Date)obj;
265         } else if (name.equalsIgnoreCase(NOT_AFTER)) {
266             notAfter = (Date)obj;
267         } else {
268           throw new CertificateException("Attribute name not recognized by"
269                            + " CertAttrSet:PrivateKeyUsage.");
270         }
271         encodeThis();
272     }
273 
274     /**
275      * Get the attribute value.
276      * @exception CertificateException on attribute handling errors.
277      */
get(String name)278     public Date get(String name) throws CertificateException {
279       if (name.equalsIgnoreCase(NOT_BEFORE)) {
280           return (new Date(notBefore.getTime()));
281       } else if (name.equalsIgnoreCase(NOT_AFTER)) {
282           return (new Date(notAfter.getTime()));
283       } else {
284           throw new CertificateException("Attribute name not recognized by"
285                            + " CertAttrSet:PrivateKeyUsage.");
286       }
287   }
288 
289     /**
290      * Delete the attribute value.
291      * @exception CertificateException on attribute handling errors.
292      */
delete(String name)293     public void delete(String name) throws CertificateException, IOException {
294         if (name.equalsIgnoreCase(NOT_BEFORE)) {
295             notBefore = null;
296         } else if (name.equalsIgnoreCase(NOT_AFTER)) {
297             notAfter = null;
298         } else {
299           throw new CertificateException("Attribute name not recognized by"
300                            + " CertAttrSet:PrivateKeyUsage.");
301         }
302         encodeThis();
303     }
304 
305     /**
306      * Return an enumeration of names of attributes existing within this
307      * attribute.
308      */
getElements()309     public Enumeration<String> getElements() {
310         AttributeNameEnumeration elements = new AttributeNameEnumeration();
311         elements.addElement(NOT_BEFORE);
312         elements.addElement(NOT_AFTER);
313 
314         return(elements.elements());
315     }
316 
317     /**
318      * Return the name of this attribute.
319      */
getName()320     public String getName() {
321       return(NAME);
322     }
323 }
324