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