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.math.BigInteger;
     27 import java.util.Arrays;
     28 import java.util.Date;
     29 import java.util.Iterator;
     30 import java.util.List;
     31 import javax.security.auth.x500.X500Principal;
     32 import org.apache.harmony.security.asn1.ASN1Explicit;
     33 import org.apache.harmony.security.asn1.ASN1Integer;
     34 import org.apache.harmony.security.asn1.ASN1Sequence;
     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 import org.apache.harmony.security.x501.Name;
     39 
     40 
     41 /**
     42  * The class encapsulates the ASN.1 DER encoding/decoding work
     43  * with TBSCertList structure which is the part of X.509 CRL
     44  * (as specified in RFC 3280 -
     45  *  Internet X.509 Public Key Infrastructure.
     46  *  Certificate and Certificate Revocation List (CRL) Profile.
     47  *  http://www.ietf.org/rfc/rfc3280.txt):
     48  *
     49  * <pre>
     50  *   TBSCertList  ::=  SEQUENCE  {
     51  *        version                 Version OPTIONAL,
     52  *                                     -- if present, MUST be v2
     53  *        signature               AlgorithmIdentifier,
     54  *        issuer                  Name,
     55  *        thisUpdate              Time,
     56  *        nextUpdate              Time OPTIONAL,
     57  *        revokedCertificates     SEQUENCE OF SEQUENCE  {
     58  *             userCertificate         CertificateSerialNumber,
     59  *             revocationDate          Time,
     60  *             crlEntryExtensions      Extensions OPTIONAL
     61  *                                           -- if present, MUST be v2
     62  *                                  }  OPTIONAL,
     63  *        crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
     64  *                                           -- if present, MUST be v2
     65  *   }
     66  * </pre>
     67  */
     68 public class TBSCertList {
     69 
     70     // the value of version field of the structure
     71     private final int version;
     72     // the value of signature field of the structure
     73     private final AlgorithmIdentifier signature;
     74     // the value of issuer field of the structure
     75     private final Name issuer;
     76     // the value of thisUpdate of the structure
     77     private final Date thisUpdate;
     78     // the value of nextUpdate of the structure
     79     private final Date nextUpdate;
     80     // the value of revokedCertificates of the structure
     81     private final List revokedCertificates;
     82     // the value of crlExtensions field of the structure
     83     private final Extensions crlExtensions;
     84     // the ASN.1 encoded form of TBSCertList
     85     private byte[] encoding;
     86 
     87     public static class RevokedCertificate {
     88         private final BigInteger userCertificate;
     89         private final Date revocationDate;
     90         private final Extensions crlEntryExtensions;
     91 
     92         private boolean issuerRetrieved;
     93         private X500Principal issuer;
     94         private byte[] encoding;
     95 
     96         public RevokedCertificate(BigInteger userCertificate,
     97                 Date revocationDate, Extensions crlEntryExtensions) {
     98             this.userCertificate = userCertificate;
     99             this.revocationDate = revocationDate;
    100             this.crlEntryExtensions = crlEntryExtensions;
    101         }
    102 
    103         public Extensions getCrlEntryExtensions() {
    104             return crlEntryExtensions;
    105         }
    106 
    107         public BigInteger getUserCertificate() {
    108             return userCertificate;
    109         }
    110 
    111         public Date getRevocationDate() {
    112             return revocationDate;
    113         }
    114 
    115         /**
    116          * Returns the value of Certificate Issuer Extension, if it is
    117          * presented.
    118          */
    119         public X500Principal getIssuer() {
    120             if (crlEntryExtensions == null) {
    121                 return null;
    122             }
    123             if (!issuerRetrieved) {
    124                 try {
    125                     issuer =
    126                         crlEntryExtensions.valueOfCertificateIssuerExtension();
    127                 } catch (IOException e) {
    128                     e.printStackTrace();
    129                 }
    130                 issuerRetrieved = true;
    131             }
    132             return issuer;
    133         }
    134 
    135         public byte[] getEncoded() {
    136             if (encoding == null) {
    137                 encoding = ASN1.encode(this);
    138             }
    139             return encoding;
    140         }
    141 
    142         public boolean equals(Object rc) {
    143             if (!(rc instanceof RevokedCertificate)) {
    144                 return false;
    145             }
    146             RevokedCertificate rcert = (RevokedCertificate) rc;
    147             return userCertificate.equals(rcert.userCertificate)
    148                 && ((revocationDate.getTime() / 1000)
    149                         == (rcert.revocationDate.getTime() / 1000))
    150                 && ((crlEntryExtensions == null)
    151                     ? rcert.crlEntryExtensions == null
    152                     : crlEntryExtensions.equals(rcert.crlEntryExtensions));
    153         }
    154 
    155         public int hashCode() {
    156         	return userCertificate.hashCode() * 37 + (int)revocationDate.getTime() / 1000
    157         	+ (crlEntryExtensions == null ? 0 : crlEntryExtensions.hashCode());
    158         }
    159 
    160         /**
    161          * Places the string representation of extension value
    162          * into the StringBuffer object.
    163          */
    164         public void dumpValue(StringBuffer buffer, String prefix) {
    165             buffer.append(prefix).append("Certificate Serial Number: ")
    166                 .append(userCertificate).append('\n');
    167             buffer.append(prefix).append("Revocation Date: ")
    168                 .append(revocationDate);
    169             if (crlEntryExtensions != null) {
    170                 buffer.append('\n').append(prefix)
    171                     .append("CRL Entry Extensions: [");
    172                 crlEntryExtensions.dumpValue(buffer, prefix + "  ");
    173                 buffer.append(prefix).append(']');
    174             }
    175         }
    176 
    177         public static final ASN1Sequence ASN1 = new ASN1Sequence(
    178                 new ASN1Type[] {ASN1Integer.getInstance(), Time.ASN1,
    179                 Extensions.ASN1}) {
    180             {
    181                 setOptional(2);
    182             }
    183 
    184             protected Object getDecodedObject(BerInputStream in) {
    185                 Object[] values = (Object[]) in.content;
    186 
    187                 return new RevokedCertificate(
    188                             new BigInteger((byte[]) values[0]),
    189                             (Date) values[1],
    190                             (Extensions) values[2]
    191                         );
    192             }
    193 
    194             protected void getValues(Object object, Object[] values) {
    195                 RevokedCertificate rcert = (RevokedCertificate) object;
    196 
    197                 values[0] = rcert.userCertificate.toByteArray();
    198                 values[1] = rcert.revocationDate;
    199                 values[2] = rcert.crlEntryExtensions;
    200             }
    201         };
    202     }
    203 
    204     /**
    205      * Constructs the instance of TBSCertList without optional fields.
    206      * Take a note, that regarding to the rfc 3280 (p. 49):
    207      * "When CRLs are issued, the CRLs MUST be version 2 CRLs, include the date
    208      * by which the next CRL will be issued in the nextUpdate field (section
    209      * 5.1.2.5), include the CRL number extension (section 5.2.3), and include
    210      * the authority key identifier extension (section 5.2.1). Conforming
    211      * applications that support CRLs are REQUIRED to process both version 1 and
    212      * version 2 complete CRLs that provide revocation information for all
    213      * certificates issued by one CA. Conforming applications are NOT REQUIRED
    214      * to support processing of delta CRLs, indirect CRLs, or CRLs with a scope
    215      * other than all certificates issued by one CA."
    216      * @param   signature:  AlgorithmIdentifier
    217      * @param   issuer: Name
    218      * @param   thisUpdate: Time
    219      */
    220     public TBSCertList(AlgorithmIdentifier signature,
    221             Name issuer, Date thisUpdate) {
    222         this.version = 1;
    223         this.signature = signature;
    224         this.issuer = issuer;
    225         this.thisUpdate = thisUpdate;
    226         this.nextUpdate = null;
    227         this.revokedCertificates = null;
    228         this.crlExtensions = null;
    229     }
    230 
    231     /**
    232      * Constructs the instance of TBSCertList with all optional fields
    233      * @param   version: version of the CRL. Should be 1 or 2.
    234      * Note that if the version of CRL is 1, then nextUpdate,
    235      * crlExtensions fields of CRL and crlEntryExtensions field
    236      * of CRL entry must not be presented in CRL.
    237      * FIXME: do check for it.
    238      * @param   signature:  AlgorithmIdentifier
    239      * @param   issuer: Name
    240      * @param   thisUpdate: Time
    241      * @param   nextUpdate: Time
    242      * @param   revokedCertificates:    List
    243      * @param   crlExtensions:  Extensions
    244      */
    245     public TBSCertList(int version, AlgorithmIdentifier signature,
    246             Name issuer, Date thisUpdate, Date nextUpdate,
    247             List revokedCertificates, Extensions crlExtensions) {
    248         this.version = version;
    249         this.signature = signature;
    250         this.issuer = issuer;
    251         this.thisUpdate = thisUpdate;
    252         this.nextUpdate = nextUpdate;
    253         this.revokedCertificates = revokedCertificates;
    254         this.crlExtensions = crlExtensions;
    255     }
    256 
    257     // Constructs the object with associated ASN.1 encoding
    258     private TBSCertList(int version, AlgorithmIdentifier signature,
    259             Name issuer, Date thisUpdate, Date nextUpdate,
    260             List revokedCertificates, Extensions crlExtensions,
    261             byte[] encoding) {
    262         this.version = version;
    263         this.signature = signature;
    264         this.issuer = issuer;
    265         this.thisUpdate = thisUpdate;
    266         this.nextUpdate = nextUpdate;
    267         this.revokedCertificates = revokedCertificates;
    268         this.crlExtensions = crlExtensions;
    269         this.encoding = encoding;
    270     }
    271 
    272     /**
    273      * Returns the value of version field of the structure.
    274      * @return  version
    275      */
    276     public int getVersion() {
    277         return version;
    278     }
    279 
    280     /**
    281      * Returns the value of signature field of the structure.
    282      * @return  signature
    283      */
    284     public AlgorithmIdentifier getSignature() {
    285         return signature;
    286     }
    287 
    288     /**
    289      * Returns the value of issuer field of the structure.
    290      * @return  issuer
    291      */
    292     public Name getIssuer() {
    293         return issuer;
    294     }
    295 
    296     /**
    297      * Returns the value of thisUpdate field of the structure.
    298      * @return thisUpdate
    299      */
    300     public Date getThisUpdate() {
    301         return thisUpdate;
    302     }
    303 
    304     /**
    305      * Returns the value of nextUpdate field of the structure.
    306      * @return nextUpdate
    307      */
    308     public Date getNextUpdate() {
    309         return nextUpdate;
    310     }
    311 
    312     /**
    313      * Returns the value of revokedCertificates field of the structure.
    314      * @return revokedCertificates
    315      */
    316     public List getRevokedCertificates() {
    317         return revokedCertificates;
    318     }
    319 
    320     /**
    321      * Returns the value of crlExtensions field of the structure.
    322      * @return  extensions
    323      */
    324     public Extensions getCrlExtensions() {
    325         return crlExtensions;
    326     }
    327 
    328     /**
    329      * Returns ASN.1 encoded form of this X.509 TBSCertList value.
    330      * @return a byte array containing ASN.1 encode form.
    331      */
    332     public byte[] getEncoded() {
    333         if (encoding == null) {
    334             encoding = ASN1.encode(this);
    335         }
    336         return encoding;
    337     }
    338 
    339     public boolean equals(Object tbs) {
    340         if (!(tbs instanceof TBSCertList)) {
    341             return false;
    342         }
    343         TBSCertList tbscert = (TBSCertList) tbs;
    344         return (version == tbscert.version)
    345             && (signature.equals(tbscert.signature))
    346             // FIXME use Name.equals when it will be implemented
    347             && (Arrays.equals(issuer.getEncoded(), tbscert.issuer.getEncoded()))
    348             && ((thisUpdate.getTime() / 1000)
    349                     == (tbscert.thisUpdate.getTime() / 1000))
    350             && ((nextUpdate == null)
    351                     ? tbscert.nextUpdate == null
    352                     : ((nextUpdate.getTime() / 1000)
    353                         == (tbscert.nextUpdate.getTime() / 1000)))
    354             && ((((revokedCertificates == null)
    355                             || (tbscert.revokedCertificates == null))
    356                     && (revokedCertificates == tbscert.revokedCertificates))
    357                 || (revokedCertificates.containsAll(tbscert.revokedCertificates)
    358                     && (revokedCertificates.size()
    359                         == tbscert.revokedCertificates.size())))
    360             && ((crlExtensions == null)
    361                     ? tbscert.crlExtensions == null
    362                     : crlExtensions.equals(tbscert.crlExtensions));
    363     }
    364 
    365     public int hashCode() {
    366     	return ((version * 37 + signature.hashCode()) * 37
    367     		+ issuer.getEncoded().hashCode()) * 37
    368     		+ (int)thisUpdate.getTime() / 1000;
    369     }
    370 
    371     /**
    372      * Places the string representation of extension value
    373      * into the StringBuffer object.
    374      */
    375     public void dumpValue(StringBuffer buffer) {
    376         buffer.append("X.509 CRL v").append(version);
    377         buffer.append("\nSignature Algorithm: [");
    378         signature.dumpValue(buffer);
    379         buffer.append(']');
    380         buffer.append("\nIssuer: ").append(issuer.getName(X500Principal.RFC2253));
    381         buffer.append("\n\nThis Update: ").append(thisUpdate);
    382         buffer.append("\nNext Update: ").append(nextUpdate).append('\n');
    383         if (revokedCertificates != null) {
    384             buffer.append("\nRevoked Certificates: ")
    385                 .append(revokedCertificates.size()).append(" [");
    386             int number = 1;
    387             for (Iterator it = revokedCertificates.iterator();it.hasNext();) {
    388                 buffer.append("\n  [").append(number++).append(']');
    389                 ((RevokedCertificate) it.next()).dumpValue(buffer, "  ");
    390                 buffer.append('\n');
    391             }
    392             buffer.append("]\n");
    393         }
    394         if (crlExtensions != null) {
    395             buffer.append("\nCRL Extensions: ")
    396                 .append(crlExtensions.size()).append(" [");
    397             crlExtensions.dumpValue(buffer, "  ");
    398             buffer.append("]\n");
    399         }
    400     }
    401 
    402     /**
    403      * X.509 TBSCertList encoder/decoder.
    404      */
    405     public static final ASN1Sequence ASN1 = new ASN1Sequence(new ASN1Type[] {
    406             ASN1Integer.getInstance(), // version
    407             AlgorithmIdentifier.ASN1,  // signature
    408             Name.ASN1, // issuer
    409             Time.ASN1, // thisUpdate
    410             Time.ASN1, // nextUpdate
    411             new ASN1SequenceOf(RevokedCertificate.ASN1), // revokedCertificates
    412             new ASN1Explicit(0, Extensions.ASN1) // crlExtensions
    413                 }) {
    414         {
    415             setOptional(0);
    416             setOptional(4);
    417             setOptional(5);
    418             setOptional(6);
    419         }
    420 
    421         protected Object getDecodedObject(BerInputStream in)
    422                         throws IOException {
    423             Object[] values = (Object[]) in.content;
    424             return new TBSCertList(
    425                         (values[0] == null)
    426                             ? 1
    427                             : ASN1Integer.toIntValue(values[0])+1,
    428                         (AlgorithmIdentifier) values[1],
    429                         (Name) values[2],
    430                         (Date) values[3],
    431                         (Date) values[4],
    432                         (List) values[5],
    433                         (Extensions) values[6],
    434                         in.getEncoded()
    435                     );
    436         }
    437 
    438         protected void getValues(Object object, Object[] values) {
    439             TBSCertList tbs = (TBSCertList) object;
    440             values[0] = (tbs.version > 1)
    441                 ? ASN1Integer.fromIntValue(tbs.version - 1) : null;
    442             values[1] = tbs.signature;
    443             values[2] = tbs.issuer;
    444             values[3] = tbs.thisUpdate;
    445             values[4] = tbs.nextUpdate;
    446             values[5] = tbs.revokedCertificates;
    447             values[6] = tbs.crlExtensions;
    448         }
    449     };
    450 }
    451 
    452