Home | History | Annotate | Download | only in x509
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 /**
     19 * @author Alexander Y. Kleymenov
     20 * @version $Revision$
     21 */
     22 
     23 package org.apache.harmony.security.x509;
     24 
     25 import java.io.IOException;
     26 import java.util.ArrayList;
     27 import java.util.Arrays;
     28 import java.util.Collection;
     29 import java.util.HashMap;
     30 import java.util.HashSet;
     31 import java.util.List;
     32 import java.util.Set;
     33 import javax.security.auth.x500.X500Principal;
     34 import org.apache.harmony.security.asn1.ASN1SequenceOf;
     35 import org.apache.harmony.security.asn1.ASN1Type;
     36 import org.apache.harmony.security.asn1.BerInputStream;
     37 
     38 /**
     39  * The class encapsulates the ASN.1 DER encoding/decoding work
     40  * with the Extensions part of X.509 certificate
     41  * (as specified in RFC 3280 -
     42  *  Internet X.509 Public Key Infrastructure.
     43  *  Certificate and Certificate Revocation List (CRL) Profile.
     44  *  http://www.ietf.org/rfc/rfc3280.txt):
     45  *
     46  * <pre>
     47  *  Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
     48  * </pre>
     49  */
     50 public final class Extensions {
     51 
     52     // Supported critical extensions oids:
     53     private static List SUPPORTED_CRITICAL = Arrays.asList(
     54             "2.5.29.15", "2.5.29.19", "2.5.29.32", "2.5.29.17",
     55             "2.5.29.30", "2.5.29.36", "2.5.29.37", "2.5.29.54");
     56 
     57     // the values of extensions of the structure
     58     private List<Extension> extensions;
     59     private Set<String> critical;
     60     private Set<String> noncritical;
     61     // the flag showing is there any unsupported critical extension
     62     // in the list of extensions or not.
     63     private boolean hasUnsupported;
     64     // map containing the oid of extensions as a keys and
     65     // Extension objects as values
     66     private HashMap<String, Extension> oidMap;
     67     // the ASN.1 encoded form of Extensions
     68     private byte[] encoding;
     69 
     70     /**
     71      * Constructs an object representing the value of Extensions.
     72      */
     73     public Extensions() {}
     74 
     75     public Extensions(List<Extension> extensions) {
     76         this.extensions = extensions;
     77     }
     78 
     79     public int size() {
     80         return (extensions == null) ? 0 : extensions.size();
     81     }
     82 
     83     /**
     84      * Returns the list of critical extensions.
     85      */
     86     public Set<String> getCriticalExtensions() {
     87         if (critical == null) {
     88             makeOidsLists();
     89         }
     90         return critical;
     91     }
     92 
     93     /**
     94      * Returns the list of critical extensions.
     95      */
     96     public Set<String> getNonCriticalExtensions() {
     97         if (noncritical == null) {
     98             makeOidsLists();
     99         }
    100         return noncritical;
    101     }
    102 
    103     public boolean hasUnsupportedCritical() {
    104         if (critical == null) {
    105             makeOidsLists();
    106         }
    107         return hasUnsupported;
    108     }
    109 
    110     //
    111     // Makes the separated lists with oids of critical
    112     // and non-critical extensions
    113     //
    114     private void makeOidsLists() {
    115         if (extensions == null) {
    116             return;
    117         }
    118         int size = extensions.size();
    119         critical = new HashSet<String>(size);
    120         noncritical = new HashSet<String>(size);
    121         for (Extension extension : extensions) {
    122             String oid = extension.getExtnID();
    123             if (extension.getCritical()) {
    124                 if (!SUPPORTED_CRITICAL.contains(oid)) {
    125                     hasUnsupported = true;
    126                 }
    127                 critical.add(oid);
    128             } else {
    129                 noncritical.add(oid);
    130             }
    131         }
    132     }
    133 
    134     /**
    135      * Returns the values of extensions.
    136      */
    137     public Extension getExtensionByOID(String oid) {
    138         if (extensions == null) {
    139             return null;
    140         }
    141         if (oidMap == null) {
    142             oidMap = new HashMap<String, Extension>();
    143             for (Extension extension : extensions) {
    144                 oidMap.put(extension.getExtnID(), extension);
    145             }
    146         }
    147         return oidMap.get(oid);
    148     }
    149 
    150 
    151     /**
    152      * Returns the value of Key Usage extension (OID == 2.5.29.15).
    153      * The ASN.1 definition of Key Usage Extension is:
    154      *
    155      * <pre>
    156      * id-ce-keyUsage OBJECT IDENTIFIER ::=  { id-ce 15 }
    157      *
    158      * KeyUsage ::= BIT STRING {
    159      *     digitalSignature        (0),
    160      *     nonRepudiation          (1),
    161      *     keyEncipherment         (2),
    162      *     dataEncipherment        (3),
    163      *     keyAgreement            (4),
    164      *     keyCertSign             (5),
    165      *     cRLSign                 (6),
    166      *     encipherOnly            (7),
    167      *     decipherOnly            (8)
    168      * }
    169      * </pre>
    170      * (as specified in RFC 3280)
    171      *
    172      * @return the value of Key Usage Extension if it is in the list,
    173      * and null if there is no such extension or its value can not be decoded
    174      * otherwise. Note, that the length of returned array can be greater
    175      * than 9.
    176      */
    177     public boolean[] valueOfKeyUsage() {
    178         Extension extension = getExtensionByOID("2.5.29.15");
    179         KeyUsage kUsage;
    180         if ((extension == null) || ((kUsage = extension.getKeyUsageValue()) == null)) {
    181             return null;
    182         }
    183         return kUsage.getKeyUsage();
    184     }
    185 
    186     /**
    187      * Returns the value of Extended Key Usage extension (OID == 2.5.29.37).
    188      * The ASN.1 definition of Extended Key Usage Extension is:
    189      *
    190      * <pre>
    191      *  id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 }
    192      *
    193      *  ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
    194      *
    195      *  KeyPurposeId ::= OBJECT IDENTIFIER
    196      * </pre>
    197      * (as specified in RFC 3280)
    198      *
    199      * @return the list with string representations of KeyPurposeId's OIDs
    200      * and null
    201      * @throws IOException if extension was incorrectly encoded.
    202      */
    203     public List<String> valueOfExtendedKeyUsage() throws IOException {
    204         Extension extension = getExtensionByOID("2.5.29.37");
    205         if (extension == null) {
    206             return null;
    207         }
    208         return ((ExtendedKeyUsage) extension.getDecodedExtensionValue()).getExtendedKeyUsage();
    209     }
    210 
    211     /**
    212      * Returns the value of Basic Constraints Extension (OID = 2.5.29.19).
    213      * The ASN.1 definition of Basic Constraints Extension is:
    214      *
    215      * <pre>
    216      *   id-ce-basicConstraints OBJECT IDENTIFIER ::=  { id-ce 19 }
    217      *
    218      *   BasicConstraints ::= SEQUENCE {
    219      *        cA                      BOOLEAN DEFAULT FALSE,
    220      *        pathLenConstraint       INTEGER (0..MAX) OPTIONAL
    221      *   }
    222      * </pre>
    223      * (as specified in RFC 3280)
    224      *
    225      * @return the value of pathLenConstraint field if extension presents,
    226      * and Integer.MAX_VALUE if does not.
    227      */
    228     public int valueOfBasicConstrains() {
    229         Extension extension = getExtensionByOID("2.5.29.19");
    230         BasicConstraints bc;
    231         if ((extension == null) || ((bc = extension.getBasicConstraintsValue()) == null)) {
    232             return Integer.MAX_VALUE;
    233         }
    234         return bc.getPathLenConstraint();
    235     }
    236 
    237     /**
    238      * Returns the value of Subject Alternative Name (OID = 2.5.29.17).
    239      * The ASN.1 definition for Subject Alternative Name is:
    240      *
    241      * <pre>
    242      *  id-ce-subjectAltName OBJECT IDENTIFIER ::=  { id-ce 17 }
    243      *
    244      *  SubjectAltName ::= GeneralNames
    245      * </pre>
    246      * (as specified in RFC 3280)
    247      *
    248      * @return Returns the collection of pairs:
    249      * (Integer (tag), Object (name value)) if extension presents, and
    250      * null if does not.
    251      */
    252     public Collection<List<?>> valueOfSubjectAlternativeName() throws IOException {
    253         Extension extension = getExtensionByOID("2.5.29.17");
    254         if (extension == null) {
    255             return null;
    256         }
    257         return ((GeneralNames) GeneralNames.ASN1.decode(extension.getExtnValue())).getPairsList();
    258     }
    259 
    260     /**
    261      * Returns the value of Issuer Alternative Name Extension (OID = 2.5.29.18).
    262      * The ASN.1 definition for Issuer Alternative Name is:
    263      *
    264      * <pre>
    265      *   id-ce-issuerAltName OBJECT IDENTIFIER ::=  { id-ce 18 }
    266      *
    267      *   IssuerAltName ::= GeneralNames
    268      * </pre>
    269      * (as specified in RFC 3280)
    270      *
    271      * @return Returns the collection of pairs:
    272      * (Integer (tag), Object (name value)) if extension presents, and
    273      * null if does not.
    274      */
    275     public Collection<List<?>> valueOfIssuerAlternativeName() throws IOException {
    276         Extension extension = getExtensionByOID("2.5.29.18");
    277         if (extension == null) {
    278             return null;
    279         }
    280         return ((GeneralNames) GeneralNames.ASN1.decode(extension.getExtnValue())).getPairsList();
    281     }
    282 
    283     /**
    284      * Returns the value of Certificate Issuer Extension (OID = 2.5.29.29).
    285      * It is a CRL entry extension and contains the GeneralNames describing
    286      * the issuer of revoked certificate. Its ASN.1 notation is as follows:
    287      * <pre>
    288      *   id-ce-certificateIssuer   OBJECT IDENTIFIER ::= { id-ce 29 }
    289      *
    290      *   certificateIssuer ::=     GeneralNames
    291      * </pre>
    292      * (as specified in RFC 3280)
    293      *
    294      * @return the value of Certificate Issuer Extension
    295      */
    296     public X500Principal valueOfCertificateIssuerExtension() throws IOException {
    297         Extension extension = getExtensionByOID("2.5.29.29");
    298         if (extension == null) {
    299             return null;
    300         }
    301         return ((CertificateIssuer) extension.getDecodedExtensionValue()).getIssuer();
    302     }
    303 
    304     /**
    305      * Returns ASN.1 encoded form of this X.509 Extensions value.
    306      */
    307     public byte[] getEncoded() {
    308         if (encoding == null) {
    309             encoding = ASN1.encode(this);
    310         }
    311         return encoding;
    312     }
    313 
    314     @Override public boolean equals(Object other) {
    315         if (!(other instanceof Extensions)) {
    316             return false;
    317         }
    318         Extensions that = (Extensions) other;
    319         return (this.extensions == null || this.extensions.isEmpty())
    320                     ? (that.extensions == null || that.extensions.isEmpty())
    321                     : (this.extensions.equals(that.extensions));
    322     }
    323 
    324     @Override public int hashCode() {
    325         int hashCode = 0;
    326         if (extensions != null) {
    327             hashCode = extensions.hashCode();
    328         }
    329         return hashCode;
    330     }
    331 
    332     public void dumpValue(StringBuilder sb, String prefix) {
    333         if (extensions == null) {
    334             return;
    335         }
    336         int num = 1;
    337         for (Extension extension: extensions) {
    338             sb.append('\n').append(prefix).append('[').append(num++).append("]: ");
    339             extension.dumpValue(sb, prefix);
    340         }
    341     }
    342 
    343     /**
    344      * Custom X.509 Extensions decoder.
    345      */
    346     public static final ASN1Type ASN1 = new ASN1SequenceOf(Extension.ASN1) {
    347         @Override public Object getDecodedObject(BerInputStream in) {
    348             return new Extensions((List<Extension>) in.content);
    349         }
    350 
    351         @Override public Collection getValues(Object object) {
    352             Extensions extensions = (Extensions) object;
    353             return (extensions.extensions == null) ? new ArrayList() : extensions.extensions;
    354         }
    355     };
    356 }
    357