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