Home | History | Annotate | Download | only in cert
      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.provider.cert;
     24 
     25 import java.io.IOException;
     26 import java.io.InputStream;
     27 import java.math.BigInteger;
     28 import java.security.InvalidKeyException;
     29 import java.security.NoSuchAlgorithmException;
     30 import java.security.NoSuchProviderException;
     31 import java.security.Principal;
     32 import java.security.PublicKey;
     33 import java.security.Signature;
     34 import java.security.SignatureException;
     35 import java.security.cert.CRLException;
     36 import java.security.cert.Certificate;
     37 import java.security.cert.X509CRL;
     38 import java.security.cert.X509CRLEntry;
     39 import java.security.cert.X509Certificate;
     40 import java.util.ArrayList;
     41 import java.util.Date;
     42 import java.util.HashSet;
     43 import java.util.List;
     44 import java.util.Set;
     45 import javax.security.auth.x500.X500Principal;
     46 import org.apache.harmony.security.utils.AlgNameMapper;
     47 import org.apache.harmony.security.x509.CertificateList;
     48 import org.apache.harmony.security.x509.Extension;
     49 import org.apache.harmony.security.x509.Extensions;
     50 import org.apache.harmony.security.x509.TBSCertList;
     51 
     52 /**
     53  * This class is an implementation of X509CRL. It wraps
     54  * the instance of org.apache.harmony.security.x509.CertificateList
     55  * built on the base of provided ASN.1 DER encoded form of
     56  * CertificateList structure (as specified in RFC 3280
     57  * http://www.ietf.org/rfc/rfc3280.txt).
     58  * Implementation supports work with indirect CRLs.
     59  * @see org.apache.harmony.security.x509.CertificateList
     60  * @see java.security.cert.X509CRL
     61  */
     62 public class X509CRLImpl extends X509CRL {
     63 
     64     // the core object to be wrapped in X509CRL
     65     private final CertificateList crl;
     66 
     67     // To speed up access to the info, the following fields
     68     // cache values retrieved from the CertificateList object
     69     private final TBSCertList tbsCertList;
     70     private byte[] tbsCertListEncoding;
     71     private final Extensions extensions;
     72     private X500Principal issuer;
     73     private ArrayList entries;
     74     private int entriesSize;
     75     private byte[] signature;
     76     private String sigAlgOID;
     77     private String sigAlgName;
     78     private byte[] sigAlgParams;
     79 
     80     // encoded form of crl
     81     private byte[] encoding;
     82 
     83     // indicates whether the signature algorithm parameters are null
     84     private boolean nullSigAlgParams;
     85     // indicates whether the crl entries have already been retrieved
     86     // from CertificateList object (crl)
     87     private boolean entriesRetrieved;
     88 
     89     // indicates whether this X.509 CRL is direct or indirect
     90     // (see rfc 3280 http://www.ietf.org/rfc/rfc3280.txt, p 5.)
     91     private boolean isIndirectCRL;
     92     // if crl is indirect, this field holds an info about how
     93     // many of the leading certificates in the list are issued
     94     // by the same issuer as CRL.
     95     private int nonIndirectEntriesSize;
     96 
     97     /**
     98      * Creates X.509 CRL by wrapping of the specified CertificateList object.
     99      */
    100     public X509CRLImpl(CertificateList crl) {
    101         this.crl = crl;
    102         this.tbsCertList = crl.getTbsCertList();
    103         this.extensions = tbsCertList.getCrlExtensions();
    104     }
    105 
    106     /**
    107      * Creates X.509 CRL on the base of ASN.1 DER encoded form of
    108      * the CRL (CertificateList structure described in RFC 3280)
    109      * provided via input stream.
    110      * @throws CRLException if decoding errors occur.
    111      */
    112     public X509CRLImpl(InputStream in) throws CRLException {
    113         try {
    114             // decode CertificateList structure
    115             this.crl = (CertificateList) CertificateList.ASN1.decode(in);
    116             this.tbsCertList = crl.getTbsCertList();
    117             this.extensions = tbsCertList.getCrlExtensions();
    118         } catch (IOException e) {
    119             throw new CRLException(e);
    120         }
    121     }
    122 
    123     /**
    124      * Creates X.509 CRL on the base of ASN.1 DER encoded form of
    125      * the CRL (CertificateList structure described in RFC 3280)
    126      * provided via array of bytes.
    127      * @throws IOException if decoding errors occur.
    128      */
    129     public X509CRLImpl(byte[] encoding) throws IOException {
    130         this((CertificateList) CertificateList.ASN1.decode(encoding));
    131     }
    132 
    133     // ---------------------------------------------------------------------
    134     // ----- java.security.cert.X509CRL abstract method implementations ----
    135     // ---------------------------------------------------------------------
    136 
    137     /**
    138      * @see java.security.cert.X509CRL#getEncoded()
    139      * method documentation for more info
    140      */
    141     public byte[] getEncoded() throws CRLException {
    142         if (encoding == null) {
    143             encoding = crl.getEncoded();
    144         }
    145         byte[] result = new byte[encoding.length];
    146         System.arraycopy(encoding, 0, result, 0, encoding.length);
    147         return result;
    148     }
    149 
    150     /**
    151      * @see java.security.cert.X509CRL#getVersion()
    152      * method documentation for more info
    153      */
    154     public int getVersion() {
    155         return tbsCertList.getVersion();
    156     }
    157 
    158     /**
    159      * @see java.security.cert.X509CRL#getIssuerDN()
    160      * method documentation for more info
    161      */
    162     public Principal getIssuerDN() {
    163         if (issuer == null) {
    164             issuer = tbsCertList.getIssuer().getX500Principal();
    165         }
    166         return issuer;
    167     }
    168 
    169     /**
    170      * @see java.security.cert.X509CRL#getIssuerX500Principal()
    171      * method documentation for more info
    172      */
    173     public X500Principal getIssuerX500Principal() {
    174         if (issuer == null) {
    175             issuer = tbsCertList.getIssuer().getX500Principal();
    176         }
    177         return issuer;
    178     }
    179 
    180     /**
    181      * @see java.security.cert.X509CRL#getThisUpdate()
    182      * method documentation for more info
    183      */
    184     public Date getThisUpdate() {
    185         return tbsCertList.getThisUpdate();
    186     }
    187 
    188     /**
    189      * @see java.security.cert.X509CRL#getNextUpdate()
    190      * method documentation for more info
    191      */
    192     public Date getNextUpdate() {
    193         return tbsCertList.getNextUpdate();
    194     }
    195 
    196     /*
    197      * Retrieves the crl entries (TBSCertList.RevokedCertificate objects)
    198      * from the TBSCertList structure and converts them to the
    199      * X509CRLEntryImpl objects
    200      */
    201     private void retrieveEntries() {
    202         entriesRetrieved = true;
    203         List rcerts = tbsCertList.getRevokedCertificates();
    204         if (rcerts == null) {
    205             return;
    206         }
    207         entriesSize = rcerts.size();
    208         entries = new ArrayList(entriesSize);
    209         // null means that revoked certificate issuer is the same as CRL issuer
    210         X500Principal rcertIssuer = null;
    211         for (int i=0; i<entriesSize; i++) {
    212             TBSCertList.RevokedCertificate rcert =
    213                 (TBSCertList.RevokedCertificate) rcerts.get(i);
    214             X500Principal iss = rcert.getIssuer();
    215             if (iss != null) {
    216                 // certificate issuer differs from CRL issuer
    217                 // and CRL is indirect.
    218                 rcertIssuer = iss;
    219                 isIndirectCRL = true;
    220                 // remember how many leading revoked certificates in the
    221                 // list are issued by the same issuer as issuer of CRL
    222                 // (these certificates are first in the list)
    223                 nonIndirectEntriesSize = i;
    224             }
    225             entries.add(new X509CRLEntryImpl(rcert, rcertIssuer));
    226         }
    227     }
    228 
    229     /**
    230      * Searches for certificate in CRL.
    231      * This method supports indirect CRLs: if CRL is indirect method takes
    232      * into account serial number and issuer of the certificate,
    233      * if CRL issued by CA (i.e. it is not indirect) search is done only
    234      * by serial number of the specified certificate.
    235      * @see java.security.cert.X509CRL#getRevokedCertificate(X509Certificate)
    236      * method documentation for more info
    237      */
    238     public X509CRLEntry getRevokedCertificate(X509Certificate certificate) {
    239         if (certificate == null) {
    240             throw new NullPointerException("certificate == null");
    241         }
    242         if (!entriesRetrieved) {
    243             retrieveEntries();
    244         }
    245         if (entries == null) {
    246             return null;
    247         }
    248         BigInteger serialN = certificate.getSerialNumber();
    249         if (isIndirectCRL) {
    250             // search in indirect crl
    251             X500Principal certIssuer = certificate.getIssuerX500Principal();
    252             if (certIssuer.equals(getIssuerX500Principal())) {
    253                 // certificate issuer is CRL issuer
    254                 certIssuer = null;
    255             }
    256             for (int i=0; i<entriesSize; i++) {
    257                 X509CRLEntry entry = (X509CRLEntry) entries.get(i);
    258                 // check the serial number of revoked certificate
    259                 if (serialN.equals(entry.getSerialNumber())) {
    260                     // revoked certificate issuer
    261                     X500Principal iss = entry.getCertificateIssuer();
    262                     // check the issuer of revoked certificate
    263                     if (certIssuer != null) {
    264                         // certificate issuer is not a CRL issuer, so
    265                         // check issuers for equality
    266                         if (certIssuer.equals(iss)) {
    267                             return entry;
    268                         }
    269                     } else if (iss == null) {
    270                         // both certificates was issued by CRL issuer
    271                         return entry;
    272                     }
    273                 }
    274             }
    275         } else {
    276             // search in CA's (non indirect) crl: just look up the serial number
    277             for (int i=0; i<entriesSize; i++) {
    278                 X509CRLEntry entry = (X509CRLEntry) entries.get(i);
    279                 if (serialN.equals(entry.getSerialNumber())) {
    280                     return entry;
    281                 }
    282             }
    283         }
    284         return null;
    285     }
    286 
    287     /**
    288      * Method searches for CRL entry with specified serial number.
    289      * The method will search only certificate issued by CRL's issuer.
    290      * @see java.security.cert.X509CRL#getRevokedCertificate(BigInteger)
    291      * method documentation for more info
    292      */
    293     public X509CRLEntry getRevokedCertificate(BigInteger serialNumber) {
    294         if (!entriesRetrieved) {
    295             retrieveEntries();
    296         }
    297         if (entries == null) {
    298             return null;
    299         }
    300         for (int i=0; i<nonIndirectEntriesSize; i++) {
    301             X509CRLEntry entry = (X509CRLEntry) entries.get(i);
    302             if (serialNumber.equals(entry.getSerialNumber())) {
    303                 return entry;
    304             }
    305         }
    306         return null;
    307     }
    308 
    309     /**
    310      * @see java.security.cert.X509CRL#getRevokedCertificates()
    311      * method documentation for more info
    312      */
    313     public Set<? extends X509CRLEntry> getRevokedCertificates() {
    314         if (!entriesRetrieved) {
    315             retrieveEntries();
    316         }
    317         if (entries == null) {
    318             return null;
    319         }
    320         return new HashSet(entries);
    321     }
    322 
    323     /**
    324      * @see java.security.cert.X509CRL#getTBSCertList()
    325      * method documentation for more info
    326      */
    327     public byte[] getTBSCertList() throws CRLException {
    328         if (tbsCertListEncoding == null) {
    329             tbsCertListEncoding = tbsCertList.getEncoded();
    330         }
    331         byte[] result = new byte[tbsCertListEncoding.length];
    332         System.arraycopy(tbsCertListEncoding, 0,
    333                 result, 0, tbsCertListEncoding.length);
    334         return result;
    335     }
    336 
    337     /**
    338      * @see java.security.cert.X509CRL#getSignature()
    339      * method documentation for more info
    340      */
    341     public byte[] getSignature() {
    342         if (signature == null) {
    343             signature = crl.getSignatureValue();
    344         }
    345         byte[] result = new byte[signature.length];
    346         System.arraycopy(signature, 0, result, 0, signature.length);
    347         return result;
    348     }
    349 
    350     /**
    351      * @see java.security.cert.X509CRL#getSigAlgName()
    352      * method documentation for more info
    353      */
    354     public String getSigAlgName() {
    355         if (sigAlgOID == null) {
    356             sigAlgOID = tbsCertList.getSignature().getAlgorithm();
    357             sigAlgName = AlgNameMapper.map2AlgName(sigAlgOID);
    358             if (sigAlgName == null) {
    359                 sigAlgName = sigAlgOID;
    360             }
    361         }
    362         return sigAlgName;
    363     }
    364 
    365     /**
    366      * @see java.security.cert.X509CRL#getSigAlgOID()
    367      * method documentation for more info
    368      */
    369     public String getSigAlgOID() {
    370         if (sigAlgOID == null) {
    371             sigAlgOID = tbsCertList.getSignature().getAlgorithm();
    372             sigAlgName = AlgNameMapper.map2AlgName(sigAlgOID);
    373             if (sigAlgName == null) {
    374                 sigAlgName = sigAlgOID;
    375             }
    376         }
    377         return sigAlgOID;
    378     }
    379 
    380     /**
    381      * @see java.security.cert.X509CRL#getSigAlgParams()
    382      * method documentation for more info
    383      */
    384     public byte[] getSigAlgParams() {
    385         if (nullSigAlgParams) {
    386             return null;
    387         }
    388         if (sigAlgParams == null) {
    389             sigAlgParams = tbsCertList.getSignature().getParameters();
    390             if (sigAlgParams == null) {
    391                 nullSigAlgParams = true;
    392                 return null;
    393             }
    394         }
    395         return sigAlgParams;
    396     }
    397 
    398     /**
    399      * @see java.security.cert.X509CRL#verify(PublicKey key)
    400      * method documentation for more info
    401      */
    402     public void verify(PublicKey key)
    403                      throws CRLException, NoSuchAlgorithmException,
    404                             InvalidKeyException, NoSuchProviderException,
    405                             SignatureException {
    406         Signature signature = Signature.getInstance(getSigAlgName());
    407         signature.initVerify(key);
    408         byte[] tbsEncoding = tbsCertList.getEncoded();
    409         signature.update(tbsEncoding, 0, tbsEncoding.length);
    410         if (!signature.verify(crl.getSignatureValue())) {
    411             throw new SignatureException("Signature was not verified");
    412         }
    413     }
    414 
    415     /**
    416      * @see java.security.cert.X509CRL#verify(PublicKey key, String sigProvider)
    417      * method documentation for more info
    418      */
    419     public void verify(PublicKey key, String sigProvider)
    420                      throws CRLException, NoSuchAlgorithmException,
    421                             InvalidKeyException, NoSuchProviderException,
    422                             SignatureException {
    423         Signature signature = Signature.getInstance(
    424                                             getSigAlgName(), sigProvider);
    425         signature.initVerify(key);
    426         byte[] tbsEncoding = tbsCertList.getEncoded();
    427         signature.update(tbsEncoding, 0, tbsEncoding.length);
    428         if (!signature.verify(crl.getSignatureValue())) {
    429             throw new SignatureException("Signature was not verified");
    430         }
    431     }
    432 
    433     // ---------------------------------------------------------------------
    434     // ------ java.security.cert.CRL abstract method implementations -------
    435     // ---------------------------------------------------------------------
    436 
    437     /**
    438      * @see java.security.cert.CRL#isRevoked(Certificate)
    439      * method documentation for more info
    440      */
    441     public boolean isRevoked(Certificate cert) {
    442         if (!(cert instanceof X509Certificate)) {
    443             return false;
    444         }
    445         return getRevokedCertificate((X509Certificate) cert) != null;
    446     }
    447 
    448     /**
    449      * @see java.security.cert.CRL#toString()
    450      * method documentation for more info
    451      */
    452     public String toString() {
    453         return crl.toString();
    454     }
    455 
    456     // ---------------------------------------------------------------------
    457     // ------ java.security.cert.X509Extension method implementations ------
    458     // ---------------------------------------------------------------------
    459 
    460     /**
    461      * @see java.security.cert.X509Extension#getNonCriticalExtensionOIDs()
    462      * method documentation for more info
    463      */
    464     public Set getNonCriticalExtensionOIDs() {
    465         if (extensions == null) {
    466             return null;
    467         }
    468         return extensions.getNonCriticalExtensions();
    469     }
    470 
    471     /**
    472      * @see java.security.cert.X509Extension#getCriticalExtensionOIDs()
    473      * method documentation for more info
    474      */
    475     public Set getCriticalExtensionOIDs() {
    476         if (extensions == null) {
    477             return null;
    478         }
    479         return extensions.getCriticalExtensions();
    480     }
    481 
    482     /**
    483      * @see java.security.cert.X509Extension#getExtensionValue(String)
    484      * method documentation for more info
    485      */
    486     public byte[] getExtensionValue(String oid) {
    487         if (extensions == null) {
    488             return null;
    489         }
    490         Extension ext = extensions.getExtensionByOID(oid);
    491         return (ext == null) ? null : ext.getRawExtnValue();
    492     }
    493 
    494     /**
    495      * @see java.security.cert.X509Extension#hasUnsupportedCriticalExtension()
    496      * method documentation for more info
    497      */
    498     public boolean hasUnsupportedCriticalExtension() {
    499         if (extensions == null) {
    500             return false;
    501         }
    502         return extensions.hasUnsupportedCritical();
    503     }
    504 }
    505