简体   繁体   English

Android Volley 是否支持 SSL?

[英]Does Android Volley support SSL?

Does anyone know whether Volley supports SSl in Android?有谁知道 Volley 是否支持 Android 中的 SSL? Is there is any way to support SSL via Volley?有没有办法通过 Volley 支持 SSL?

You can refer to my working sample code.您可以参考我的工作示例代码。 Hope this helps!希望这可以帮助!

public class MainActivity extends AppCompatActivity {

    private TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTextView = (TextView) findViewById(R.id.textView);

        String url = "https://192.168.1.100/testvolley";

        HurlStack hurlStack = new HurlStack() {
            @Override
            protected HttpURLConnection createConnection(URL url) throws IOException {
                HttpsURLConnection httpsURLConnection = (HttpsURLConnection) super.createConnection(url);
                try {
                    httpsURLConnection.setSSLSocketFactory(getSSLSocketFactory());
                    httpsURLConnection.setHostnameVerifier(getHostnameVerifier());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return httpsURLConnection;
            }
        };

        final JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, url, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    mTextView.setText(response.toString(5));
                } catch (JSONException e) {
                    mTextView.setText(e.toString());
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                mTextView.setText(error.toString());
            }
        });

        final RequestQueue requestQueue = Volley.newRequestQueue(this, hurlStack);

        requestQueue.add(jsonObjectRequest);
    }

    // Let's assume your server app is hosting inside a server machine
    // which has a server certificate in which "Issued to" is "localhost",for example.
    // Then, inside verify method you can verify "localhost". 
    // If not, you can temporarily return true
    private HostnameVerifier getHostnameVerifier() {
        return new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                //return true; // verify always returns true, which could cause insecure network traffic due to trusting TLS/SSL server certificates for wrong hostnames
                HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
                return hv.verify("localhost", session);
            }
        };
    }

    private TrustManager[] getWrappedTrustManagers(TrustManager[] trustManagers) {
        final X509TrustManager originalTrustManager = (X509TrustManager) trustManagers[0];
        return new TrustManager[]{
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return originalTrustManager.getAcceptedIssuers();
                    }

                    public void checkClientTrusted(X509Certificate[] certs, String authType) {
                        try {
                            if (certs != null && certs.length > 0){
                                certs[0].checkValidity();
                            } else {
                                originalTrustManager.checkClientTrusted(certs, authType);
                            }
                        } catch (CertificateException e) {
                            Log.w("checkClientTrusted", e.toString());
                        }
                    }

                    public void checkServerTrusted(X509Certificate[] certs, String authType) {
                        try {
                            if (certs != null && certs.length > 0){
                                certs[0].checkValidity();
                            } else {
                                originalTrustManager.checkServerTrusted(certs, authType);
                            }
                        } catch (CertificateException e) {
                            Log.w("checkServerTrusted", e.toString());
                        }
                    }
                }
        };
    }    

    private SSLSocketFactory getSSLSocketFactory()
            throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, KeyManagementException {
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        InputStream caInput = getResources().openRawResource(R.raw.my_cert); // this cert file stored in \app\src\main\res\raw folder path

        Certificate ca = cf.generateCertificate(caInput);
        caInput.close();

        KeyStore keyStore = KeyStore.getInstance("BKS");
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);

        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);

        TrustManager[] wrappedTrustManagers = getWrappedTrustManagers(tmf.getTrustManagers());

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, wrappedTrustManagers, null);

        return sslContext.getSocketFactory();
    }
}

IMO, you should also read more at Google's Documentation - Security with HTTPS and SSL IMO,您还应该在Google 的文档中阅读更多内容- HTTPS 和 SSL 的安全性

Yes, of course.是的当然。

Android Volley is a library that you can use to easily and efficiently manage your networking operations over http. Android Volley 是一个库,您可以使用它轻松有效地管理您通过 http 的网络操作。 If the underlying layer use SSL (ie https) or not is totally unrelated.底层是否使用SSL(即https)完全无关。

In other words: the Volley framework is TCP layer agnostic and SSL only impact the TCP layer.换句话说:Volley 框架与TCP 层无关,而 SSL 仅影响 TCP 层。

Yes, I've implemented SSL Pinning with Volley.是的,我已经使用 Volley 实现了 SSL Pinning。 I also used certificates as a String and .cer file.我还将证书用作字符串和 .cer 文件。 Please follow my step.请按照我的步骤。

You need to Create VolleySingleton class.您需要创建 VolleySingleton 类。

import android.content.Context;
import android.os.Build;

import androidx.annotation.RequiresApi;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
import java.io.ByteArrayInputStream;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
public class VolleySingleton {
private static VolleySingleton volleySingleton;
private RequestQueue requestQueue;
private static Context mctx;


private VolleySingleton(Context context){
    this.mctx=context;
    this.requestQueue=getRequestQueue();

}
public RequestQueue getRequestQueue(){
    if (requestQueue==null){
        requestQueue= Volley.newRequestQueue(mctx.getApplicationContext());
    }
    return requestQueue;
}
public static synchronized VolleySingleton getInstance(Context context){
    if (volleySingleton==null){
        volleySingleton=new VolleySingleton(context);
    }
    return volleySingleton;
}
public<T> void addToRequestQue(Request<T> request){
    requestQueue.add(request);

}


public HostnameVerifier getHostnameVerifier() {
    return new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            //return true;
 // verify always returns true, which could cause  insecure network traffic due to     trusting TLS/SSL server certificates for wrong hostnames
            HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
            return hv.verify("Enter your host url", session);
        }
    };

}

@RequiresApi(api = Build.VERSION_CODES.O)
public SSLSocketFactory getGlobalSSlFactory() {
    try {

//Use the certificate from raw folder...use below line
        InputStream inputStream=mctx.getResources().openRawResource(R.raw.test);
//Use the certificate as a String.. I've done the conversion here for String 
        String certificate= "Paste your certificate as string";
        byte encodedCert[] = Base64.getDecoder().decode(certificate);
        ByteArrayInputStream inputStream  = new ByteArrayInputStream(encodedCert);

        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        Certificate ca = cf.generateCertificate(inputStream);
        inputStream.close();
        KeyStore keyStore = KeyStore.getInstance("BKS");
        keyStore.load(null, null);

        keyStore.setCertificateEntry("ca", ca);

        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);

        KeyManagerFactory kmf = KeyManagerFactory.getInstance(
                KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(keyStore, "xxxxxxx".toCharArray());

        final SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, tmf.getTrustManagers(), null);
        return sslContext.getSocketFactory();
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

@RequiresApi(api = Build.VERSION_CODES.O)
public static X509Certificate convertToX509Cert(String certificateString) throws CertificateException {
    X509Certificate certificate = null;
    CertificateFactory cf = null;
    try {
        if (certificateString != null && !certificateString.trim().isEmpty()) {
            certificateString = certificateString.replace("-----BEGIN CERTIFICATE-----\n", "")
                    .replace("-----END CERTIFICATE-----", ""); // NEED FOR PEM FORMAT CERT STRING
            byte[] certificateData = Base64.getDecoder().decode(certificateString);
            cf = CertificateFactory.getInstance("X509");
            certificate = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certificateData));
        }
    } catch (CertificateException e) {
        throw new CertificateException(e);
    }
    return certificate;
}

Now You can call your API and verify the SSL host verifier and certificate like this.现在您可以调用您的 API 并像这样验证 SSL 主机验证程序和证书。

 HurlStack hurlStack = new HurlStack() {
        @Override
        protected HttpURLConnection createConnection(URL url) throws IOException {
            HttpsURLConnection httpsURLConnection = (HttpsURLConnection) super.createConnection(url);
            try {
                httpsURLConnection.setSSLSocketFactory(VolleySingleton.getInstance(getApplicationContext()).getGlobalSSlFactory());
                httpsURLConnection.setHostnameVerifier(VolleySingleton.getInstance(getApplicationContext()).getHostnameVerifier());
                Log.i("SSL","SUCCESS");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return httpsURLConnection;
        }
    };

    JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET,"URL", null,new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject response) {
          
         Log.i("onResponse", response.toString());
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
   
            Log.e("onErrorResponse", error.toString());
        }
    });
    final RequestQueue requestQueue = Volley.newRequestQueue(this, hurlStack);
    requestQueue.add(request); 

You can use this on production mode.您可以在生产模式下使用它。 Happy Coding.. Please thumps up.快乐编码.. 请拍拍。 If It works.如果它有效。 :) :)

I would like to look further @BNK 's answer.我想进一步查看@BNK 的回答。 Suggest建议

requestQueue = Volley.newRequestQueue(getApplicationContext(), new HurlStack(null, getSSLSocketFactory()));

would be enough.就足够了。 Don't know why , by follow @BNK 's answer that extends createConnection , Volley create about 5 connections for reuse which observed by netstat command By just passing getSSLSocketFactory() as new HurlStack() parameter, the connection opened by Volley is reduced.不知道为什么,通过遵循@BNK 的扩展createConnection的答案,Volley 创建了大约 5 个连接以供重用,这些连接由netstat命令观察到通过将getSSLSocketFactory()作为new HurlStack()参数传递,Volley 打开的连接减少了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM