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.Arrays;
     27 import org.apache.harmony.security.asn1.ASN1Boolean;
     28 import org.apache.harmony.security.asn1.ASN1OctetString;
     29 import org.apache.harmony.security.asn1.ASN1Oid;
     30 import org.apache.harmony.security.asn1.ASN1Sequence;
     31 import org.apache.harmony.security.asn1.ASN1Type;
     32 import org.apache.harmony.security.asn1.BerInputStream;
     33 import org.apache.harmony.security.asn1.ObjectIdentifier;
     34 import org.apache.harmony.security.utils.Array;
     35 
     36 /**
     37  * The class encapsulates the ASN.1 DER encoding/decoding work
     38  * with the Extension part of X.509 certificate
     39  * (as specified in RFC 3280 -
     40  *  Internet X.509 Public Key Infrastructure.
     41  *  Certificate and Certificate Revocation List (CRL) Profile.
     42  *  http://www.ietf.org/rfc/rfc3280.txt):
     43  *
     44  * <pre>
     45  *  Extension  ::=  SEQUENCE  {
     46  *       extnID      OBJECT IDENTIFIER,
     47  *       critical    BOOLEAN DEFAULT FALSE,
     48  *       extnValue   OCTET STRING
     49  *  }
     50  * </pre>
     51  */
     52 public final class Extension {
     53     // critical constants
     54     public static final boolean CRITICAL = true;
     55     public static final boolean NON_CRITICAL = false;
     56 
     57     // constants: the extension OIDs
     58     // certificate extensions:
     59     static final int[] SUBJ_DIRECTORY_ATTRS = {2, 5, 29, 9};
     60     static final int[] SUBJ_KEY_ID = {2, 5, 29, 14};
     61     static final int[] KEY_USAGE = {2, 5, 29, 15};
     62     static final int[] PRIVATE_KEY_USAGE_PERIOD = {2, 5, 29, 16};
     63     static final int[] SUBJECT_ALT_NAME = {2, 5, 29, 17};
     64     static final int[] ISSUER_ALTERNATIVE_NAME = {2, 5, 29, 18};
     65     static final int[] BASIC_CONSTRAINTS = {2, 5, 29, 19};
     66     static final int[] NAME_CONSTRAINTS = {2, 5, 29, 30};
     67     static final int[] CRL_DISTR_POINTS = {2, 5, 29, 31};
     68     static final int[] CERTIFICATE_POLICIES = {2, 5, 29, 32};
     69     static final int[] POLICY_MAPPINGS = {2, 5, 29, 33};
     70     static final int[] AUTH_KEY_ID = {2, 5, 29, 35};
     71     static final int[] POLICY_CONSTRAINTS = {2, 5, 29, 36};
     72     static final int[] EXTENDED_KEY_USAGE = {2, 5, 29, 37};
     73     static final int[] FRESHEST_CRL = {2, 5, 29, 46};
     74     static final int[] INHIBIT_ANY_POLICY = {2, 5, 29, 54};
     75     static final int[] AUTHORITY_INFO_ACCESS =
     76                                             {1, 3, 6, 1, 5, 5, 7, 1, 1};
     77     static final int[] SUBJECT_INFO_ACCESS =
     78                                             {1, 3, 6, 1, 5, 5, 7, 1, 11};
     79     // crl extensions:
     80     static final int[] ISSUING_DISTR_POINT = {2, 5, 29, 28};
     81     // crl entry extensions:
     82     static final int[] CRL_NUMBER = {2, 5, 29, 20};
     83     static final int[] CERTIFICATE_ISSUER = {2, 5, 29, 29};
     84     static final int[] INVALIDITY_DATE = {2, 5, 29, 24};
     85     static final int[] REASON_CODE = {2, 5, 29, 21};
     86     static final int[] ISSUING_DISTR_POINTS = {2, 5, 29, 28};
     87 
     88     // the value of extnID field of the structure
     89     private final int[] extnID;
     90     private String extnID_str;
     91     // the value of critical field of the structure
     92     private final boolean critical;
     93     // the value of extnValue field of the structure
     94     private final byte[] extnValue;
     95     // the ASN.1 encoded form of Extension
     96     private byte[] encoding;
     97     // the raw (not decoded) value of extnValue field of the structure
     98     private byte[] rawExtnValue;
     99     // the decoded extension value
    100     protected ExtensionValue extnValueObject;
    101     // tells whether extension value has been decoded or not
    102     private volatile boolean valueDecoded = false;
    103 
    104     public Extension(String extnID, boolean critical,
    105             ExtensionValue extnValueObject) {
    106         this.extnID_str = extnID;
    107         this.extnID = ObjectIdentifier.toIntArray(extnID);
    108         this.critical = critical;
    109         this.extnValueObject = extnValueObject;
    110         this.valueDecoded = true;
    111         this.extnValue = extnValueObject.getEncoded();
    112     }
    113 
    114     public Extension(String extnID, boolean critical, byte[] extnValue) {
    115         this.extnID_str = extnID;
    116         this.extnID = ObjectIdentifier.toIntArray(extnID);
    117         this.critical = critical;
    118         this.extnValue = extnValue;
    119     }
    120 
    121     public Extension(int[] extnID, boolean critical, byte[] extnValue) {
    122         this.extnID = extnID;
    123         this.critical = critical;
    124         this.extnValue = extnValue;
    125     }
    126 
    127     public Extension(String extnID, byte[] extnValue) {
    128         this(extnID, NON_CRITICAL, extnValue);
    129     }
    130 
    131     public Extension(int[] extnID, byte[] extnValue) {
    132         this(extnID, NON_CRITICAL, extnValue);
    133     }
    134 
    135     private Extension(int[] extnID, boolean critical, byte[] extnValue,
    136             byte[] rawExtnValue, byte[] encoding,
    137             ExtensionValue decodedExtValue) {
    138         this(extnID, critical, extnValue);
    139         this.rawExtnValue = rawExtnValue;
    140         this.encoding = encoding;
    141         this.extnValueObject = decodedExtValue;
    142         this.valueDecoded = (decodedExtValue != null);
    143     }
    144 
    145     /**
    146      * Returns the value of extnID field of the structure.
    147      */
    148     public String getExtnID() {
    149         if (extnID_str == null) {
    150             extnID_str = ObjectIdentifier.toString(extnID);
    151         }
    152         return extnID_str;
    153     }
    154 
    155     /**
    156      * Returns the value of critical field of the structure.
    157      */
    158     public boolean getCritical() {
    159         return critical;
    160     }
    161 
    162     /**
    163      * Returns the value of extnValue field of the structure.
    164      */
    165     public byte[] getExtnValue() {
    166         return extnValue;
    167     }
    168 
    169     /**
    170      * Returns the raw (undecoded octet string) value of extnValue
    171      * field of the structure.
    172      */
    173     public byte[] getRawExtnValue() {
    174         if (rawExtnValue == null) {
    175             rawExtnValue = ASN1OctetString.getInstance().encode(extnValue);
    176         }
    177         return rawExtnValue;
    178     }
    179 
    180     /**
    181      * Returns ASN.1 encoded form of this X.509 Extension value.
    182      */
    183     public byte[] getEncoded() {
    184         if (encoding == null) {
    185             encoding = Extension.ASN1.encode(this);
    186         }
    187         return encoding;
    188     }
    189 
    190     @Override public boolean equals(Object ext) {
    191         if (!(ext instanceof Extension)) {
    192             return false;
    193         }
    194         Extension extension = (Extension) ext;
    195         return Arrays.equals(extnID, extension.extnID)
    196             && (critical == extension.critical)
    197             && Arrays.equals(extnValue, extension.extnValue);
    198     }
    199 
    200     @Override public int hashCode() {
    201         return (Arrays.hashCode(extnID) * 37 + (critical ? 1 : 0)) * 37 + Arrays.hashCode(extnValue);
    202     }
    203 
    204     public ExtensionValue getDecodedExtensionValue() throws IOException {
    205         if (!valueDecoded) {
    206             decodeExtensionValue();
    207         }
    208         return extnValueObject;
    209     }
    210 
    211     public KeyUsage getKeyUsageValue() {
    212         if (!valueDecoded) {
    213             try {
    214                 decodeExtensionValue();
    215             } catch (IOException ignored) {
    216             }
    217         }
    218         if (extnValueObject instanceof KeyUsage) {
    219             return (KeyUsage) extnValueObject;
    220         } else {
    221             return null;
    222         }
    223     }
    224 
    225     public BasicConstraints getBasicConstraintsValue() {
    226         if (!valueDecoded) {
    227             try {
    228                 decodeExtensionValue();
    229             } catch (IOException ignored) {
    230             }
    231         }
    232         if (extnValueObject instanceof BasicConstraints) {
    233             return (BasicConstraints) extnValueObject;
    234         } else {
    235             return null;
    236         }
    237     }
    238 
    239     private void decodeExtensionValue() throws IOException {
    240         if (valueDecoded) {
    241             return;
    242         }
    243         if (Arrays.equals(extnID, SUBJ_KEY_ID)) {
    244             extnValueObject = SubjectKeyIdentifier.decode(extnValue);
    245         } else if (Arrays.equals(extnID, KEY_USAGE)) {
    246             extnValueObject = new KeyUsage(extnValue);
    247         } else if (Arrays.equals(extnID, SUBJECT_ALT_NAME)) {
    248             extnValueObject = new AlternativeName(
    249                     AlternativeName.SUBJECT, extnValue);
    250         } else if (Arrays.equals(extnID, ISSUER_ALTERNATIVE_NAME)) {
    251             extnValueObject = new AlternativeName(
    252                     AlternativeName.SUBJECT, extnValue);
    253         } else if (Arrays.equals(extnID, BASIC_CONSTRAINTS)) {
    254             extnValueObject = new BasicConstraints(extnValue);
    255         } else if (Arrays.equals(extnID, NAME_CONSTRAINTS)) {
    256             extnValueObject = NameConstraints.decode(extnValue);
    257         } else if (Arrays.equals(extnID, CERTIFICATE_POLICIES)) {
    258             extnValueObject = CertificatePolicies.decode(extnValue);
    259         } else if (Arrays.equals(extnID, AUTH_KEY_ID)) {
    260             extnValueObject = AuthorityKeyIdentifier.decode(extnValue);
    261         } else if (Arrays.equals(extnID, POLICY_CONSTRAINTS)) {
    262             extnValueObject = new PolicyConstraints(extnValue);
    263         } else if (Arrays.equals(extnID, EXTENDED_KEY_USAGE)) {
    264             extnValueObject = new ExtendedKeyUsage(extnValue);
    265         } else if (Arrays.equals(extnID, INHIBIT_ANY_POLICY)) {
    266             extnValueObject = new InhibitAnyPolicy(extnValue);
    267         } else if (Arrays.equals(extnID, CERTIFICATE_ISSUER)) {
    268             extnValueObject = new CertificateIssuer(extnValue);
    269         } else if (Arrays.equals(extnID, CRL_DISTR_POINTS)) {
    270             extnValueObject = CRLDistributionPoints.decode(extnValue);
    271         } else if (Arrays.equals(extnID, CERTIFICATE_ISSUER)) {
    272             extnValueObject = new ReasonCode(extnValue);
    273         } else if (Arrays.equals(extnID, INVALIDITY_DATE)) {
    274             extnValueObject = new InvalidityDate(extnValue);
    275         } else if (Arrays.equals(extnID, REASON_CODE)) {
    276             extnValueObject = new ReasonCode(extnValue);
    277         } else if (Arrays.equals(extnID, CRL_NUMBER)) {
    278             extnValueObject = new CRLNumber(extnValue);
    279         } else if (Arrays.equals(extnID, ISSUING_DISTR_POINTS)) {
    280             extnValueObject = IssuingDistributionPoint.decode(extnValue);
    281         } else if (Arrays.equals(extnID, AUTHORITY_INFO_ACCESS)) {
    282             extnValueObject = InfoAccessSyntax.decode(extnValue);
    283         } else if (Arrays.equals(extnID, SUBJECT_INFO_ACCESS)) {
    284             extnValueObject = InfoAccessSyntax.decode(extnValue);
    285         }
    286         valueDecoded = true;
    287     }
    288 
    289     public void dumpValue(StringBuilder sb, String prefix) {
    290         sb.append("OID: ").append(getExtnID()).append(", Critical: ").append(critical).append('\n');
    291         if (!valueDecoded) {
    292             try {
    293                 decodeExtensionValue();
    294             } catch (IOException ignored) {
    295             }
    296         }
    297         if (extnValueObject != null) {
    298             extnValueObject.dumpValue(sb, prefix);
    299             return;
    300         }
    301         // else: dump unparsed hex representation
    302         sb.append(prefix);
    303         if (Arrays.equals(extnID, SUBJ_DIRECTORY_ATTRS)) {
    304             sb.append("Subject Directory Attributes Extension");
    305         } else if (Arrays.equals(extnID, SUBJ_KEY_ID)) {
    306             sb.append("Subject Key Identifier Extension");
    307         } else if (Arrays.equals(extnID, KEY_USAGE)) {
    308             sb.append("Key Usage Extension");
    309         } else if (Arrays.equals(extnID, PRIVATE_KEY_USAGE_PERIOD)) {
    310             sb.append("Private Key Usage Period Extension");
    311         } else if (Arrays.equals(extnID, SUBJECT_ALT_NAME)) {
    312             sb.append("Subject Alternative Name Extension");
    313         } else if (Arrays.equals(extnID, ISSUER_ALTERNATIVE_NAME)) {
    314             sb.append("Issuer Alternative Name Extension");
    315         } else if (Arrays.equals(extnID, BASIC_CONSTRAINTS)) {
    316             sb.append("Basic Constraints Extension");
    317         } else if (Arrays.equals(extnID, NAME_CONSTRAINTS)) {
    318             sb.append("Name Constraints Extension");
    319         } else if (Arrays.equals(extnID, CRL_DISTR_POINTS)) {
    320             sb.append("CRL Distribution Points Extension");
    321         } else if (Arrays.equals(extnID, CERTIFICATE_POLICIES)) {
    322             sb.append("Certificate Policies Extension");
    323         } else if (Arrays.equals(extnID, POLICY_MAPPINGS)) {
    324             sb.append("Policy Mappings Extension");
    325         } else if (Arrays.equals(extnID, AUTH_KEY_ID)) {
    326             sb.append("Authority Key Identifier Extension");
    327         } else if (Arrays.equals(extnID, POLICY_CONSTRAINTS)) {
    328             sb.append("Policy Constraints Extension");
    329         } else if (Arrays.equals(extnID, EXTENDED_KEY_USAGE)) {
    330             sb.append("Extended Key Usage Extension");
    331         } else if (Arrays.equals(extnID, INHIBIT_ANY_POLICY)) {
    332             sb.append("Inhibit Any-Policy Extension");
    333         } else if (Arrays.equals(extnID, AUTHORITY_INFO_ACCESS)) {
    334             sb.append("Authority Information Access Extension");
    335         } else if (Arrays.equals(extnID, SUBJECT_INFO_ACCESS)) {
    336             sb.append("Subject Information Access Extension");
    337         } else if (Arrays.equals(extnID, INVALIDITY_DATE)) {
    338             sb.append("Invalidity Date Extension");
    339         } else if (Arrays.equals(extnID, CRL_NUMBER)) {
    340             sb.append("CRL Number Extension");
    341         } else if (Arrays.equals(extnID, REASON_CODE)) {
    342             sb.append("Reason Code Extension");
    343         } else {
    344             sb.append("Unknown Extension");
    345         }
    346         sb.append('\n').append(prefix).append("Unparsed Extension Value:\n");
    347         sb.append(Array.toString(extnValue, prefix));
    348     }
    349 
    350 
    351     /**
    352      * X.509 Extension encoder/decoder.
    353      */
    354     public static final ASN1Sequence ASN1 = new ASN1Sequence(new ASN1Type[] {
    355             ASN1Oid.getInstance(),
    356             ASN1Boolean.getInstance(),
    357             new ASN1OctetString() {
    358                 @Override public Object getDecodedObject(BerInputStream in) throws IOException {
    359                     // first - decoded octet string,
    360                     // second - raw encoding of octet string
    361                     return new Object[]
    362                         {super.getDecodedObject(in), in.getEncoded()};
    363                 }
    364             }
    365         }) {
    366         {
    367             setDefault(Boolean.FALSE, 1);
    368         }
    369 
    370         @Override protected Object getDecodedObject(BerInputStream in) throws IOException {
    371             Object[] values = (Object[]) in.content;
    372 
    373             int[] oid = (int[]) values[0];
    374             byte[] extnValue = (byte[]) ((Object[]) values[2])[0];
    375             byte[] rawExtnValue = (byte[]) ((Object[]) values[2])[1];
    376 
    377             ExtensionValue decodedExtValue = null;
    378             // decode Key Usage and Basic Constraints extension values
    379             if (Arrays.equals(oid, KEY_USAGE)) {
    380                 decodedExtValue = new KeyUsage(extnValue);
    381             } else if (Arrays.equals(oid, BASIC_CONSTRAINTS)) {
    382                 decodedExtValue = new BasicConstraints(extnValue);
    383             }
    384 
    385             return new Extension((int[]) values[0], (Boolean) values[1],
    386                     extnValue, rawExtnValue, in.getEncoded(), decodedExtValue);
    387         }
    388 
    389         @Override protected void getValues(Object object, Object[] values) {
    390             Extension ext = (Extension) object;
    391             values[0] = ext.extnID;
    392             values[1] = (ext.critical) ? Boolean.TRUE : Boolean.FALSE;
    393             values[2] = ext.extnValue;
    394         }
    395     };
    396 }
    397