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