Home | History | Annotate | Download | only in x509
      1 /*
      2  * Copyright (c) 1997, 2012, 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.lang.reflect.Constructor;
     31 import java.lang.reflect.InvocationTargetException;
     32 import java.security.cert.CRLException;
     33 import java.security.cert.CertificateException;
     34 import java.util.Collection;
     35 import java.util.Collections;
     36 import java.util.Enumeration;
     37 import java.util.Map;
     38 import java.util.TreeMap;
     39 
     40 import sun.security.util.*;
     41 
     42 /**
     43  * This class defines the CRL Extensions.
     44  * It is used for both CRL Extensions and CRL Entry Extensions,
     45  * which are defined are follows:
     46  * <pre>
     47  * TBSCertList  ::=  SEQUENCE  {
     48  *    version              Version OPTIONAL,   -- if present, must be v2
     49  *    signature            AlgorithmIdentifier,
     50  *    issuer               Name,
     51  *    thisUpdate           Time,
     52  *    nextUpdate           Time  OPTIONAL,
     53  *    revokedCertificates  SEQUENCE OF SEQUENCE  {
     54  *        userCertificate         CertificateSerialNumber,
     55  *        revocationDate          Time,
     56  *        crlEntryExtensions      Extensions OPTIONAL  -- if present, must be v2
     57  *    }  OPTIONAL,
     58  *    crlExtensions        [0] EXPLICIT Extensions OPTIONAL  -- if present, must be v2
     59  * }
     60  * </pre>
     61  *
     62  * @author Hemma Prafullchandra
     63  */
     64 public class CRLExtensions {
     65 
     66     private Map<String,Extension> map = Collections.synchronizedMap(
     67             new TreeMap<String,Extension>());
     68     private boolean unsupportedCritExt = false;
     69 
     70     /**
     71      * Default constructor.
     72      */
     73     public CRLExtensions() { }
     74 
     75     /**
     76      * Create the object, decoding the values from the passed DER stream.
     77      *
     78      * @param in the DerInputStream to read the Extension from, i.e. the
     79      *        sequence of extensions.
     80      * @exception CRLException on decoding errors.
     81      */
     82     public CRLExtensions(DerInputStream in) throws CRLException {
     83         init(in);
     84     }
     85 
     86     // helper routine
     87     private void init(DerInputStream derStrm) throws CRLException {
     88         try {
     89             DerInputStream str = derStrm;
     90 
     91             byte nextByte = (byte)derStrm.peekByte();
     92             // check for context specific byte 0; skip it
     93             if (((nextByte & 0x0c0) == 0x080) &&
     94                 ((nextByte & 0x01f) == 0x000)) {
     95                 DerValue val = str.getDerValue();
     96                 str = val.data;
     97             }
     98 
     99             DerValue[] exts = str.getSequence(5);
    100             for (int i = 0; i < exts.length; i++) {
    101                 Extension ext = new Extension(exts[i]);
    102                 parseExtension(ext);
    103             }
    104         } catch (IOException e) {
    105             throw new CRLException("Parsing error: " + e.toString());
    106         }
    107     }
    108 
    109     private static final Class[] PARAMS = {Boolean.class, Object.class};
    110 
    111     // Parse the encoded extension
    112     private void parseExtension(Extension ext) throws CRLException {
    113         try {
    114             Class<?> extClass = OIDMap.getClass(ext.getExtensionId());
    115             if (extClass == null) {   // Unsupported extension
    116                 if (ext.isCritical())
    117                     unsupportedCritExt = true;
    118                 if (map.put(ext.getExtensionId().toString(), ext) != null)
    119                     throw new CRLException("Duplicate extensions not allowed");
    120                 return;
    121             }
    122             Constructor<?> cons = extClass.getConstructor(PARAMS);
    123             Object[] passed = new Object[] {Boolean.valueOf(ext.isCritical()),
    124                                             ext.getExtensionValue()};
    125             CertAttrSet<?> crlExt = (CertAttrSet<?>)cons.newInstance(passed);
    126             if (map.put(crlExt.getName(), (Extension)crlExt) != null) {
    127                 throw new CRLException("Duplicate extensions not allowed");
    128             }
    129         } catch (InvocationTargetException invk) {
    130             throw new CRLException(invk.getTargetException().getMessage());
    131         } catch (Exception e) {
    132             throw new CRLException(e.toString());
    133         }
    134     }
    135 
    136     /**
    137      * Encode the extensions in DER form to the stream.
    138      *
    139      * @param out the DerOutputStream to marshal the contents to.
    140      * @param isExplicit the tag indicating whether this is an entry
    141      * extension (false) or a CRL extension (true).
    142      * @exception CRLException on encoding errors.
    143      */
    144     public void encode(OutputStream out, boolean isExplicit)
    145     throws CRLException {
    146         try {
    147             DerOutputStream extOut = new DerOutputStream();
    148             Collection<Extension> allExts = map.values();
    149             Object[] objs = allExts.toArray();
    150 
    151             for (int i = 0; i < objs.length; i++) {
    152                 if (objs[i] instanceof CertAttrSet)
    153                     ((CertAttrSet)objs[i]).encode(extOut);
    154                 else if (objs[i] instanceof Extension)
    155                     ((Extension)objs[i]).encode(extOut);
    156                 else
    157                     throw new CRLException("Illegal extension object");
    158             }
    159 
    160             DerOutputStream seq = new DerOutputStream();
    161             seq.write(DerValue.tag_Sequence, extOut);
    162 
    163             DerOutputStream tmp = new DerOutputStream();
    164             if (isExplicit)
    165                 tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT,
    166                                              true, (byte)0), seq);
    167             else
    168                 tmp = seq;
    169 
    170             out.write(tmp.toByteArray());
    171         } catch (IOException e) {
    172             throw new CRLException("Encoding error: " + e.toString());
    173         } catch (CertificateException e) {
    174             throw new CRLException("Encoding error: " + e.toString());
    175         }
    176     }
    177 
    178     /**
    179      * Get the extension with this alias.
    180      *
    181      * @param alias the identifier string for the extension to retrieve.
    182      */
    183     public Extension get(String alias) {
    184         X509AttributeName attr = new X509AttributeName(alias);
    185         String name;
    186         String id = attr.getPrefix();
    187         if (id.equalsIgnoreCase(X509CertImpl.NAME)) { // fully qualified
    188             int index = alias.lastIndexOf(".");
    189             name = alias.substring(index + 1);
    190         } else
    191             name = alias;
    192         return map.get(name);
    193     }
    194 
    195     /**
    196      * Set the extension value with this alias.
    197      *
    198      * @param alias the identifier string for the extension to set.
    199      * @param obj the Object to set the extension identified by the
    200      *        alias.
    201      */
    202     public void set(String alias, Object obj) {
    203         map.put(alias, (Extension)obj);
    204     }
    205 
    206     /**
    207      * Delete the extension value with this alias.
    208      *
    209      * @param alias the identifier string for the extension to delete.
    210      */
    211     public void delete(String alias) {
    212         map.remove(alias);
    213     }
    214 
    215     /**
    216      * Return an enumeration of the extensions.
    217      * @return an enumeration of the extensions in this CRL.
    218      */
    219     public Enumeration<Extension> getElements() {
    220         return Collections.enumeration(map.values());
    221     }
    222 
    223     /**
    224      * Return a collection view of the extensions.
    225      * @return a collection view of the extensions in this CRL.
    226      */
    227     public Collection<Extension> getAllExtensions() {
    228         return map.values();
    229     }
    230 
    231     /**
    232      * Return true if a critical extension is found that is
    233      * not supported, otherwise return false.
    234      */
    235     public boolean hasUnsupportedCriticalExtension() {
    236         return unsupportedCritExt;
    237     }
    238 
    239     /**
    240      * Compares this CRLExtensions for equality with the specified
    241      * object. If the <code>other</code> object is an
    242      * <code>instanceof</code> <code>CRLExtensions</code>, then
    243      * all the entries are compared with the entries from this.
    244      *
    245      * @param other the object to test for equality with this CRLExtensions.
    246      * @return true iff all the entries match that of the Other,
    247      * false otherwise.
    248      */
    249     public boolean equals(Object other) {
    250         if (this == other)
    251             return true;
    252         if (!(other instanceof CRLExtensions))
    253             return false;
    254         Collection<Extension> otherC =
    255                         ((CRLExtensions)other).getAllExtensions();
    256         Object[] objs = otherC.toArray();
    257 
    258         int len = objs.length;
    259         if (len != map.size())
    260             return false;
    261 
    262         Extension otherExt, thisExt;
    263         String key = null;
    264         for (int i = 0; i < len; i++) {
    265             if (objs[i] instanceof CertAttrSet)
    266                 key = ((CertAttrSet)objs[i]).getName();
    267             otherExt = (Extension)objs[i];
    268             if (key == null)
    269                 key = otherExt.getExtensionId().toString();
    270             thisExt = map.get(key);
    271             if (thisExt == null)
    272                 return false;
    273             if (! thisExt.equals(otherExt))
    274                 return false;
    275         }
    276         return true;
    277     }
    278 
    279     /**
    280      * Returns a hashcode value for this CRLExtensions.
    281      *
    282      * @return the hashcode value.
    283      */
    284     public int hashCode() {
    285         return map.hashCode();
    286     }
    287 
    288     /**
    289      * Returns a string representation of this <tt>CRLExtensions</tt> object
    290      * in the form of a set of entries, enclosed in braces and separated
    291      * by the ASCII characters "<tt>,&nbsp;</tt>" (comma and space).
    292      * <p>Overrides to <tt>toString</tt> method of <tt>Object</tt>.
    293      *
    294      * @return  a string representation of this CRLExtensions.
    295      */
    296     public String toString() {
    297         return map.toString();
    298     }
    299 }
    300