Home | History | Annotate | Download | only in pkcs
      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.pkcs;
     27 
     28 import java.io.IOException;
     29 import java.io.OutputStream;
     30 import java.util.Hashtable;
     31 import sun.security.util.DerEncoder;
     32 import sun.security.util.DerValue;
     33 import sun.security.util.DerInputStream;
     34 import sun.security.util.DerOutputStream;
     35 import sun.security.util.ObjectIdentifier;
     36 
     37 /**
     38  * A set of attributes of class PKCS9Attribute.
     39  *
     40  * @author Douglas Hoover
     41  */
     42 public class PKCS9Attributes {
     43     /**
     44      * Attributes in this set indexed by OID.
     45      */
     46     private final Hashtable<ObjectIdentifier, PKCS9Attribute> attributes =
     47         new Hashtable<ObjectIdentifier, PKCS9Attribute>(3);
     48 
     49     /**
     50      * The keys of this hashtable are the OIDs of permitted attributes.
     51      */
     52     private final Hashtable<ObjectIdentifier, ObjectIdentifier> permittedAttributes;
     53 
     54     /**
     55      * The DER encoding of this attribute set.  The tag byte must be
     56      * DerValue.tag_SetOf.
     57      */
     58     private final byte[] derEncoding;
     59 
     60     /*
     61      * Contols how attributes, which are not recognized by the PKCS9Attribute
     62      * class, are handled during parsing.
     63      */
     64     private boolean ignoreUnsupportedAttributes = false;
     65 
     66     /**
     67      * Construct a set of PKCS9 Attributes from its
     68      * DER encoding on a DerInputStream, accepting only attributes
     69      * with OIDs on the given
     70      * list.  If the array is null, accept all attributes supported by
     71      * class PKCS9Attribute.
     72      *
     73      * @param permittedAttributes
     74      * Array of attribute OIDs that will be accepted.
     75      * @param in
     76      * the contents of the DER encoding of the attribute set.
     77      *
     78      * @exception IOException
     79      * on i/o error, encoding syntax error, unacceptable or
     80      * unsupported attribute, or duplicate attribute.
     81      *
     82      * @see PKCS9Attribute
     83      */
     84     public PKCS9Attributes(ObjectIdentifier[] permittedAttributes,
     85                            DerInputStream in) throws IOException {
     86         if (permittedAttributes != null) {
     87             this.permittedAttributes =
     88                 new Hashtable<ObjectIdentifier, ObjectIdentifier>(
     89                                                 permittedAttributes.length);
     90 
     91             for (int i = 0; i < permittedAttributes.length; i++)
     92                 this.permittedAttributes.put(permittedAttributes[i],
     93                                              permittedAttributes[i]);
     94         } else {
     95             this.permittedAttributes = null;
     96         }
     97 
     98         // derEncoding initialized in <code>decode()</code>
     99         derEncoding = decode(in);
    100     }
    101 
    102     /**
    103      * Construct a set of PKCS9 Attributes from the contents of its
    104      * DER encoding on a DerInputStream.  Accept all attributes
    105      * supported by class PKCS9Attribute and reject any unsupported
    106      * attributes.
    107      *
    108      * @param in the contents of the DER encoding of the attribute set.
    109      * @exception IOException
    110      * on i/o error, encoding syntax error, or unsupported or
    111      * duplicate attribute.
    112      *
    113      * @see PKCS9Attribute
    114      */
    115     public PKCS9Attributes(DerInputStream in) throws IOException {
    116         this(in, false);
    117     }
    118 
    119     /**
    120      * Construct a set of PKCS9 Attributes from the contents of its
    121      * DER encoding on a DerInputStream.  Accept all attributes
    122      * supported by class PKCS9Attribute and ignore any unsupported
    123      * attributes, if directed.
    124      *
    125      * @param in the contents of the DER encoding of the attribute set.
    126      * @param ignoreUnsupportedAttributes If true then any attributes
    127      * not supported by the PKCS9Attribute class are ignored. Otherwise
    128      * unsupported attributes cause an exception to be thrown.
    129      * @exception IOException
    130      * on i/o error, encoding syntax error, or unsupported or
    131      * duplicate attribute.
    132      *
    133      * @see PKCS9Attribute
    134      */
    135     public PKCS9Attributes(DerInputStream in,
    136         boolean ignoreUnsupportedAttributes) throws IOException {
    137 
    138         this.ignoreUnsupportedAttributes = ignoreUnsupportedAttributes;
    139         // derEncoding initialized in <code>decode()</code>
    140         derEncoding = decode(in);
    141         permittedAttributes = null;
    142     }
    143 
    144     /**
    145      * Construct a set of PKCS9 Attributes from the given array of
    146      * PKCS9 attributes.
    147      * DER encoding on a DerInputStream.  All attributes in
    148      * <code>attribs</code> must be
    149      * supported by class PKCS9Attribute.
    150      *
    151      * @exception IOException
    152      * on i/o error, encoding syntax error, or unsupported or
    153      * duplicate attribute.
    154      *
    155      * @see PKCS9Attribute
    156      */
    157     public PKCS9Attributes(PKCS9Attribute[] attribs)
    158     throws IllegalArgumentException, IOException {
    159         ObjectIdentifier oid;
    160         for (int i=0; i < attribs.length; i++) {
    161             oid = attribs[i].getOID();
    162             if (attributes.containsKey(oid))
    163                 throw new IllegalArgumentException(
    164                           "PKCSAttribute " + attribs[i].getOID() +
    165                           " duplicated while constructing " +
    166                           "PKCS9Attributes.");
    167 
    168             attributes.put(oid, attribs[i]);
    169         }
    170         derEncoding = generateDerEncoding();
    171         permittedAttributes = null;
    172     }
    173 
    174 
    175     /**
    176      * Decode this set of PKCS9 attributes from the contents of its
    177      * DER encoding. Ignores unsupported attributes when directed.
    178      *
    179      * @param in
    180      * the contents of the DER encoding of the attribute set.
    181      *
    182      * @exception IOException
    183      * on i/o error, encoding syntax error, unacceptable or
    184      * unsupported attribute, or duplicate attribute.
    185      */
    186     private byte[] decode(DerInputStream in) throws IOException {
    187 
    188         DerValue val = in.getDerValue();
    189 
    190         // save the DER encoding with its proper tag byte.
    191         byte[] derEncoding = val.toByteArray();
    192         derEncoding[0] = DerValue.tag_SetOf;
    193 
    194         DerInputStream derIn = new DerInputStream(derEncoding);
    195         DerValue[] derVals = derIn.getSet(3,true);
    196 
    197         PKCS9Attribute attrib;
    198         ObjectIdentifier oid;
    199         boolean reuseEncoding = true;
    200 
    201         for (int i=0; i < derVals.length; i++) {
    202 
    203             try {
    204                 attrib = new PKCS9Attribute(derVals[i]);
    205 
    206             } catch (ParsingException e) {
    207                 if (ignoreUnsupportedAttributes) {
    208                     reuseEncoding = false; // cannot reuse supplied DER encoding
    209                     continue; // skip
    210                 } else {
    211                     throw e;
    212                 }
    213             }
    214             oid = attrib.getOID();
    215 
    216             if (attributes.get(oid) != null)
    217                 throw new IOException("Duplicate PKCS9 attribute: " + oid);
    218 
    219             if (permittedAttributes != null &&
    220                 !permittedAttributes.containsKey(oid))
    221                 throw new IOException("Attribute " + oid +
    222                                       " not permitted in this attribute set");
    223 
    224             attributes.put(oid, attrib);
    225         }
    226         return reuseEncoding ? derEncoding : generateDerEncoding();
    227     }
    228 
    229     /**
    230      * Put the DER encoding of this PKCS9 attribute set on an
    231      * DerOutputStream, tagged with the given implicit tag.
    232      *
    233      * @param tag the implicit tag to use in the DER encoding.
    234      * @param out the output stream on which to put the DER encoding.
    235      *
    236      * @exception IOException  on output error.
    237      */
    238     public void encode(byte tag, OutputStream out) throws IOException {
    239         out.write(tag);
    240         out.write(derEncoding, 1, derEncoding.length -1);
    241     }
    242 
    243     private byte[] generateDerEncoding() throws IOException {
    244         DerOutputStream out = new DerOutputStream();
    245         Object[] attribVals = attributes.values().toArray();
    246 
    247         out.putOrderedSetOf(DerValue.tag_SetOf,
    248                             castToDerEncoder(attribVals));
    249         return out.toByteArray();
    250     }
    251 
    252     /**
    253      * Return the DER encoding of this attribute set, tagged with
    254      * DerValue.tag_SetOf.
    255      */
    256     public byte[] getDerEncoding() throws IOException {
    257         return derEncoding.clone();
    258 
    259     }
    260 
    261     /**
    262      * Get an attribute from this set.
    263      */
    264     public PKCS9Attribute getAttribute(ObjectIdentifier oid) {
    265         return attributes.get(oid);
    266     }
    267 
    268     /**
    269      * Get an attribute from this set.
    270      */
    271     public PKCS9Attribute getAttribute(String name) {
    272         return attributes.get(PKCS9Attribute.getOID(name));
    273     }
    274 
    275 
    276     /**
    277      * Get an array of all attributes in this set, in order of OID.
    278      */
    279     public PKCS9Attribute[] getAttributes() {
    280         PKCS9Attribute[] attribs = new PKCS9Attribute[attributes.size()];
    281         ObjectIdentifier oid;
    282 
    283         int j = 0;
    284         for (int i=1; i < PKCS9Attribute.PKCS9_OIDS.length &&
    285                       j < attribs.length; i++) {
    286             attribs[j] = getAttribute(PKCS9Attribute.PKCS9_OIDS[i]);
    287 
    288             if (attribs[j] != null)
    289                 j++;
    290         }
    291         return attribs;
    292     }
    293 
    294     /**
    295      * Get an attribute value by OID.
    296      */
    297     public Object getAttributeValue(ObjectIdentifier oid)
    298     throws IOException {
    299         try {
    300             Object value = getAttribute(oid).getValue();
    301             return value;
    302         } catch (NullPointerException ex) {
    303             throw new IOException("No value found for attribute " + oid);
    304         }
    305 
    306     }
    307 
    308     /**
    309      *  Get an attribute value by type name.
    310      */
    311     public Object getAttributeValue(String name) throws IOException {
    312         ObjectIdentifier oid = PKCS9Attribute.getOID(name);
    313 
    314         if (oid == null)
    315             throw new IOException("Attribute name " + name +
    316                                   " not recognized or not supported.");
    317 
    318         return getAttributeValue(oid);
    319     }
    320 
    321 
    322     /**
    323      * Returns the PKCS9 block in a printable string form.
    324      */
    325     public String toString() {
    326         StringBuffer buf = new StringBuffer(200);
    327         buf.append("PKCS9 Attributes: [\n\t");
    328 
    329         ObjectIdentifier oid;
    330         PKCS9Attribute value;
    331 
    332         boolean first = true;
    333         for (int i = 1; i < PKCS9Attribute.PKCS9_OIDS.length; i++) {
    334             value = getAttribute(PKCS9Attribute.PKCS9_OIDS[i]);
    335 
    336             if (value == null) continue;
    337 
    338             // we have a value; print it
    339             if (first)
    340                 first = false;
    341             else
    342                 buf.append(";\n\t");
    343 
    344             buf.append(value.toString());
    345         }
    346 
    347         buf.append("\n\t] (end PKCS9 Attributes)");
    348 
    349         return buf.toString();
    350     }
    351 
    352     /**
    353      * Cast an object array whose components are
    354      * <code>DerEncoder</code>s to <code>DerEncoder[]</code>.
    355      */
    356     static DerEncoder[] castToDerEncoder(Object[] objs) {
    357 
    358         DerEncoder[] encoders = new DerEncoder[objs.length];
    359 
    360         for (int i=0; i < encoders.length; i++)
    361             encoders[i] = (DerEncoder) objs[i];
    362 
    363         return encoders;
    364     }
    365 }
    366