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
26package sun.security.x509;
27
28import java.io.IOException;
29import java.io.OutputStream;
30import java.security.cert.CertificateException;
31import java.security.cert.CertificateParsingException;
32import java.security.cert.CertificateExpiredException;
33import java.security.cert.CertificateNotYetValidException;
34import java.util.Date;
35import java.util.Enumeration;
36import java.util.Objects;
37
38import 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 */
62public class PrivateKeyUsageExtension extends Extension
63implements 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.
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
309    public String getName() {
310      return(NAME);
311    }
312}
313