Example usage for android.net.http SslCertificate SslCertificate

List of usage examples for android.net.http SslCertificate SslCertificate

Introduction

In this page you can find the example usage for android.net.http SslCertificate SslCertificate.

Prototype

public SslCertificate(X509Certificate certificate) 

Source Link

Document

Creates a new SSL certificate object from an X509 certificate

Usage

From source file:android.net.http.CertificateChainValidator.java

/**
 * Performs the handshake and server certificates validation
 * @param sslSocket The secure connection socket
 * @param domain The website domain/*from   w w  w.  j  av a2 s .c om*/
 * @return An SSL error object if there is an error and null otherwise
 */
public SslError doHandshakeAndValidateServerCertificates(HttpsConnection connection, SSLSocket sslSocket,
        String domain) throws SSLHandshakeException, IOException {

    ++sTotal;

    SSLContext sslContext = HttpsConnection.getContext();
    if (sslContext == null) {
        closeSocketThrowException(sslSocket, "SSL context is null");
    }

    X509Certificate[] serverCertificates = null;

    long sessionBeforeHandshakeLastAccessedTime = 0;
    byte[] sessionBeforeHandshakeId = null;

    SSLSession sessionAfterHandshake = null;

    synchronized (sslContext) {
        // get SSL session before the handshake
        SSLSession sessionBeforeHandshake = getSSLSession(sslContext, connection.getHost());
        if (sessionBeforeHandshake != null) {
            sessionBeforeHandshakeLastAccessedTime = sessionBeforeHandshake.getLastAccessedTime();

            sessionBeforeHandshakeId = sessionBeforeHandshake.getId();
        }

        // start handshake, close the socket if we fail
        try {
            sslSocket.setUseClientMode(true);
            sslSocket.startHandshake();
        } catch (IOException e) {
            closeSocketThrowException(sslSocket, e.getMessage(), "failed to perform SSL handshake");
        }

        // retrieve the chain of the server peer certificates
        Certificate[] peerCertificates = sslSocket.getSession().getPeerCertificates();

        if (peerCertificates == null || peerCertificates.length <= 0) {
            closeSocketThrowException(sslSocket, "failed to retrieve peer certificates");
        } else {
            serverCertificates = new X509Certificate[peerCertificates.length];
            for (int i = 0; i < peerCertificates.length; ++i) {
                serverCertificates[i] = (X509Certificate) (peerCertificates[i]);
            }

            // update the SSL certificate associated with the connection
            if (connection != null) {
                if (serverCertificates[0] != null) {
                    connection.setCertificate(new SslCertificate(serverCertificates[0]));
                }
            }
        }

        // get SSL session after the handshake
        sessionAfterHandshake = getSSLSession(sslContext, connection.getHost());
    }

    if (sessionBeforeHandshakeLastAccessedTime != 0 && sessionAfterHandshake != null
            && Arrays.equals(sessionBeforeHandshakeId, sessionAfterHandshake.getId())
            && sessionBeforeHandshakeLastAccessedTime < sessionAfterHandshake.getLastAccessedTime()) {

        if (HttpLog.LOGV) {
            HttpLog.v("SSL session was reused: total reused: " + sTotalReused + " out of total of: " + sTotal);

            ++sTotalReused;
        }

        // no errors!!!
        return null;
    }

    // check if the first certificate in the chain is for this site
    X509Certificate currCertificate = serverCertificates[0];
    if (currCertificate == null) {
        closeSocketThrowException(sslSocket, "certificate for this site is null");
    } else {
        if (!DomainNameChecker.match(currCertificate, domain)) {
            String errorMessage = "certificate not for this host: " + domain;

            if (HttpLog.LOGV) {
                HttpLog.v(errorMessage);
            }

            sslSocket.getSession().invalidate();
            return new SslError(SslError.SSL_IDMISMATCH, currCertificate);
        }
    }

    //
    // first, we validate the chain using the standard validation
    // solution; if we do not find any errors, we are done; if we
    // fail the standard validation, we re-validate again below,
    // this time trying to retrieve any individual errors we can
    // report back to the user.
    //
    try {
        synchronized (mDefaultTrustManager) {
            mDefaultTrustManager.checkServerTrusted(serverCertificates, "RSA");

            // no errors!!!
            return null;
        }
    } catch (CertificateException e) {
        if (HttpLog.LOGV) {
            HttpLog.v("failed to pre-validate the certificate chain, error: " + e.getMessage());
        }
    }

    sslSocket.getSession().invalidate();

    SslError error = null;

    // we check the root certificate separately from the rest of the
    // chain; this is because we need to know what certificate in
    // the chain resulted in an error if any
    currCertificate = serverCertificates[serverCertificates.length - 1];
    if (currCertificate == null) {
        closeSocketThrowException(sslSocket, "root certificate is null");
    }

    // check if the last certificate in the chain (root) is trusted
    X509Certificate[] rootCertificateChain = { currCertificate };
    try {
        synchronized (mDefaultTrustManager) {
            mDefaultTrustManager.checkServerTrusted(rootCertificateChain, "RSA");
        }
    } catch (CertificateExpiredException e) {
        String errorMessage = e.getMessage();
        if (errorMessage == null) {
            errorMessage = "root certificate has expired";
        }

        if (HttpLog.LOGV) {
            HttpLog.v(errorMessage);
        }

        error = new SslError(SslError.SSL_EXPIRED, currCertificate);
    } catch (CertificateNotYetValidException e) {
        String errorMessage = e.getMessage();
        if (errorMessage == null) {
            errorMessage = "root certificate not valid yet";
        }

        if (HttpLog.LOGV) {
            HttpLog.v(errorMessage);
        }

        error = new SslError(SslError.SSL_NOTYETVALID, currCertificate);
    } catch (CertificateException e) {
        String errorMessage = e.getMessage();
        if (errorMessage == null) {
            errorMessage = "root certificate not trusted";
        }

        if (HttpLog.LOGV) {
            HttpLog.v(errorMessage);
        }

        return new SslError(SslError.SSL_UNTRUSTED, currCertificate);
    }

    // Then go through the certificate chain checking that each
    // certificate trusts the next and that each certificate is
    // within its valid date range. Walk the chain in the order
    // from the CA to the end-user
    X509Certificate prevCertificate = serverCertificates[serverCertificates.length - 1];

    for (int i = serverCertificates.length - 2; i >= 0; --i) {
        currCertificate = serverCertificates[i];

        // if a certificate is null, we cannot verify the chain
        if (currCertificate == null) {
            closeSocketThrowException(sslSocket, "null certificate in the chain");
        }

        // verify if trusted by chain
        if (!prevCertificate.getSubjectDN().equals(currCertificate.getIssuerDN())) {
            String errorMessage = "not trusted by chain";

            if (HttpLog.LOGV) {
                HttpLog.v(errorMessage);
            }

            return new SslError(SslError.SSL_UNTRUSTED, currCertificate);
        }

        try {
            currCertificate.verify(prevCertificate.getPublicKey());
        } catch (GeneralSecurityException e) {
            String errorMessage = e.getMessage();
            if (errorMessage == null) {
                errorMessage = "not trusted by chain";
            }

            if (HttpLog.LOGV) {
                HttpLog.v(errorMessage);
            }

            return new SslError(SslError.SSL_UNTRUSTED, currCertificate);
        }

        // verify if the dates are valid
        try {
            currCertificate.checkValidity();
        } catch (CertificateExpiredException e) {
            String errorMessage = e.getMessage();
            if (errorMessage == null) {
                errorMessage = "certificate expired";
            }

            if (HttpLog.LOGV) {
                HttpLog.v(errorMessage);
            }

            if (error == null || error.getPrimaryError() < SslError.SSL_EXPIRED) {
                error = new SslError(SslError.SSL_EXPIRED, currCertificate);
            }
        } catch (CertificateNotYetValidException e) {
            String errorMessage = e.getMessage();
            if (errorMessage == null) {
                errorMessage = "certificate not valid yet";
            }

            if (HttpLog.LOGV) {
                HttpLog.v(errorMessage);
            }

            if (error == null || error.getPrimaryError() < SslError.SSL_NOTYETVALID) {
                error = new SslError(SslError.SSL_NOTYETVALID, currCertificate);
            }
        }

        prevCertificate = currCertificate;
    }

    // if we do not have an error to report back to the user, throw
    // an exception (a generic error will be reported instead)
    if (error == null) {
        closeSocketThrowException(sslSocket,
                "failed to pre-validate the certificate chain due to a non-standard error");
    }

    return error;
}