1 /*
2  * Copyright (c) 1997, 2020, 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 package sun.security.x509;
26 
27 import java.io.IOException;
28 import java.io.OutputStream;
29 import java.security.cert.*;
30 import java.util.Date;
31 import java.util.Enumeration;
32 
33 import sun.security.util.*;
34 
35 /**
36  * This class defines the interval for which the certificate is valid.
37  *
38  * @author Amit Kapoor
39  * @author Hemma Prafullchandra
40  * @see CertAttrSet
41  */
42 public class CertificateValidity implements CertAttrSet<String> {
43     /**
44      * Identifier for this attribute, to be used with the
45      * get, set, delete methods of Certificate, x509 type.
46      */
47     public static final String IDENT = "x509.info.validity";
48     /**
49      * Sub attributes name for this CertAttrSet.
50      */
51     public static final String NAME = "validity";
52     public static final String NOT_BEFORE = "notBefore";
53     public static final String NOT_AFTER = "notAfter";
54     /**
55      * YR_2050 date and time set to Jan01 00:00 2050 GMT
56      */
57     static final long YR_2050 = 2524608000000L;
58 
59     // Private data members
60     private Date        notBefore;
61     private Date        notAfter;
62 
63     // Returns the first time the certificate is valid.
getNotBefore()64     private Date getNotBefore() {
65         return (new Date(notBefore.getTime()));
66     }
67 
68     // Returns the last time the certificate is valid.
getNotAfter()69     private Date getNotAfter() {
70        return (new Date(notAfter.getTime()));
71     }
72 
73     // Construct the class from the DerValue
construct(DerValue derVal)74     private void construct(DerValue derVal) throws IOException {
75         if (derVal.tag != DerValue.tag_Sequence) {
76             throw new IOException("Invalid encoded CertificateValidity, " +
77                                   "starting sequence tag missing.");
78         }
79         // check if UTCTime encoded or GeneralizedTime
80         if (derVal.data.available() == 0)
81             throw new IOException("No data encoded for CertificateValidity");
82 
83         DerInputStream derIn = new DerInputStream(derVal.toByteArray());
84         DerValue[] seq = derIn.getSequence(2);
85         if (seq.length != 2)
86             throw new IOException("Invalid encoding for CertificateValidity");
87 
88         if (seq[0].tag == DerValue.tag_UtcTime) {
89             notBefore = derVal.data.getUTCTime();
90         } else if (seq[0].tag == DerValue.tag_GeneralizedTime) {
91             notBefore = derVal.data.getGeneralizedTime();
92         } else {
93             throw new IOException("Invalid encoding for CertificateValidity");
94         }
95 
96         if (seq[1].tag == DerValue.tag_UtcTime) {
97             notAfter = derVal.data.getUTCTime();
98         } else if (seq[1].tag == DerValue.tag_GeneralizedTime) {
99             notAfter = derVal.data.getGeneralizedTime();
100         } else {
101             throw new IOException("Invalid encoding for CertificateValidity");
102         }
103     }
104 
105     /**
106      * Default constructor for the class.
107      */
CertificateValidity()108     public CertificateValidity() { }
109 
110     /**
111      * The default constructor for this class for the specified interval.
112      *
113      * @param notBefore the date and time before which the certificate
114      *                   is not valid.
115      * @param notAfter the date and time after which the certificate is
116      *                  not valid.
117      */
CertificateValidity(Date notBefore, Date notAfter)118     public CertificateValidity(Date notBefore, Date notAfter) {
119         this.notBefore = notBefore;
120         this.notAfter = notAfter;
121     }
122 
123     /**
124      * Create the object, decoding the values from the passed DER stream.
125      *
126      * @param in the DerInputStream to read the CertificateValidity from.
127      * @exception IOException on decoding errors.
128      */
CertificateValidity(DerInputStream in)129     public CertificateValidity(DerInputStream in) throws IOException {
130         DerValue derVal = in.getDerValue();
131         construct(derVal);
132     }
133 
134     /**
135      * Return the validity period as user readable string.
136      */
toString()137     public String toString() {
138         if (notBefore == null || notAfter == null)
139             return "";
140         return "Validity: [From: " + notBefore +
141                ",\n               To: " + notAfter + ']';
142     }
143 
144     /**
145      * Encode the CertificateValidity period in DER form to the stream.
146      *
147      * @param out the OutputStream to marshal the contents to.
148      * @exception IOException on errors.
149      */
encode(OutputStream out)150     public void encode(OutputStream out) throws IOException {
151 
152         // in cases where default constructor is used check for
153         // null values
154         if (notBefore == null || notAfter == null) {
155             throw new IOException("CertAttrSet:CertificateValidity:" +
156                                   " null values to encode.\n");
157         }
158         DerOutputStream pair = new DerOutputStream();
159 
160         if (notBefore.getTime() < YR_2050) {
161             pair.putUTCTime(notBefore);
162         } else
163             pair.putGeneralizedTime(notBefore);
164 
165         if (notAfter.getTime() < YR_2050) {
166             pair.putUTCTime(notAfter);
167         } else {
168             pair.putGeneralizedTime(notAfter);
169         }
170         DerOutputStream seq = new DerOutputStream();
171         seq.write(DerValue.tag_Sequence, pair);
172 
173         out.write(seq.toByteArray());
174     }
175 
176     /**
177      * Set the attribute value.
178      */
set(String name, Object obj)179     public void set(String name, Object obj) throws IOException {
180         if (!(obj instanceof Date)) {
181             throw new IOException("Attribute must be of type Date.");
182         }
183         if (name.equalsIgnoreCase(NOT_BEFORE)) {
184             notBefore = (Date)obj;
185         } else if (name.equalsIgnoreCase(NOT_AFTER)) {
186             notAfter = (Date)obj;
187         } else {
188             throw new IOException("Attribute name not recognized by " +
189                             "CertAttrSet: CertificateValidity.");
190         }
191     }
192 
193     /**
194      * Get the attribute value.
195      */
get(String name)196     public Date get(String name) throws IOException {
197         if (name.equalsIgnoreCase(NOT_BEFORE)) {
198             return (getNotBefore());
199         } else if (name.equalsIgnoreCase(NOT_AFTER)) {
200             return (getNotAfter());
201         } else {
202             throw new IOException("Attribute name not recognized by " +
203                             "CertAttrSet: CertificateValidity.");
204         }
205     }
206 
207     /**
208      * Delete the attribute value.
209      */
delete(String name)210     public void delete(String name) throws IOException {
211         if (name.equalsIgnoreCase(NOT_BEFORE)) {
212             notBefore = null;
213         } else if (name.equalsIgnoreCase(NOT_AFTER)) {
214             notAfter = null;
215         } else {
216             throw new IOException("Attribute name not recognized by " +
217                             "CertAttrSet: CertificateValidity.");
218         }
219     }
220 
221     /**
222      * Return an enumeration of names of attributes existing within this
223      * attribute.
224      */
getElements()225     public Enumeration<String> getElements() {
226         AttributeNameEnumeration elements = new AttributeNameEnumeration();
227         elements.addElement(NOT_BEFORE);
228         elements.addElement(NOT_AFTER);
229 
230         return (elements.elements());
231     }
232 
233     /**
234      * Return the name of this attribute.
235      */
getName()236     public String getName() {
237         return (NAME);
238     }
239 
240     /**
241      * Verify that the current time is within the validity period.
242      *
243      * @exception CertificateExpiredException if the certificate has expired.
244      * @exception CertificateNotYetValidException if the certificate is not
245      * yet valid.
246      */
valid()247     public void valid()
248     throws CertificateNotYetValidException, CertificateExpiredException {
249         Date now = new Date();
250         valid(now);
251     }
252 
253     /**
254      * Verify that the passed time is within the validity period.
255      * @param now the Date against which to compare the validity
256      * period.
257      *
258      * @exception CertificateExpiredException if the certificate has expired
259      * with respect to the <code>Date</code> supplied.
260      * @exception CertificateNotYetValidException if the certificate is not
261      * yet valid with respect to the <code>Date</code> supplied.
262      *
263      */
valid(Date now)264     public void valid(Date now)
265     throws CertificateNotYetValidException, CertificateExpiredException {
266         /*
267          * we use the internal Dates rather than the passed in Date
268          * because someone could override the Date methods after()
269          * and before() to do something entirely different.
270          */
271         if (notBefore.after(now)) {
272             throw new CertificateNotYetValidException("NotBefore: " +
273                                                       notBefore.toString());
274         }
275         if (notAfter.before(now)) {
276             throw new CertificateExpiredException("NotAfter: " +
277                                                   notAfter.toString());
278         }
279     }
280 }
281