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