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.OpenSSLSignature;
     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 final class X509CertImpl extends X509Certificate {
     64 
     65     /** @serial */
     66     private static final long serialVersionUID = 2972248729446736154L;
     67 
     68     /** the core object to be wrapped in X509Certificate */
     69     private final Certificate certificate;
     70 
     71     // to speed up access to the info, the following fields
     72     // cache values retrieved from the certificate object,
     73     // initialized using the "single-check idiom".
     74     private final TBSCertificate tbsCert;
     75     private final Extensions extensions;
     76     private volatile long notBefore = -1;
     77     private volatile long notAfter = -1;
     78     private volatile BigInteger serialNumber;
     79     private volatile X500Principal issuer;
     80     private volatile X500Principal subject;
     81     private volatile byte[] tbsCertificate;
     82     private volatile byte[] signature;
     83     private volatile String sigAlgName;
     84     private volatile String sigAlgOID;
     85     private volatile byte[] sigAlgParams;
     86     // indicates whether the signature algorithm parameters are null
     87     private volatile boolean nullSigAlgParams;
     88     private volatile PublicKey publicKey;
     89 
     90     // encoding of the certificate
     91     private volatile byte[] encoding;
     92 
     93     /**
     94      * Constructs the instance on the base of ASN.1 encoded
     95      * form of X.509 certificate provided via stream parameter.
     96      * @param in input stream containing ASN.1 encoded form of certificate.
     97      * @throws CertificateException if some decoding problems occur.
     98      */
     99     public X509CertImpl(InputStream in) throws CertificateException {
    100         try {
    101             // decode the Certificate object
    102             this.certificate = (Certificate) Certificate.ASN1.decode(in);
    103             // cache the values of TBSCertificate and Extensions
    104             this.tbsCert = certificate.getTbsCertificate();
    105             this.extensions = tbsCert.getExtensions();
    106         } catch (IOException e) {
    107             throw new CertificateException(e);
    108         }
    109     }
    110 
    111     /**
    112      * Constructs the instance on the base of existing Certificate object to
    113      * be wrapped.
    114      */
    115     public X509CertImpl(Certificate certificate) {
    116         this.certificate = certificate;
    117         // cache the values of TBSCertificate and Extensions
    118         this.tbsCert = certificate.getTbsCertificate();
    119         this.extensions = tbsCert.getExtensions();
    120     }
    121 
    122     /**
    123      * Constructs the instance on the base of ASN.1 encoded
    124      * form of X.509 certificate provided via array of bytes.
    125      * @param encoding byte array containing ASN.1 encoded form of certificate.
    126      * @throws IOException if some decoding problems occur.
    127      */
    128     public X509CertImpl(byte[] encoding) throws IOException {
    129         this((Certificate) Certificate.ASN1.decode(encoding));
    130     }
    131 
    132     public void checkValidity()
    133             throws CertificateExpiredException, CertificateNotYetValidException {
    134         checkValidity(System.currentTimeMillis());
    135     }
    136 
    137     public void checkValidity(Date date)
    138             throws CertificateExpiredException, CertificateNotYetValidException {
    139         checkValidity(date.getTime());
    140     }
    141 
    142     private void checkValidity(long time)
    143             throws CertificateExpiredException, CertificateNotYetValidException {
    144         if (time < getNotBeforeInternal()) {
    145             throw new CertificateNotYetValidException("current time: " + new Date(time)
    146                 + ", validation time: " + new Date(getNotBeforeInternal()));
    147         }
    148         if (time > getNotAfterInternal()) {
    149             throw new CertificateExpiredException("current time: " + new Date(time)
    150                 + ", expiration time: " + new Date(getNotAfterInternal()));
    151         }
    152     }
    153 
    154     public int getVersion() {
    155         return tbsCert.getVersion() + 1;
    156     }
    157 
    158     public BigInteger getSerialNumber() {
    159         BigInteger result = serialNumber;
    160         if (result == null) {
    161             serialNumber = result = tbsCert.getSerialNumber();
    162         }
    163         return result;
    164     }
    165 
    166     public Principal getIssuerDN() {
    167         return getIssuerX500Principal();
    168     }
    169 
    170     public X500Principal getIssuerX500Principal() {
    171         X500Principal result = issuer;
    172         if (result == null) {
    173             // retrieve the issuer's principal
    174             issuer = result = tbsCert.getIssuer().getX500Principal();
    175         }
    176         return result;
    177     }
    178 
    179     public Principal getSubjectDN() {
    180         return getSubjectX500Principal();
    181     }
    182 
    183     public X500Principal getSubjectX500Principal() {
    184         X500Principal result = subject;
    185         if (result == null) {
    186             // retrieve the subject's principal
    187             subject = result = tbsCert.getSubject().getX500Principal();
    188         }
    189         return result;
    190     }
    191 
    192     public Date getNotBefore() {
    193         return new Date(getNotBeforeInternal());
    194     }
    195 
    196     private long getNotBeforeInternal() {
    197         long result = notBefore;
    198         if (result == -1) {
    199             notBefore = result = tbsCert.getValidity().getNotBefore().getTime();
    200         }
    201         return result;
    202     }
    203 
    204     public Date getNotAfter() {
    205         return new Date(getNotAfterInternal());
    206     }
    207 
    208     private long getNotAfterInternal() {
    209         long result = notAfter;
    210         if (result == -1) {
    211             notAfter = result = tbsCert.getValidity().getNotAfter().getTime();
    212         }
    213         return result;
    214     }
    215 
    216     public byte[] getTBSCertificate() throws CertificateEncodingException {
    217         return getTbsCertificateInternal().clone();
    218     }
    219 
    220     private byte[] getTbsCertificateInternal() {
    221         byte[] result = tbsCertificate;
    222         if (result == null) {
    223             tbsCertificate = result = tbsCert.getEncoded();
    224         }
    225         return result;
    226     }
    227 
    228     public byte[] getSignature() {
    229         return getSignatureInternal().clone();
    230     }
    231 
    232     private byte[] getSignatureInternal() {
    233         byte[] result = signature;
    234         if (result == null) {
    235             signature = result = certificate.getSignatureValue();
    236         }
    237         return result;
    238     }
    239 
    240     public String getSigAlgName() {
    241         String result = sigAlgName;
    242         if (result == null) {
    243             String sigAlgOIDLocal = getSigAlgOID();
    244             // retrieve the name of the signing algorithm
    245             result = AlgNameMapper.map2AlgName(sigAlgOIDLocal);
    246             if (result == null) {
    247                 // if could not be found, use OID as a name
    248                 result = sigAlgOIDLocal;
    249             }
    250             sigAlgName = result;
    251         }
    252         return result;
    253     }
    254 
    255     public String getSigAlgOID() {
    256         String result = sigAlgOID;
    257         if (result == null) {
    258             // if info was not retrieved (and cached), do it:
    259             sigAlgOID = result = tbsCert.getSignature().getAlgorithm();
    260         }
    261         return result;
    262     }
    263 
    264     public byte[] getSigAlgParams() {
    265         if (nullSigAlgParams) {
    266             return null;
    267         }
    268         byte[] result = sigAlgParams;
    269         if (result == null) {
    270             result = tbsCert.getSignature().getParameters();
    271             if (result == null) {
    272                 nullSigAlgParams = true;
    273                 return null;
    274             }
    275             sigAlgParams = result;
    276         }
    277         return result;
    278     }
    279 
    280     public boolean[] getIssuerUniqueID() {
    281         return tbsCert.getIssuerUniqueID();
    282     }
    283 
    284     public boolean[] getSubjectUniqueID() {
    285         return tbsCert.getSubjectUniqueID();
    286     }
    287 
    288     public boolean[] getKeyUsage() {
    289         if (extensions == null) {
    290             return null;
    291         }
    292         return extensions.valueOfKeyUsage();
    293     }
    294 
    295     public List<String> getExtendedKeyUsage()
    296                                 throws CertificateParsingException {
    297         if (extensions == null) {
    298             return null;
    299         }
    300         try {
    301             return extensions.valueOfExtendedKeyUsage();
    302         } catch (IOException e) {
    303             throw new CertificateParsingException(e);
    304         }
    305     }
    306 
    307     public int getBasicConstraints() {
    308         if (extensions == null) {
    309             return Integer.MAX_VALUE;
    310         }
    311         return extensions.valueOfBasicConstrains();
    312     }
    313 
    314     public Collection<List<?>> getSubjectAlternativeNames() throws CertificateParsingException {
    315         if (extensions == null) {
    316             return null;
    317         }
    318         try {
    319             // Retrieve the extension value from the cached extensions object
    320             // This extension is not checked for correctness during
    321             // certificate generation, so now it can throw exception
    322             return extensions.valueOfSubjectAlternativeName();
    323         } catch (IOException e) {
    324             throw new CertificateParsingException(e);
    325         }
    326     }
    327 
    328     /**
    329      * @see java.security.cert.X509Certificate#getIssuerAlternativeNames()
    330      * method documentation for more information.
    331      */
    332     public Collection<List<?>> getIssuerAlternativeNames() throws CertificateParsingException {
    333         if (extensions == null) {
    334             return null;
    335         }
    336         try {
    337             // Retrieve the extension value from the cached extensions object
    338             // This extension is not checked for correctness during
    339             // certificate generation, so now it can throw exception
    340             return extensions.valueOfIssuerAlternativeName();
    341         } catch (IOException e) {
    342             throw new CertificateParsingException(e);
    343         }
    344     }
    345 
    346     @Override public byte[] getEncoded() throws CertificateEncodingException {
    347         return getEncodedInternal().clone();
    348     }
    349     private byte[] getEncodedInternal() throws CertificateEncodingException {
    350         byte[] result = encoding;
    351         if (encoding == null) {
    352             encoding = result = certificate.getEncoded();
    353         }
    354         return result;
    355     }
    356 
    357     @Override public PublicKey getPublicKey() {
    358         PublicKey result = publicKey;
    359         if (result == null) {
    360             publicKey = result = tbsCert.getSubjectPublicKeyInfo().getPublicKey();
    361         }
    362         return result;
    363     }
    364 
    365     @Override public String toString() {
    366         return certificate.toString();
    367     }
    368 
    369     @Override public void verify(PublicKey key)
    370             throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
    371             NoSuchProviderException, SignatureException {
    372 
    373         Signature signature;
    374         try {
    375             signature = OpenSSLSignature.getInstance(getSigAlgName());
    376         } catch (NoSuchAlgorithmException ignored) {
    377             signature = Signature.getInstance(getSigAlgName());
    378         }
    379         signature.initVerify(key);
    380         // retrieve the encoding of the TBSCertificate structure
    381         byte[] tbsCertificateLocal = getTbsCertificateInternal();
    382         // compute and verify the signature
    383         signature.update(tbsCertificateLocal, 0, tbsCertificateLocal.length);
    384         if (!signature.verify(certificate.getSignatureValue())) {
    385             throw new SignatureException("Signature was not verified");
    386         }
    387     }
    388 
    389     @Override public void verify(PublicKey key, String sigProvider)
    390             throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
    391             NoSuchProviderException, SignatureException {
    392 
    393         Signature signature;
    394         try {
    395             if (sigProvider == null) {
    396                 signature = OpenSSLSignature.getInstance(getSigAlgName());
    397             } else {
    398                 signature = Signature.getInstance(getSigAlgName(), sigProvider);
    399             }
    400         } catch (NoSuchAlgorithmException ignored) {
    401             signature = Signature.getInstance(getSigAlgName(), sigProvider);
    402         }
    403         signature.initVerify(key);
    404         // retrieve the encoding of the TBSCertificate structure
    405         byte[] tbsCertificateLocal = getTbsCertificateInternal();
    406         // compute and verify the signature
    407         signature.update(tbsCertificateLocal, 0, tbsCertificateLocal.length);
    408         if (!signature.verify(certificate.getSignatureValue())) {
    409             throw new SignatureException("Signature was not verified");
    410         }
    411     }
    412 
    413     @Override public Set<String> getNonCriticalExtensionOIDs() {
    414         if (extensions == null) {
    415             return null;
    416         }
    417         // retrieve the info from the cached extensions object
    418         return extensions.getNonCriticalExtensions();
    419     }
    420 
    421     @Override public Set<String> getCriticalExtensionOIDs() {
    422         if (extensions == null) {
    423             return null;
    424         }
    425         // retrieve the info from the cached extensions object
    426         return extensions.getCriticalExtensions();
    427     }
    428 
    429     @Override public byte[] getExtensionValue(String oid) {
    430         if (extensions == null) {
    431             return null;
    432         }
    433         // retrieve the info from the cached extensions object
    434         Extension ext = extensions.getExtensionByOID(oid);
    435         return (ext == null) ? null : ext.getRawExtnValue();
    436     }
    437 
    438     @Override public boolean hasUnsupportedCriticalExtension() {
    439         if (extensions == null) {
    440             return false;
    441         }
    442         // retrieve the info from the cached extensions object
    443         return extensions.hasUnsupportedCritical();
    444     }
    445 
    446 }
    447