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