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 Authority Key Identifier Extension.
     36  *
     37  * <p>The authority key identifier extension provides a means of
     38  * identifying the particular public key used to sign a certificate.
     39  * This extension would be used where an issuer has multiple signing
     40  * keys (either due to multiple concurrent key pairs or due to
     41  * changeover).
     42  * <p>
     43  * The ASN.1 syntax for this is:
     44  * <pre>
     45  * AuthorityKeyIdentifier ::= SEQUENCE {
     46  *    keyIdentifier             [0] KeyIdentifier           OPTIONAL,
     47  *    authorityCertIssuer       [1] GeneralNames            OPTIONAL,
     48  *    authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL
     49  * }
     50  * KeyIdentifier ::= OCTET STRING
     51  * </pre>
     52  * @author Amit Kapoor
     53  * @author Hemma Prafullchandra
     54  * @see Extension
     55  * @see CertAttrSet
     56  */
     57 public class AuthorityKeyIdentifierExtension extends Extension
     58 implements CertAttrSet<String> {
     59     /**
     60      * Identifier for this attribute, to be used with the
     61      * get, set, delete methods of Certificate, x509 type.
     62      */
     63     public static final String IDENT =
     64                          "x509.info.extensions.AuthorityKeyIdentifier";
     65     /**
     66      * Attribute names.
     67      */
     68     public static final String NAME = "AuthorityKeyIdentifier";
     69     public static final String KEY_ID = "key_id";
     70     public static final String AUTH_NAME = "auth_name";
     71     public static final String SERIAL_NUMBER = "serial_number";
     72 
     73     // Private data members
     74     private static final byte TAG_ID = 0;
     75     private static final byte TAG_NAMES = 1;
     76     private static final byte TAG_SERIAL_NUM = 2;
     77 
     78     private KeyIdentifier       id = null;
     79     private GeneralNames        names = null;
     80     private SerialNumber        serialNum = null;
     81 
     82     // Encode only the extension value
     83     private void encodeThis() throws IOException {
     84         if (id == null && names == null && serialNum == null) {
     85             this.extensionValue = null;
     86             return;
     87         }
     88         DerOutputStream seq = new DerOutputStream();
     89         DerOutputStream tmp = new DerOutputStream();
     90         if (id != null) {
     91             DerOutputStream tmp1 = new DerOutputStream();
     92             id.encode(tmp1);
     93             tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
     94                               false, TAG_ID), tmp1);
     95         }
     96         try {
     97             if (names != null) {
     98                 DerOutputStream tmp1 = new DerOutputStream();
     99                 names.encode(tmp1);
    100                 tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
    101                                   true, TAG_NAMES), tmp1);
    102             }
    103         } catch (Exception e) {
    104             throw new IOException(e.toString());
    105         }
    106         if (serialNum != null) {
    107             DerOutputStream tmp1 = new DerOutputStream();
    108             serialNum.encode(tmp1);
    109             tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT,
    110                               false, TAG_SERIAL_NUM), tmp1);
    111         }
    112         seq.write(DerValue.tag_Sequence, tmp);
    113         this.extensionValue = seq.toByteArray();
    114     }
    115 
    116     /**
    117      * The default constructor for this extension.  Null parameters make
    118      * the element optional (not present).
    119      *
    120      * @param id the KeyIdentifier associated with this extension.
    121      * @param names the GeneralNames associated with this extension
    122      * @param serialNum the CertificateSerialNumber associated with
    123      *         this extension.
    124      * @exception IOException on error.
    125      */
    126     public AuthorityKeyIdentifierExtension(KeyIdentifier kid, GeneralNames name,
    127                                            SerialNumber sn)
    128     throws IOException {
    129         this.id = kid;
    130         this.names = name;
    131         this.serialNum = sn;
    132 
    133         this.extensionId = PKIXExtensions.AuthorityKey_Id;
    134         this.critical = false;
    135         encodeThis();
    136     }
    137 
    138     /**
    139      * Create the extension from the passed DER encoded value of the same.
    140      *
    141      * @param critical true if the extension is to be treated as critical.
    142      * @param value an array of DER encoded bytes of the actual value.
    143      * @exception ClassCastException if value is not an array of bytes
    144      * @exception IOException on error.
    145      */
    146     public AuthorityKeyIdentifierExtension(Boolean critical, Object value)
    147     throws IOException {
    148         this.extensionId = PKIXExtensions.AuthorityKey_Id;
    149         this.critical = critical.booleanValue();
    150 
    151         this.extensionValue = (byte[]) value;
    152         DerValue val = new DerValue(this.extensionValue);
    153         if (val.tag != DerValue.tag_Sequence) {
    154             throw new IOException("Invalid encoding for " +
    155                                   "AuthorityKeyIdentifierExtension.");
    156         }
    157 
    158         // Note that all the fields in AuthorityKeyIdentifier are defined as
    159         // being OPTIONAL, i.e., there could be an empty SEQUENCE, resulting
    160         // in val.data being null.
    161         while ((val.data != null) && (val.data.available() != 0)) {
    162             DerValue opt = val.data.getDerValue();
    163 
    164             // NB. this is always encoded with the IMPLICIT tag
    165             // The checks only make sense if we assume implicit tagging,
    166             // with explicit tagging the form is always constructed.
    167             if (opt.isContextSpecific(TAG_ID) && !opt.isConstructed()) {
    168                 if (id != null)
    169                     throw new IOException("Duplicate KeyIdentifier in " +
    170                                           "AuthorityKeyIdentifier.");
    171                 opt.resetTag(DerValue.tag_OctetString);
    172                 id = new KeyIdentifier(opt);
    173 
    174             } else if (opt.isContextSpecific(TAG_NAMES) &&
    175                        opt.isConstructed()) {
    176                 if (names != null)
    177                     throw new IOException("Duplicate GeneralNames in " +
    178                                           "AuthorityKeyIdentifier.");
    179                 opt.resetTag(DerValue.tag_Sequence);
    180                 names = new GeneralNames(opt);
    181 
    182             } else if (opt.isContextSpecific(TAG_SERIAL_NUM) &&
    183                        !opt.isConstructed()) {
    184                 if (serialNum != null)
    185                     throw new IOException("Duplicate SerialNumber in " +
    186                                           "AuthorityKeyIdentifier.");
    187                 opt.resetTag(DerValue.tag_Integer);
    188                 serialNum = new SerialNumber(opt);
    189             } else
    190                 throw new IOException("Invalid encoding of " +
    191                                       "AuthorityKeyIdentifierExtension.");
    192         }
    193     }
    194 
    195     /**
    196      * Return the object as a string.
    197      */
    198     public String toString() {
    199         String s = super.toString() + "AuthorityKeyIdentifier [\n";
    200         if (id != null) {
    201             s += id.toString();     // id already has a newline
    202         }
    203         if (names != null) {
    204             s += names.toString() + "\n";
    205         }
    206         if (serialNum != null) {
    207             s += serialNum.toString() + "\n";
    208         }
    209         return (s + "]\n");
    210     }
    211 
    212     /**
    213      * Write the extension to the OutputStream.
    214      *
    215      * @param out the OutputStream to write the extension to.
    216      * @exception IOException on error.
    217      */
    218     public void encode(OutputStream out) throws IOException {
    219         DerOutputStream tmp = new DerOutputStream();
    220         if (this.extensionValue == null) {
    221             extensionId = PKIXExtensions.AuthorityKey_Id;
    222             critical = false;
    223             encodeThis();
    224         }
    225         super.encode(tmp);
    226         out.write(tmp.toByteArray());
    227     }
    228 
    229     /**
    230      * Set the attribute value.
    231      */
    232     public void set(String name, Object obj) throws IOException {
    233         if (name.equalsIgnoreCase(KEY_ID)) {
    234             if (!(obj instanceof KeyIdentifier)) {
    235               throw new IOException("Attribute value should be of " +
    236                                     "type KeyIdentifier.");
    237             }
    238             id = (KeyIdentifier)obj;
    239         } else if (name.equalsIgnoreCase(AUTH_NAME)) {
    240             if (!(obj instanceof GeneralNames)) {
    241               throw new IOException("Attribute value should be of " +
    242                                     "type GeneralNames.");
    243             }
    244             names = (GeneralNames)obj;
    245         } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) {
    246             if (!(obj instanceof SerialNumber)) {
    247               throw new IOException("Attribute value should be of " +
    248                                     "type SerialNumber.");
    249             }
    250             serialNum = (SerialNumber)obj;
    251         } else {
    252           throw new IOException("Attribute name not recognized by " +
    253                         "CertAttrSet:AuthorityKeyIdentifier.");
    254         }
    255         encodeThis();
    256     }
    257 
    258     /**
    259      * Get the attribute value.
    260      */
    261     public Object get(String name) throws IOException {
    262         if (name.equalsIgnoreCase(KEY_ID)) {
    263             return (id);
    264         } else if (name.equalsIgnoreCase(AUTH_NAME)) {
    265             return (names);
    266         } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) {
    267             return (serialNum);
    268         } else {
    269           throw new IOException("Attribute name not recognized by " +
    270                         "CertAttrSet:AuthorityKeyIdentifier.");
    271         }
    272     }
    273 
    274     /**
    275      * Delete the attribute value.
    276      */
    277     public void delete(String name) throws IOException {
    278         if (name.equalsIgnoreCase(KEY_ID)) {
    279             id = null;
    280         } else if (name.equalsIgnoreCase(AUTH_NAME)) {
    281             names = null;
    282         } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) {
    283             serialNum = null;
    284         } else {
    285           throw new IOException("Attribute name not recognized by " +
    286                         "CertAttrSet:AuthorityKeyIdentifier.");
    287         }
    288         encodeThis();
    289     }
    290 
    291     /**
    292      * Return an enumeration of names of attributes existing within this
    293      * attribute.
    294      */
    295     public Enumeration<String> getElements() {
    296         AttributeNameEnumeration elements = new AttributeNameEnumeration();
    297         elements.addElement(KEY_ID);
    298         elements.addElement(AUTH_NAME);
    299         elements.addElement(SERIAL_NUMBER);
    300 
    301         return (elements.elements());
    302     }
    303 
    304     /**
    305      * Return the name of this attribute.
    306      */
    307     public String getName() {
    308         return (NAME);
    309     }
    310 
    311     /**
    312      * Return the encoded key identifier, or null if not specified.
    313      */
    314     public byte[] getEncodedKeyIdentifier() throws IOException {
    315         if (id != null) {
    316             DerOutputStream derOut = new DerOutputStream();
    317             id.encode(derOut);
    318             return derOut.toByteArray();
    319         }
    320         return null;
    321     }
    322 }
    323