如何调用Android的HTTPS Web服务Android、HTTPS、Web

2023-09-13 01:45:56 作者:是你又如何

您好,我对Android应用程序的工作,我需要调用Web服务,而我使用 HTTPS 的协议。我能够调用简单的 HTTP 的协议的Web服务,但没有发现任何正确的解决方案来调用 HTTPS 。我发现计算器的多个线程像How忽略的Apache的HttpClient 4.0 SSL证书错误,但一切又都绕过导致不小的 HTTPS 的SSL。我不想绕过SSL。

Hello I am working on android application where I need to call Web Service where I am using https protocol. I am able to call simple http protocol based web service but did not find any correct solution to call https. I found many threads of stackoverflow like How to ignore SSL certificate errors in Apache HttpClient 4.0 but all they are bypassing the SSL that results no mean of https. I don't want to bypass SSL.

我这样做,但它不能正常工作。

I am doing this but it is not working.

HttpClient httpclient = new DefaultHttpClient();

// Prepare a request object
HttpGet httpget = new HttpGet(url); 

// Execute the request
HttpResponse response;
try {
    response = httpclient.execute(httpget);
    // Examine the response status
    Log.i("Praeda",response.getStatusLine().toString());

    // Get hold of the response entity
    HttpEntity entity = response.getEntity();
    // If the response does not enclose an entity, there is no need
    // to worry about connection release

    if (entity != null) {

        // A Simple JSON Response Read
        InputStream instream = entity.getContent();
        String result= convertStreamToString(instream);
        // now you have the string representation of the HTML request
        instream.close();
    }


} catch (Exception e) {}

请帮我做这件事。先谢谢了。

Please help me to do this. Thanks in advance.

推荐答案

下面是该完整的解决方案。我写了这个对我的申请。

Here is complete solution for that. I have written this for my application.

/**
 * This Activity is being used to show an alert about certificate exception
 * while communicating to server. User can take action on the alert and
 * {@link X509Certificate} will be added to trust zone if user proceed.
 */
public class SSLCertificateErrorDialogActivity extends Activity {

    private static final String TAG = SSLCertificateErrorDialogActivity.class
            .getSimpleName();
    /** Key to send certificate via Intent between activities */
    private static final String CERTIFICATE_INTENT_EXTRA = "ssl_certificate";
    /** Key to send failing url via Intent between activities */
    private static final String FAILING_URL_INTENT_EXTRA = "failing_url";
    /** Request code for install certificate */
    private static final int INSTALL_CERTIFICATE = 100;
    private AlertDialog mCertificateDialog;
    /**
     * Certificate which needs to added to trust zone.
     */
    private X509Certificate mX509Certificate;
    /**
     * Url which is being failed for the SSL handshake
     */
    private String mFailingUrl;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // This is UI less Activity. Layout should not be set.
        // Read certificate intent and install
        handleIntent(getIntent());
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        handleIntent(intent);
    }

    private void handleIntent(Intent intent) {
        if (intent == null) {
            Log.d(TAG, "Can not show dialog, intent is null");
            finish();
            return;
        }
        this.mX509Certificate = (X509Certificate) intent
                .getSerializableExtra(CERTIFICATE_INTENT_EXTRA);
        this.mFailingUrl = (String) intent.getStringExtra(FAILING_URL_INTENT_EXTRA);
        if ((this.mX509Certificate == null) || (this.mFailingUrl == null)) {
            Log.d(TAG,
                    "Can not show dialog, certificate or failingurl is null");
            finish();
            return;
        }
        // Inform user for certificate error
        if ((mCertificateDialog == null)
                || (mCertificateDialog.isShowing() == false)) {
            // Show dialog only when if it it not showing.
            // Certificate will be updated, and will be read
            // from dialog when click on ok. So no need to
            // dismiss current dialog.
            showSSLCertificateAcceptDialog();
        }
    }

    @Override
    public void onBackPressed() {
        // Prevent back press
    }

    @Override
    protected void onDestroy() {
        if ((mCertificateDialog != null)
                && (mCertificateDialog.isShowing() == true)) {
            mCertificateDialog.dismiss();
        }
        super.onDestroy();
    }

    /**
     * Shows an alert dialog about SSL certificate issue. If user proceed,
     * certificate will be added to trust zone, and this dialog will not be
     * shown for same certificate.
     */
    private void showSSLCertificateAcceptDialog() {

        AlertDialog.Builder builder = new AlertDialog.Builder(
                SSLCertificateErrorDialogActivity.this);
        builder.setIcon(R.drawable.abouthp_icon);
        builder.setTitle(R.string.untrusted_cert_dialog_title);
        builder.setMessage(msg);
        builder.setPositiveButton(R.string.untrusted_cert_dialog_action_ok,
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        installCertificate();
                    }
                });
        builder.setNegativeButton(R.string.untrusted_cert_dialog_action_cancel,
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        dialog.dismiss();
                        // TODO Retry the failing url
                        finish();
                    }
                });
        mCertificateDialog = builder.create();
        mCertificateDialog.setCancelable(false);
        mCertificateDialog.show();
    }

    /**
     * Install {@link X509Certificate} to trust zone. First this method will try
     * to add certificate from background and on fail it will show a dialog to
     * add certificate. This method must be called from an Activity, as it need
     * an activity instance.
     */
    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    private void installCertificate() {
        X509Certificate certificate = SSLCertificateErrorDialogActivity.this.mX509Certificate;
        if (certificate != null) {
            byte[] encodedCert = null;
            try {
                encodedCert = certificate.getEncoded();
            } catch (CertificateEncodingException e) {
                e.printStackTrace();
            }
            if (encodedCert != null) {
                installUsingIntent(encodedCert, INSTALL_CERTIFICATE);
            }
        } else {
            // TODO Retry the failing url
            finish();
        }
    }

    /**
     * Install certificate to trust zone using intent. User action will be
     * required while installing.
     * 
     * @param encodedCert
     *            of {@link X509Certificate}
     * @param requestCode
     */
    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    private void installUsingIntent(byte[] encodedCert, int requestCode) {
        Intent intent = KeyChain.createInstallIntent();
        // Default Alias name. User can change it.
        intent.putExtra(KeyChain.EXTRA_NAME, "MY Certificate");
        intent.putExtra(KeyChain.EXTRA_CERTIFICATE, encodedCert);
        startActivityForResult(intent, requestCode);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
        case INSTALL_CERTIFICATE:
            // No matter if action was success or not, retry to connect with
            // failed url and finish this activity.
            // You can retry the failiing url
            finish();
            break;

        default:
            break;
        }
    }

    /**
     * Show {@link SSLCertificateErrorDialogActivity} to inform user that, while
     * communicating to server there is untrusted certificate exception. User
     * can take action, certificate will be added to trust zone if user proceed.
     * 
     * @param context
     * @param certificate
     *            {@link X509Certificate} to be added to trust zone.
     * @param failingUrl
     *            is an url for SSL certificate error occurred, purpose of this
     *            url is to retry the same url after user action either
     *            cancelled or proceed.
     */
    public static void show(Context context, X509Certificate certificate,
            String failingUrl) {
        Context appContext = context.getApplicationContext();
        Intent intent = new Intent(appContext,
                SSLCertificateErrorDialogActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
        intent.putExtra(CERTIFICATE_INTENT_EXTRA, certificate);
        intent.putExtra(FAILING_URL_INTENT_EXTRA, failingUrl);
        appContext.startActivity(intent);
    }
}

这里是工具类,帮助您处理SSL证书错误,并安装到设备

And here is the utility class which help you to handle SSL certificate error and install to device

import java.io.IOException;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import android.content.Context;
import android.util.Log;

/**
 * This class will perform all network related calls like post, get and put.
 */
public class NetworkUtility {
    protected static final String TAG = NetworkUtility.class.getSimpleName();
    /**
     * Connection timeout. 15 seconds
     */
    private static final int HTTP_CONNECTION_TIMEOUT = 150000;

    /**
     * Returns Default HTTP client with socket factories initialised.
     * 
     * @param context
     * @param targetUrl
     *            to do request
     * @return Default HTTP Client
     */
    private static HttpClient getDefaultHttpClient(Context context,
            String targetUrl) {
        HttpParams params = new BasicHttpParams();
        HttpConnectionParams.setConnectionTimeout(params,
                HTTP_CONNECTION_TIMEOUT);
        HttpConnectionParams.setSoTimeout(params, HTTP_CONNECTION_TIMEOUT);
        try {
            SchemeRegistry registry = new SchemeRegistry();
            registry.register(new Scheme("http", PlainSocketFactory
                    .getSocketFactory(), 80));
            registry.register(new Scheme("https", new MySSLSocketFactory(
                    context.getApplicationContext(), targetUrl), 443));
            ClientConnectionManager ccm = new ThreadSafeClientConnManager(
                    params, registry);
            return new DefaultHttpClient(ccm, params);
        } catch (Exception e) {
            e.printStackTrace();
            return new DefaultHttpClient(params);
        }
    }

    /**
     * TrustManager to accept all certificates. It does not do any certificates
     * validation.
     * 
     * TODO: Once we have actual certificates this implementation should be
     * changed accordingly.
     */
    private static class MyTrustManager implements X509TrustManager {
        private X509TrustManager mOriginalX509TrustManager;
        private Context mContext;
        private String mTargetUrl;

        /**
         * @param context
         *            - application context.
         * @param targetUrl
         *            - to do request.
         */
        public MyTrustManager(Context context, String targetUrl) {
            try {
                this.mContext = context;
                this.mTargetUrl = targetUrl;
                TrustManagerFactory originalTrustManagerFactory = TrustManagerFactory
                        .getInstance("X509");
                originalTrustManagerFactory.init((KeyStore) null);
                TrustManager[] originalTrustManagers = originalTrustManagerFactory
                        .getTrustManagers();
                this.mOriginalX509TrustManager = (X509TrustManager) originalTrustManagers[0];
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public void checkClientTrusted(X509Certificate[] cert, String authType)
                throws CertificateException {
        }

        public void checkServerTrusted(X509Certificate[] cert, String authType)
                throws CertificateException {
            try {
                // Verify if the certificate has been trusted.
                // This validation will pass if certificate has
                // been added by user or system.
                mOriginalX509TrustManager.checkServerTrusted(cert, authType);
            } catch (CertificateException originalException) {
                // Certificate has not present into trust zone.
                // Find first certificate from the array of certificates which
                // needs to install.
                X509Certificate certificate = getCertificateToInstall(cert);
                Log.d(TAG, "Showing dialog for certificate exception...");
                // Show dialog where user can install this certificate
                SSLCertificateErrorDialogActivity.show(this.mContext,
                        certificate, this.mTargetUrl);
                throw originalException;
            }
        }

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    }

    /**
     * Get certificate to be installed from the given list of certificates. It
     * iterates all certificates from CA and if a certificate, from the given
     * array is not present into CA, this method returns that certificate.
     * 
     * @param certificates
     * @return {@link X509Certificate} to install.
     */
    private static X509Certificate getCertificateToInstall(
            X509Certificate[] certificates) {
        X509Certificate result = null;
        try {
            KeyStore ks = KeyStore.getInstance("AndroidCAStore");
            if (ks != null) {
                ks.load(null, null);
                boolean certFound = false;
                for (X509Certificate certificate : certificates) {
                    Enumeration<String> aliases = ks.aliases();
                    while (aliases.hasMoreElements()) {
                        String alias = (String) aliases.nextElement();
                        X509Certificate cert = (X509Certificate) ks
                                .getCertificate(alias);
                        if (certificate.equals(cert) == true) {
                            certFound = true;
                            break;
                        }
                    }
                    if (certFound == false) {
                        Log.d(TAG, "Not found certificate");
                        // Need to install this certificate
                        result = certificate;
                        break;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }


    private static class MySSLSocketFactory extends SSLSocketFactory {
        private javax.net.ssl.SSLSocketFactory mFactory;

        public MySSLSocketFactory(Context context, String targetUrl)
                throws KeyManagementException, NoSuchAlgorithmException,
                KeyStoreException, UnrecoverableKeyException {
            super((KeyStore) null);
            try {
                SSLContext sslcontext = SSLContext.getInstance("TLS");
                sslcontext.init(null, new TrustManager[] { new MyTrustManager(
                        context, targetUrl) }, null);
                mFactory = sslcontext.getSocketFactory();
                setHostnameVerifier(new AllowAllHostnameVerifier());
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

        @Override
        public Socket createSocket() throws IOException {
            return mFactory.createSocket();
        }

        @Override
        public Socket createSocket(Socket socket, String s, int i, boolean flag)
                throws IOException {
            return mFactory.createSocket(socket, s, i, flag);
        }
    }
}

使用是非常简单的,如下

Use is very simple as below

HttpGet httpGet = new HttpGet(url);
HttpClient httpClient = getDefaultHttpClient(context, url);
HttpResponse response = httpClient.execute(httpGet);