Home | History | Annotate | Download | only in http
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.net.http;
     18 
     19 import com.android.org.conscrypt.SSLParametersImpl;
     20 import com.android.org.conscrypt.TrustManagerImpl;
     21 
     22 import java.io.ByteArrayInputStream;
     23 import java.io.IOException;
     24 import java.security.GeneralSecurityException;
     25 import java.security.KeyManagementException;
     26 import java.security.cert.Certificate;
     27 import java.security.cert.CertificateException;
     28 import java.security.cert.CertificateFactory;
     29 import java.security.cert.X509Certificate;
     30 import javax.net.ssl.DefaultHostnameVerifier;
     31 import javax.net.ssl.SSLHandshakeException;
     32 import javax.net.ssl.SSLSession;
     33 import javax.net.ssl.SSLSocket;
     34 import javax.net.ssl.X509TrustManager;
     35 
     36 /**
     37  * Class responsible for all server certificate validation functionality
     38  *
     39  * {@hide}
     40  */
     41 public class CertificateChainValidator {
     42 
     43     /**
     44      * The singleton instance of the certificate chain validator
     45      */
     46     private static final CertificateChainValidator sInstance
     47             = new CertificateChainValidator();
     48 
     49     private static final DefaultHostnameVerifier sVerifier
     50             = new DefaultHostnameVerifier();
     51 
     52     /**
     53      * @return The singleton instance of the certificates chain validator
     54      */
     55     public static CertificateChainValidator getInstance() {
     56         return sInstance;
     57     }
     58 
     59     /**
     60      * Creates a new certificate chain validator. This is a private constructor.
     61      * If you need a Certificate chain validator, call getInstance().
     62      */
     63     private CertificateChainValidator() {}
     64 
     65     /**
     66      * Performs the handshake and server certificates validation
     67      * Notice a new chain will be rebuilt by tracing the issuer and subject
     68      * before calling checkServerTrusted().
     69      * And if the last traced certificate is self issued and it is expired, it
     70      * will be dropped.
     71      * @param sslSocket The secure connection socket
     72      * @param domain The website domain
     73      * @return An SSL error object if there is an error and null otherwise
     74      */
     75     public SslError doHandshakeAndValidateServerCertificates(
     76             HttpsConnection connection, SSLSocket sslSocket, String domain)
     77             throws IOException {
     78         // get a valid SSLSession, close the socket if we fail
     79         SSLSession sslSession = sslSocket.getSession();
     80         if (!sslSession.isValid()) {
     81             closeSocketThrowException(sslSocket, "failed to perform SSL handshake");
     82         }
     83 
     84         // retrieve the chain of the server peer certificates
     85         Certificate[] peerCertificates =
     86             sslSocket.getSession().getPeerCertificates();
     87 
     88         if (peerCertificates == null || peerCertificates.length == 0) {
     89             closeSocketThrowException(
     90                 sslSocket, "failed to retrieve peer certificates");
     91         } else {
     92             // update the SSL certificate associated with the connection
     93             if (connection != null) {
     94                 if (peerCertificates[0] != null) {
     95                     connection.setCertificate(
     96                         new SslCertificate((X509Certificate)peerCertificates[0]));
     97                 }
     98             }
     99         }
    100 
    101         return verifyServerDomainAndCertificates((X509Certificate[]) peerCertificates, domain, "RSA");
    102     }
    103 
    104     /**
    105      * Similar to doHandshakeAndValidateServerCertificates but exposed to JNI for use
    106      * by Chromium HTTPS stack to validate the cert chain.
    107      * @param certChain The bytes for certificates in ASN.1 DER encoded certificates format.
    108      * @param domain The full website hostname and domain
    109      * @param authType The authentication type for the cert chain
    110      * @return An SSL error object if there is an error and null otherwise
    111      */
    112     public static SslError verifyServerCertificates(
    113         byte[][] certChain, String domain, String authType)
    114         throws IOException {
    115 
    116         if (certChain == null || certChain.length == 0) {
    117             throw new IllegalArgumentException("bad certificate chain");
    118         }
    119 
    120         X509Certificate[] serverCertificates = new X509Certificate[certChain.length];
    121 
    122         try {
    123             CertificateFactory cf = CertificateFactory.getInstance("X.509");
    124             for (int i = 0; i < certChain.length; ++i) {
    125                 serverCertificates[i] = (X509Certificate) cf.generateCertificate(
    126                         new ByteArrayInputStream(certChain[i]));
    127             }
    128         } catch (CertificateException e) {
    129             throw new IOException("can't read certificate", e);
    130         }
    131 
    132         return verifyServerDomainAndCertificates(serverCertificates, domain, authType);
    133     }
    134 
    135     /**
    136      * Handles updates to credential storage.
    137      */
    138     public static void handleTrustStorageUpdate() {
    139 
    140         try {
    141             X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultTrustManager();
    142             if( x509TrustManager instanceof TrustManagerImpl ) {
    143                 TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager;
    144                 trustManager.handleTrustStorageUpdate();
    145             }
    146         } catch (KeyManagementException ignored) {
    147         }
    148     }
    149 
    150     /**
    151      * Common code of doHandshakeAndValidateServerCertificates and verifyServerCertificates.
    152      * Calls DomainNamevalidator to verify the domain, and TrustManager to verify the certs.
    153      * @param chain the cert chain in X509 cert format.
    154      * @param domain The full website hostname and domain
    155      * @param authType The authentication type for the cert chain
    156      * @return An SSL error object if there is an error and null otherwise
    157      */
    158     private static SslError verifyServerDomainAndCertificates(
    159             X509Certificate[] chain, String domain, String authType)
    160             throws IOException {
    161         // check if the first certificate in the chain is for this site
    162         X509Certificate currCertificate = chain[0];
    163         if (currCertificate == null) {
    164             throw new IllegalArgumentException("certificate for this site is null");
    165         }
    166 
    167         boolean valid = domain != null
    168                 && !domain.isEmpty()
    169                 && sVerifier.verify(domain, currCertificate);
    170         if (!valid) {
    171             if (HttpLog.LOGV) {
    172                 HttpLog.v("certificate not for this host: " + domain);
    173             }
    174             return new SslError(SslError.SSL_IDMISMATCH, currCertificate);
    175         }
    176 
    177         try {
    178             X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultTrustManager();
    179             if (x509TrustManager instanceof TrustManagerImpl) {
    180                 TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager;
    181                 trustManager.checkServerTrusted(chain, authType, domain);
    182             } else {
    183                 x509TrustManager.checkServerTrusted(chain, authType);
    184             }
    185             return null;  // No errors.
    186         } catch (GeneralSecurityException e) {
    187             if (HttpLog.LOGV) {
    188                 HttpLog.v("failed to validate the certificate chain, error: " +
    189                     e.getMessage());
    190             }
    191             return new SslError(SslError.SSL_UNTRUSTED, currCertificate);
    192         }
    193     }
    194 
    195 
    196     private void closeSocketThrowException(
    197             SSLSocket socket, String errorMessage, String defaultErrorMessage)
    198             throws IOException {
    199         closeSocketThrowException(
    200             socket, errorMessage != null ? errorMessage : defaultErrorMessage);
    201     }
    202 
    203     private void closeSocketThrowException(SSLSocket socket,
    204             String errorMessage) throws IOException {
    205         if (HttpLog.LOGV) {
    206             HttpLog.v("validation error: " + errorMessage);
    207         }
    208 
    209         if (socket != null) {
    210             SSLSession session = socket.getSession();
    211             if (session != null) {
    212                 session.invalidate();
    213             }
    214 
    215             socket.close();
    216         }
    217 
    218         throw new SSLHandshakeException(errorMessage);
    219     }
    220 }
    221