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.
  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 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 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 }