Home | History | Annotate | Download | only in x509
      1 /*
      2  * Copyright (c) 1997, 2009, 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.util.Enumeration;
     31 
     32 import sun.security.util.*;
     33 
     34 /**
     35  * This class represents the Basic Constraints Extension.
     36  *
     37  * <p>The basic constraints extension identifies whether the subject of the
     38  * certificate is a CA and how deep a certification path may exist
     39  * through that CA.
     40  *
     41  * <pre>
     42  * The ASN.1 syntax for this extension is:
     43  * BasicConstraints ::= SEQUENCE {
     44  *     cA                BOOLEAN DEFAULT FALSE,
     45  *     pathLenConstraint INTEGER (0..MAX) OPTIONAL
     46  * }
     47  * </pre>
     48  * @author Amit Kapoor
     49  * @author Hemma Prafullchandra
     50  * @see CertAttrSet
     51  * @see Extension
     52  */
     53 public class BasicConstraintsExtension extends Extension
     54 implements CertAttrSet<String> {
     55     /**
     56      * Identifier for this attribute, to be used with the
     57      * get, set, delete methods of Certificate, x509 type.
     58      */
     59     public static final String IDENT = "x509.info.extensions.BasicConstraints";
     60     /**
     61      * Attribute names.
     62      */
     63     public static final String NAME = "BasicConstraints";
     64     public static final String IS_CA = "is_ca";
     65     public static final String PATH_LEN = "path_len";
     66 
     67     // Private data members
     68     private boolean     ca = false;
     69     private int pathLen = -1;
     70 
     71     // Encode this extension value
     72     private void encodeThis() throws IOException {
     73         DerOutputStream out = new DerOutputStream();
     74         DerOutputStream tmp = new DerOutputStream();
     75 
     76         if (ca) {
     77             tmp.putBoolean(ca);
     78             // Only encode pathLen when ca == true
     79             if (pathLen >= 0) {
     80                 tmp.putInteger(pathLen);
     81             }
     82         }
     83         out.write(DerValue.tag_Sequence, tmp);
     84         this.extensionValue = out.toByteArray();
     85     }
     86 
     87     /**
     88      * Default constructor for this object. The extension is marked
     89      * critical if the ca flag is true, false otherwise.
     90      *
     91      * @param ca true, if the subject of the Certificate is a CA.
     92      * @param len specifies the depth of the certification path.
     93      */
     94     public BasicConstraintsExtension(boolean ca, int len) throws IOException {
     95         this(Boolean.valueOf(ca), ca, len);
     96     }
     97 
     98     /**
     99      * Constructor for this object with specified criticality.
    100      *
    101      * @param critical true, if the extension should be marked critical
    102      * @param ca true, if the subject of the Certificate is a CA.
    103      * @param len specifies the depth of the certification path.
    104      */
    105     public BasicConstraintsExtension(Boolean critical, boolean ca, int len)
    106     throws IOException {
    107         this.ca = ca;
    108         this.pathLen = len;
    109         this.extensionId = PKIXExtensions.BasicConstraints_Id;
    110         this.critical = critical.booleanValue();
    111         encodeThis();
    112     }
    113 
    114     /**
    115      * Create the extension from the passed DER encoded value of the same.
    116      *
    117      * @param critical flag indicating if extension is critical or not
    118      * @param value an array containing the DER encoded bytes of the extension.
    119      * @exception ClassCastException if value is not an array of bytes
    120      * @exception IOException on error.
    121      */
    122      public BasicConstraintsExtension(Boolean critical, Object value)
    123          throws IOException
    124     {
    125          this.extensionId = PKIXExtensions.BasicConstraints_Id;
    126          this.critical = critical.booleanValue();
    127 
    128          this.extensionValue = (byte[]) value;
    129          DerValue val = new DerValue(this.extensionValue);
    130          if (val.tag != DerValue.tag_Sequence) {
    131              throw new IOException("Invalid encoding of BasicConstraints");
    132          }
    133 
    134          if (val.data == null || val.data.available() == 0) {
    135              // non-CA cert ("cA" field is FALSE by default), return -1
    136              return;
    137          }
    138          DerValue opt = val.data.getDerValue();
    139          if (opt.tag != DerValue.tag_Boolean) {
    140              // non-CA cert ("cA" field is FALSE by default), return -1
    141              return;
    142          }
    143 
    144          this.ca = opt.getBoolean();
    145          if (val.data.available() == 0) {
    146              // From PKIX profile:
    147              // Where pathLenConstraint does not appear, there is no
    148              // limit to the allowed length of the certification path.
    149              this.pathLen = Integer.MAX_VALUE;
    150              return;
    151          }
    152 
    153          opt = val.data.getDerValue();
    154          if (opt.tag != DerValue.tag_Integer) {
    155              throw new IOException("Invalid encoding of BasicConstraints");
    156          }
    157          this.pathLen = opt.getInteger();
    158          /*
    159           * Activate this check once again after PKIX profiling
    160           * is a standard and this check no longer imposes an
    161           * interoperability barrier.
    162           * if (ca) {
    163           *   if (!this.critical) {
    164           *   throw new IOException("Criticality cannot be false for CA.");
    165           *   }
    166           * }
    167           */
    168      }
    169 
    170      /**
    171       * Return user readable form of extension.
    172       */
    173      public String toString() {
    174          String s = super.toString() + "BasicConstraints:[\n";
    175 
    176          s += ((ca) ? ("  CA:true") : ("  CA:false")) + "\n";
    177          if (pathLen >= 0) {
    178              s += "  PathLen:" + pathLen + "\n";
    179          } else {
    180              s += "  PathLen: undefined\n";
    181          }
    182          return (s + "]\n");
    183      }
    184 
    185      /**
    186       * Encode this extension value to the output stream.
    187       *
    188       * @param out the DerOutputStream to encode the extension to.
    189       */
    190      public void encode(OutputStream out) throws IOException {
    191          DerOutputStream tmp = new DerOutputStream();
    192          if (extensionValue == null) {
    193              this.extensionId = PKIXExtensions.BasicConstraints_Id;
    194              if (ca) {
    195                  critical = true;
    196              } else {
    197                  critical = false;
    198              }
    199              encodeThis();
    200          }
    201          super.encode(tmp);
    202 
    203          out.write(tmp.toByteArray());
    204      }
    205 
    206     /**
    207      * Set the attribute value.
    208      */
    209     public void set(String name, Object obj) throws IOException {
    210         if (name.equalsIgnoreCase(IS_CA)) {
    211             if (!(obj instanceof Boolean)) {
    212               throw new IOException("Attribute value should be of type Boolean.");
    213             }
    214             ca = ((Boolean)obj).booleanValue();
    215         } else if (name.equalsIgnoreCase(PATH_LEN)) {
    216             if (!(obj instanceof Integer)) {
    217               throw new IOException("Attribute value should be of type Integer.");
    218             }
    219             pathLen = ((Integer)obj).intValue();
    220         } else {
    221           throw new IOException("Attribute name not recognized by " +
    222                                 "CertAttrSet:BasicConstraints.");
    223         }
    224         encodeThis();
    225     }
    226 
    227     /**
    228      * Get the attribute value.
    229      */
    230     public Object get(String name) throws IOException {
    231         if (name.equalsIgnoreCase(IS_CA)) {
    232             return (Boolean.valueOf(ca));
    233         } else if (name.equalsIgnoreCase(PATH_LEN)) {
    234             return (Integer.valueOf(pathLen));
    235         } else {
    236           throw new IOException("Attribute name not recognized by " +
    237                                 "CertAttrSet:BasicConstraints.");
    238         }
    239     }
    240 
    241     /**
    242      * Delete the attribute value.
    243      */
    244     public void delete(String name) throws IOException {
    245         if (name.equalsIgnoreCase(IS_CA)) {
    246             ca = false;
    247         } else if (name.equalsIgnoreCase(PATH_LEN)) {
    248             pathLen = -1;
    249         } else {
    250           throw new IOException("Attribute name not recognized by " +
    251                                 "CertAttrSet:BasicConstraints.");
    252         }
    253         encodeThis();
    254     }
    255 
    256     /**
    257      * Return an enumeration of names of attributes existing within this
    258      * attribute.
    259      */
    260     public Enumeration<String> getElements() {
    261         AttributeNameEnumeration elements = new AttributeNameEnumeration();
    262         elements.addElement(IS_CA);
    263         elements.addElement(PATH_LEN);
    264 
    265         return (elements.elements());
    266     }
    267 
    268     /**
    269      * Return the name of this attribute.
    270      */
    271     public String getName() {
    272         return (NAME);
    273     }
    274 }
    275