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.CertificateEncodingException;
     36 import java.security.cert.CertificateException;
     37 import java.security.cert.CertificateExpiredException;
     38 import java.security.cert.CertificateNotYetValidException;
     39 import java.security.cert.CertificateParsingException;
     40 import java.security.cert.X509Certificate;
     41 import java.security.interfaces.RSAPublicKey;
     42 import java.util.Collection;
     43 import java.util.Date;
     44 import java.util.List;
     45 import java.util.Set;
     46 import javax.security.auth.x500.X500Principal;
     47 import org.apache.harmony.security.utils.AlgNameMapper;
     48 import org.apache.harmony.security.x509.Certificate;
     49 import org.apache.harmony.security.x509.Extension;
     50 import org.apache.harmony.security.x509.Extensions;
     51 import org.apache.harmony.security.x509.TBSCertificate;
     52 import org.apache.harmony.xnet.provider.jsse.NativeCrypto;
     53 
     54 /**
     55  * This class is an implementation of X509Certificate. It wraps
     56  * the instance of org.apache.harmony.security.x509.Certificate
     57  * built on the base of provided ASN.1 DER encoded form of
     58  * Certificate structure (as specified in RFC 3280
     59  * http://www.ietf.org/rfc/rfc3280.txt).
     60  * @see org.apache.harmony.security.x509.Certificate
     61  * @see java.security.cert.X509Certificate
     62  */
     63 public class X509CertImpl extends X509Certificate {
     64 
     65     /**
     66      * @serial
     67      */
     68     private static final long serialVersionUID = 2972248729446736154L;
     69 
     70     // the core object to be wrapped in X509Certificate
     71     private final Certificate certificate;
     72 
     73     // to speed up access to the info, the following fields
     74     // cache values retrieved from the certificate object,
     75     // initialized using the "single-check idiom".
     76     private final TBSCertificate tbsCert;
     77     private final Extensions extensions;
     78     private volatile long notBefore = -1;
     79     private volatile long notAfter = -1;
     80     private volatile BigInteger serialNumber;
     81     private volatile X500Principal issuer;
     82     private volatile X500Principal subject;
     83     private volatile byte[] tbsCertificate;
     84     private volatile byte[] signature;
     85     private volatile String sigAlgName;
     86     private volatile String sigAlgOID;
     87     private volatile byte[] sigAlgParams;
     88     // indicates whether the signature algorithm parameters are null
     89     private volatile boolean nullSigAlgParams;
     90     private volatile PublicKey publicKey;
     91 
     92     // encoding of the certificate
     93     private volatile byte[] encoding;
     94 
     95     //
     96     // ---------------------- Constructors -------------------------------
     97     //
     98 
     99     /**
    100      * Constructs the instance on the base of ASN.1 encoded
    101      * form of X.509 certificate provided via stream parameter.
    102      * @param in input stream containing ASN.1 encoded form of certificate.
    103      * @throws CertificateException if some decoding problems occur.
    104      */
    105     public X509CertImpl(InputStream in) throws CertificateException {
    106         try {
    107             // decode the Certificate object
    108             this.certificate = (Certificate) Certificate.ASN1.decode(in);
    109             // cache the values of TBSCertificate and Extensions
    110             this.tbsCert = certificate.getTbsCertificate();
    111             this.extensions = tbsCert.getExtensions();
    112         } catch (IOException e) {
    113             throw new CertificateException(e);
    114         }
    115     }
    116 
    117     /**
    118      * Constructs the instance on the base of existing Certificate object to
    119      * be wrapped.
    120      */
    121     public X509CertImpl(Certificate certificate) {
    122         this.certificate = certificate;
    123         // cache the values of TBSCertificate and Extensions
    124         this.tbsCert = certificate.getTbsCertificate();
    125         this.extensions = tbsCert.getExtensions();
    126     }
    127 
    128     /**
    129      * Constructs the instance on the base of ASN.1 encoded
    130      * form of X.509 certificate provided via array of bytes.
    131      * @param encoding byte array containing ASN.1 encoded form of certificate.
    132      * @throws IOException if some decoding problems occur.
    133      */
    134     public X509CertImpl(byte[] encoding) throws IOException {
    135         this((Certificate) Certificate.ASN1.decode(encoding));
    136     }
    137 
    138     //
    139     // ----------------- Public methods implementations ------------------
    140     //
    141 
    142     /**
    143      * @see java.security.cert.X509Certificate#checkValidity()
    144      * method documentation for more information.
    145      */
    146     public void checkValidity()
    147             throws CertificateExpiredException, CertificateNotYetValidException {
    148         checkValidity(System.currentTimeMillis());
    149     }
    150 
    151     /**
    152      * @see java.security.cert.X509Certificate#checkValidity(Date)
    153      * method documentation for more information.
    154      */
    155     public void checkValidity(Date date)
    156             throws CertificateExpiredException, CertificateNotYetValidException {
    157         checkValidity(date.getTime());
    158     }
    159 
    160     private void checkValidity(long time)
    161             throws CertificateExpiredException, CertificateNotYetValidException {
    162         if (time < getNotBeforeInternal()) {
    163             throw new CertificateNotYetValidException("current time: " + new Date(time)
    164                 + ", validation time: " + new Date(getNotBeforeInternal()));
    165         }
    166         if (time > getNotAfterInternal()) {
    167             throw new CertificateExpiredException("current time: " + new Date(time)
    168                 + ", expiration time: " + new Date(getNotAfterInternal()));
    169         }
    170     }
    171 
    172     /**
    173      * @see java.security.cert.X509Certificate#getVersion()
    174      * method documentation for more information.
    175      */
    176     public int getVersion() {
    177         return tbsCert.getVersion() + 1;
    178     }
    179 
    180     /**
    181      * @see java.security.cert.X509Certificate#getSerialNumber()
    182      * method documentation for more information.
    183      */
    184     public BigInteger getSerialNumber() {
    185         BigInteger result = serialNumber;
    186         if (result == null) {
    187             serialNumber = result = tbsCert.getSerialNumber();
    188         }
    189         return result;
    190     }
    191 
    192     /**
    193      * @see java.security.cert.X509Certificate#getIssuerDN()
    194      * method documentation for more information.
    195      */
    196     public Principal getIssuerDN() {
    197         return getIssuerX500Principal();
    198     }
    199 
    200     /**
    201      * @see java.security.cert.X509Certificate#getIssuerX500Principal()
    202      * method documentation for more information.
    203      */
    204     public X500Principal getIssuerX500Principal() {
    205         X500Principal result = issuer;
    206         if (result == null) {
    207             // retrieve the issuer's principal
    208             issuer = result = tbsCert.getIssuer().getX500Principal();
    209         }
    210         return result;
    211     }
    212 
    213     /**
    214      * @see java.security.cert.X509Certificate#getSubjectDN()
    215      * method documentation for more information.
    216      */
    217     public Principal getSubjectDN() {
    218         return getSubjectX500Principal();
    219     }
    220 
    221     /**
    222      * @see java.security.cert.X509Certificate#getSubjectX500Principal()
    223      * method documentation for more information.
    224      */
    225     public X500Principal getSubjectX500Principal() {
    226         X500Principal result = subject;
    227         if (result == null) {
    228             // retrieve the subject's principal
    229             subject = result = tbsCert.getSubject().getX500Principal();
    230         }
    231         return result;
    232     }
    233 
    234     /**
    235      * @see java.security.cert.X509Certificate#getNotBefore()
    236      * method documentation for more information.
    237      */
    238     public Date getNotBefore() {
    239         return new Date(getNotBeforeInternal());
    240     }
    241     private long getNotBeforeInternal() {
    242         long result = notBefore;
    243         if (result == -1) {
    244             notBefore = result = tbsCert.getValidity().getNotBefore().getTime();
    245         }
    246         return result;
    247     }
    248 
    249     /**
    250      * @see java.security.cert.X509Certificate#getNotAfter()
    251      * method documentation for more information.
    252      */
    253     public Date getNotAfter() {
    254         return new Date(getNotAfterInternal());
    255     }
    256     private long getNotAfterInternal() {
    257         long result = notAfter;
    258         if (result == -1) {
    259             notAfter = result = tbsCert.getValidity().getNotAfter().getTime();
    260         }
    261         return result;
    262     }
    263 
    264     /**
    265      * @see java.security.cert.X509Certificate#getTBSCertificate()
    266      * method documentation for more information.
    267      */
    268     public byte[] getTBSCertificate() throws CertificateEncodingException {
    269         return getTbsCertificateInternal().clone();
    270     }
    271     private byte[] getTbsCertificateInternal() {
    272         byte[] result = tbsCertificate;
    273         if (result == null) {
    274             tbsCertificate = result = tbsCert.getEncoded();
    275         }
    276         return result;
    277     }
    278 
    279     /**
    280      * @see java.security.cert.X509Certificate#getSignature()
    281      * method documentation for more information.
    282      */
    283     public byte[] getSignature() {
    284         return getSignatureInternal().clone();
    285     }
    286     private byte[] getSignatureInternal() {
    287         byte[] result = signature;
    288         if (result == null) {
    289             signature = result = certificate.getSignatureValue();
    290         }
    291         return result;
    292     }
    293 
    294     /**
    295      * @see java.security.cert.X509Certificate#getSigAlgName()
    296      * method documentation for more information.
    297      */
    298     public String getSigAlgName() {
    299         String result = sigAlgName;
    300         if (result == null) {
    301             String sigAlgOIDLocal = getSigAlgOID();
    302             // retrieve the name of the signing algorithm
    303             result = AlgNameMapper.map2AlgName(sigAlgOIDLocal);
    304             if (result == null) {
    305                 // if could not be found, use OID as a name
    306                 result = sigAlgOIDLocal;
    307             }
    308             sigAlgName = result;
    309         }
    310         return result;
    311     }
    312 
    313     /**
    314      * @see java.security.cert.X509Certificate#getSigAlgOID()
    315      * method documentation for more information.
    316      */
    317     public String getSigAlgOID() {
    318         String result = sigAlgOID;
    319         if (result == null) {
    320             // if info was not retrieved (and cached), do it:
    321             sigAlgOID = result = tbsCert.getSignature().getAlgorithm();
    322         }
    323         return result;
    324     }
    325 
    326     /**
    327      * @see java.security.cert.X509Certificate#getSigAlgParams()
    328      * method documentation for more information.
    329      */
    330     public byte[] getSigAlgParams() {
    331         if (nullSigAlgParams) {
    332             return null;
    333         }
    334         byte[] result = sigAlgParams;
    335         if (result == null) {
    336             result = tbsCert.getSignature().getParameters();
    337             if (result == null) {
    338                 nullSigAlgParams = true;
    339                 return null;
    340             }
    341             sigAlgParams = result;
    342         }
    343         return result;
    344     }
    345 
    346     /**
    347      * @see java.security.cert.X509Certificate#getIssuerUniqueID()
    348      * method documentation for more information.
    349      */
    350     public boolean[] getIssuerUniqueID() {
    351         return tbsCert.getIssuerUniqueID();
    352     }
    353 
    354     /**
    355      * @see java.security.cert.X509Certificate#getSubjectUniqueID()
    356      * method documentation for more information.
    357      */
    358     public boolean[] getSubjectUniqueID() {
    359         return tbsCert.getSubjectUniqueID();
    360     }
    361 
    362     /**
    363      * @see java.security.cert.X509Certificate#getKeyUsage()
    364      * method documentation for more information.
    365      */
    366     public boolean[] getKeyUsage() {
    367         if (extensions == null) {
    368             return null;
    369         }
    370         return extensions.valueOfKeyUsage();
    371     }
    372 
    373     /**
    374      * @see java.security.cert.X509Certificate#getExtendedKeyUsage()
    375      * method documentation for more information.
    376      */
    377     public List/*<String>*/ getExtendedKeyUsage()
    378                                 throws CertificateParsingException {
    379         if (extensions == null) {
    380             return null;
    381         }
    382         try {
    383             return extensions.valueOfExtendedKeyUsage();
    384         } catch (IOException e) {
    385             throw new CertificateParsingException(e);
    386         }
    387     }
    388 
    389     /**
    390      * @see java.security.cert.X509Certificate#getBasicConstraints()
    391      * method documentation for more information.
    392      */
    393     public int getBasicConstraints() {
    394         if (extensions == null) {
    395             return Integer.MAX_VALUE;
    396         }
    397         return extensions.valueOfBasicConstrains();
    398     }
    399 
    400     /**
    401      * @see java.security.cert.X509Certificate#getSubjectAlternativeNames()
    402      * method documentation for more information.
    403      */
    404     public Collection/*<List<?>>*/ getSubjectAlternativeNames()
    405                                 throws CertificateParsingException {
    406         if (extensions == null) {
    407             return null;
    408         }
    409         try {
    410             // Retrieve the extension value from the cached extensions object
    411             // This extension is not checked for correctness during
    412             // certificate generation, so now it can throw exception
    413             return extensions.valueOfSubjectAlternativeName();
    414         } catch (IOException e) {
    415             throw new CertificateParsingException(e);
    416         }
    417     }
    418 
    419     /**
    420      * @see java.security.cert.X509Certificate#getIssuerAlternativeNames()
    421      * method documentation for more information.
    422      */
    423     public Collection/*FIXME <List<?>>*/ getIssuerAlternativeNames()
    424                                 throws CertificateParsingException {
    425         if (extensions == null) {
    426             return null;
    427         }
    428         try {
    429             // Retrieve the extension value from the cached extensions object
    430             // This extension is not checked for correctness during
    431             // certificate generation, so now it can throw exception
    432             return extensions.valueOfIssuerAlternativeName();
    433         } catch (IOException e) {
    434             throw new CertificateParsingException(e);
    435         }
    436     }
    437 
    438     //
    439     // ----- java.security.cert.Certificate methods implementations ------
    440     //
    441 
    442     /**
    443      * @see java.security.cert.Certificate#getEncoded()
    444      * method documentation for more information.
    445      */
    446     public byte[] getEncoded() throws CertificateEncodingException {
    447         return getEncodedInternal().clone();
    448     }
    449     private byte[] getEncodedInternal() throws CertificateEncodingException {
    450         byte[] result = encoding;
    451         if (encoding == null) {
    452             encoding = result = certificate.getEncoded();
    453         }
    454         return result;
    455     }
    456 
    457     /**
    458      * @see java.security.cert.Certificate#getPublicKey()
    459      * method documentation for more information.
    460      */
    461     public PublicKey getPublicKey() {
    462         PublicKey result = publicKey;
    463         if (result == null) {
    464             publicKey = result = tbsCert.getSubjectPublicKeyInfo().getPublicKey();
    465         }
    466         return result;
    467     }
    468 
    469     /**
    470      * @see java.security.cert.Certificate#toString()
    471      * method documentation for more information.
    472      */
    473     public String toString() {
    474         return certificate.toString();
    475     }
    476 
    477     /**
    478      * Verifies the signature of the certificate.
    479      * @see java.security.cert.Certificate#verify(PublicKey)
    480      * method documentation for more information.
    481      */
    482     public void verify(PublicKey key)
    483                          throws CertificateException, NoSuchAlgorithmException,
    484                                 InvalidKeyException, NoSuchProviderException,
    485                                 SignatureException {
    486         if (getSigAlgName().endsWith("withRSA")) {
    487             fastVerify(key);
    488             return;
    489         }
    490 
    491         Signature signature = Signature.getInstance(getSigAlgName());
    492         signature.initVerify(key);
    493         // retrieve the encoding of the TBSCertificate structure
    494         byte[] tbsCertificateLocal = getTbsCertificateInternal();
    495         // compute and verify the signature
    496         signature.update(tbsCertificateLocal, 0, tbsCertificateLocal.length);
    497         if (!signature.verify(certificate.getSignatureValue())) {
    498             throw new SignatureException("Signature was not verified");
    499         }
    500     }
    501 
    502     /**
    503      * Verifies the signature of the certificate.
    504      * @see java.security.cert.Certificate#verify(PublicKey,String)
    505      * method documentation for more information.
    506      */
    507     public void verify(PublicKey key, String sigProvider)
    508                          throws CertificateException, NoSuchAlgorithmException,
    509                                 InvalidKeyException, NoSuchProviderException,
    510                                 SignatureException {
    511         if (getSigAlgName().endsWith("withRSA")) {
    512             fastVerify(key);
    513             return;
    514         }
    515 
    516         Signature signature =
    517             Signature.getInstance(getSigAlgName(), sigProvider);
    518         signature.initVerify(key);
    519         // retrieve the encoding of the TBSCertificate structure
    520         byte[] tbsCertificateLocal = getTbsCertificateInternal();
    521         // compute and verify the signature
    522         signature.update(tbsCertificateLocal, 0, tbsCertificateLocal.length);
    523         if (!signature.verify(certificate.getSignatureValue())) {
    524             throw new SignatureException("Signature was not verified");
    525         }
    526     }
    527 
    528     /**
    529      * Implements a faster RSA verification method that delegates to OpenSSL
    530      * native code. In all other aspects it behaves just like the ordinary
    531      * {@link verify} method.
    532      *
    533      * @param key The RSA public key to use
    534      *
    535      * @throws SignatureException If the verification fails.
    536      * @throws InvalidKeyException
    537      */
    538     private void fastVerify(PublicKey key) throws SignatureException,
    539             InvalidKeyException, NoSuchAlgorithmException {
    540         if (!(key instanceof RSAPublicKey)) {
    541             throw new InvalidKeyException("key is not an instance of RSAPublicKey");
    542         }
    543         RSAPublicKey rsaKey = (RSAPublicKey) key;
    544 
    545         String algorithm = getSigAlgName();
    546 
    547         // We don't support MD2 anymore. This needs to also check for aliases
    548         // and OIDs.
    549         if ("MD2withRSA".equalsIgnoreCase(algorithm) ||
    550                 "MD2withRSAEncryption".equalsIgnoreCase(algorithm) ||
    551                 "1.2.840.113549.1.1.2".equalsIgnoreCase(algorithm) ||
    552                 "MD2/RSA".equalsIgnoreCase(algorithm)) {
    553             throw new NoSuchAlgorithmException(algorithm);
    554         }
    555 
    556         int i = algorithm.indexOf("with");
    557         algorithm = algorithm.substring(i + 4) + "-" + algorithm.substring(0, i);
    558 
    559         byte[] tbsCertificateLocal = getTbsCertificateInternal();
    560         byte[] sig = certificate.getSignatureValue();
    561         if (!NativeCrypto.verifySignature(tbsCertificateLocal, sig, algorithm, rsaKey)) {
    562             throw new SignatureException("Signature was not verified");
    563         }
    564     }
    565 
    566     //
    567     // ----- java.security.cert.X509Extension methods implementations ----
    568     //
    569 
    570     /**
    571      * @see java.security.cert.X509Extension#getNonCriticalExtensionOIDs()
    572      * method documentation for more information.
    573      */
    574     public Set getNonCriticalExtensionOIDs() {
    575         if (extensions == null) {
    576             return null;
    577         }
    578         // retrieve the info from the cached extensions object
    579         return extensions.getNonCriticalExtensions();
    580     }
    581 
    582     /**
    583      * @see java.security.cert.X509Extension#getCriticalExtensionOIDs()
    584      * method documentation for more information.
    585      */
    586     public Set getCriticalExtensionOIDs() {
    587         if (extensions == null) {
    588             return null;
    589         }
    590         // retrieve the info from the cached extensions object
    591         return extensions.getCriticalExtensions();
    592     }
    593 
    594     /**
    595      * @see java.security.cert.X509Extension#getExtensionValue(String)
    596      * method documentation for more information.
    597      */
    598     public byte[] getExtensionValue(String oid) {
    599         if (extensions == null) {
    600             return null;
    601         }
    602         // retrieve the info from the cached extensions object
    603         Extension ext = extensions.getExtensionByOID(oid);
    604         return (ext == null) ? null : ext.getRawExtnValue();
    605     }
    606 
    607     /**
    608      * @see java.security.cert.X509Extension#hasUnsupportedCriticalExtension()
    609      * method documentation for more information.
    610      */
    611     public boolean hasUnsupportedCriticalExtension() {
    612         if (extensions == null) {
    613             return false;
    614         }
    615         // retrieve the info from the cached extensions object
    616         return extensions.hasUnsupportedCritical();
    617     }
    618 
    619 }
    620