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        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 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     */
204    public void valid()
205    throws CertificateNotYetValidException, CertificateExpiredException {
206        Date now = new Date();
207        valid(now);
208    }
209
210    /**
211     * Verify that 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     */
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     */
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     */
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     */
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     */
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     */
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     */
320    public String getName() {
321      return(NAME);
322    }
323}
324