Android uses HTTPS frequently. Therefore, how to verify HTTPS certificates? The verification principle of HTTPS can be referred to an article I wrote before: “HTTPS protocol implementation Principle”, I believe that after reading HTTPS should have a relatively general understanding. Moreover, the tool of HTTP(S) request is encapsulated, so we need to understand the idea of this encapsulation tool class, which is the common Listener mechanism in coding. Then is the Android TCP, UDP communication examples, mainly the Android device as a Client, if the Socket programming is more familiar with Java, these are particularly simple examples, very easy to understand.

TCP/UDP simple example

The following example shows how a Client sends a string of lowercase characters to a Server, and the Server returns an uppercase string:

UDPServer. Java:

public class UDPServer { private static final SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss"); public static void main(String[] args) throws Exception { DatagramSocket datagramSocket; datagramSocket = new DatagramSocket(8090); byte[] buf; DatagramPacket packet; while (true){ buf = new byte[1024]; packet = new DatagramPacket(buf, buf.length); datagramSocket.receive(packet); String content = new String(packet.getData()); InetAddress address = packet.getAddress(); System.out.println(format.format(new Date()) + "-" + address + "-" + content); int port = packet.getPort(); String replyContent = content.toUpperCase(); byte[] sendData = replyContent.getBytes(); DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, address, port); datagramSocket.send(sendPacket); }}}Copy the code

UDPClient. Java:

public class UDPClient { private static final SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss"); Public static void main(String[] args) throws Exception {system.out.println (" Please enter an English sentence, the server will return the uppercase form [exit]"); Scanner scanner = new Scanner(System.in); InetAddress address = InetAddress.getLocalHost(); DatagramPacket packet; DatagramSocket socket = new DatagramSocket(); while(true){ String line = scanner.nextLine(); if("exit".equals(line)) break; byte[] bytes = line.getBytes(); packet = new DatagramPacket(bytes, bytes.length, address, 8090); socket.send(packet); byte[] recvBuf = new byte[1024]; DatagramPacket recvPacket = new DatagramPacket(recvBuf, recvBuf.length); socket.receive(recvPacket); System.out.println(format.format(new Date()) + "-" + address + "-" + new String(recvBuf)); } socket.close(); }}Copy the code

TCPServer. Java:

public class TCPServer { static SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss"); public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(9090); while (true){ Socket socket = serverSocket.accept(); InetAddress address = socket.getInetAddress(); InputStream is = socket.getInputStream(); byte[] readBuf = new byte[1024]; try{ int len = is.read(readBuf); String recv = new String(readBuf, 0, len); System.out.println(format.format(new Date()) + "-" + address + "-" + recv); OutputStream os = socket.getOutputStream(); os.write(recv.toUpperCase().getBytes()); } catch (SocketException e){system.err.println (" client did not send message "); } finally { socket.close(); }}}}Copy the code

TCPClient. Java:

public class TCPClient { private static final SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss"); Public static void main(String[] args) throws Exception {system.out.println (" Please enter an English sentence, the server will return the uppercase form [exit]"); Scanner scanner = new Scanner(System.in); While (true){Socket Socket = new Socket("127.0.0.1", 9090); String line = scanner.nextLine(); if("exit".equals(line)) break; OutputStream os = socket.getOutputStream(); os.write(line.getBytes()); InputStream is = socket.getInputStream(); byte[] readBuf = new byte[1024]; String recv = new String(readBuf, 0, is.read(readBuf)); InetAddress address = socket.getInetAddress(); System.out.println(format.format(new Date()) + "-" + address + "-" + recv); socket.close(); }}}Copy the code

Client is ported to Android

Port two clients to Android:

activity_main.xml

<? The XML version = "1.0" encoding = "utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" <EditText Android :hint=" android:id="@+id/et_content" android:id="@+id/et_content" android:layout_width="match_parent" android:layout_height="wrap_content"/> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <EditText Android: text = "192.168.1.113:8090" android: id = "@ + id/et_udp_server" android: layout_width = "0 dp" android: layout_weight = "1" </EditText> <Button android:text="UDP "Android :onClick="sendUdpMessage" android:layout_weight="1" android:layout_width="0dp" android:layout_height="wrap_content"/> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> < EditText android: text = "192.168.1.113:9090" android: id = "@ + id/et_tcp_server" android: layout_width = "0 dp" </EditText> <Button android:text="TCP" android:onClick="sendTcpMessage" android:layout_weight="1" android:layout_width="0dp" Android :layout_height="wrap_content"/> </LinearLayout> <TextView Android :id="@+id/tv_show" Android :text=" " android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout>Copy the code

MainActivity. Java:

public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private static final SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss", Locale.CHINA); private EditText etInput; private TextView textView; private EditText udpServerET; private EditText tcpServerET; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); etInput = findViewById(R.id.et_content); textView = findViewById(R.id.tv_show); udpServerET = findViewById(R.id.et_udp_server); tcpServerET = findViewById(R.id.et_tcp_server); } public void sendTcpMessage(View view) { String[] tcpInfo = tcpServerET.getText().toString().split(":"); String inputContent = etInput.getText().toString(); new Thread(()->{ try (Socket socket = new Socket(tcpInfo[0], Integer.parseInt(tcpInfo[1]))){ OutputStream os = socket.getOutputStream(); os.write(inputContent.getBytes()); InputStream is = socket.getInputStream(); byte[] readBuf = new byte[1024]; String recv = new String(readBuf, 0, is.read(readBuf)); InetAddress address = socket.getInetAddress(); String ret = String.format("%s-%s-%s", df.format(new Date()), address, recv); runOnUiThread(()-> textView.setText(ret)); }catch (IOException e){ Log.e(TAG, "sendTcpMessage: Error!" ); } }).start(); } public void sendUdpMessage(View view) { String[] udpInfo = udpServerET.getText().toString().split(":"); String inputContent = etInput.getText().toString(); new Thread(()->{ try { DatagramSocket socket = new DatagramSocket(); byte[] bytes = inputContent.getBytes(); InetAddress address = InetAddress.getByName(udpInfo[0]); int serverPort = Integer.parseInt(udpInfo[1]); DatagramPacket packet = new DatagramPacket(bytes, bytes.length, address, serverPort); socket.send(packet); byte[] recvBuf = new byte[1024]; DatagramPacket recvPacket = new DatagramPacket(recvBuf, recvBuf.length); socket.receive(recvPacket); String ret = String.format("%s-%s-%s", df.format(new Date()), address, new String(recvBuf)); runOnUiThread(()-> textView.setText(ret)); }catch (IOException e){ Log.e(TAG, "sendUdpMessage: Error!" ); } }).start(); }}Copy the code

Androidmanifest.xml:

<uses-permission android:/>
Copy the code

The runOnUiThread() method is used in the child thread code to update the UI

Android access HTTPS

For a normal HTTP request, we can use the following method to initiate the request. Here is a simple HTTP request utility class:

public class HttpUtils {
    private static Handler mUIHandler = new Handler(Looper.getMainLooper());

    interface HttpListener {
        void onSuccess(String content);

        void onFail(Exception e);
    }

    public static void doGet(String urlStr, HttpListener listener) {
        new Thread(() -> {
            Looper.prepare();
            try {
                URL url = new URL(urlStr);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(5000);
                conn.setReadTimeout(5000);
                conn.connect();

                try (InputStream is = conn.getInputStream();
                     InputStreamReader reader = new InputStreamReader(is)
                ) {
                    char[] buf = new char[4096];
                    int len;
                    StringBuilder sb = new StringBuilder();
                    while ((len = reader.read(buf)) != -1) {
                        sb.append(new String(buf, 0, len));
                    }
                    mUIHandler.post(() -> listener.onSuccess(sb.toString()));
                } catch (IOException e) {
                    e.printStackTrace();
                    listener.onFail(e);
                }
            }catch (IOException e){
                e.printStackTrace();
                listener.onFail(e);
            }
        }).start();
    }
}
Copy the code

1. No certificate verification (not recommended)

Myx509trustmanager.java, MyX509TrustManager implementation does nothing:

. import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; public class MyX509TrustManager implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[]  chain, String authType) throws CertificateException { // TODO... } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { // TODO... } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }}Copy the code

HttpsUtils.java

. public class HttpsUtils { private static Handler mUIHandler = new Handler(Looper.getMainLooper()); interface HttpListener { void onSuccess(String content); void onFail(Exception e); } public static void doGet(Context context, String urlStr, HttpListener listener) { new Thread(() -> { Looper.prepare();  try { URL url = new URL(urlStr); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); SSLContext sslContext = SSLContext.getInstance("TLS"); TrustManager[] trustManagers = {new MyX509TrustManager()}; sslContext.init(null, trustManagers, new SecureRandom()); conn.setSSLSocketFactory(sslContext.getSocketFactory()); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); conn.connect(); try (InputStream is = conn.getInputStream(); InputStreamReader reader = new InputStreamReader(is) ) { char[] buf = new char[4096]; int len; StringBuilder sb = new StringBuilder(); while ((len = reader.read(buf)) ! = -1) { sb.append(new String(buf, 0, len)); } mUIHandler.post(() -> listener.onSuccess(sb.toString())); } catch (IOException e) { e.printStackTrace(); listener.onFail(e); } }catch (Exception e){ e.printStackTrace(); listener.onFail(e); } }).start(); }}Copy the code

2. Verification certificate (recommended)

My own blog, for example, simply downloads the corresponding certificate in the browser (DER encoding binary or Base64 encoding) and saves a file named srca.cer to the desktop:

Copy the certificate file to the SRC /main/assets/ directory of the project. Create the certificate file without assets. Therefore, the complete path is SRC /main/assets/srca.cer.

Next we need to implement the methods in myx509TrustManager.java:

public class MyX509TrustManager implements X509TrustManager { private static final String TAG = "MyX509TrustManager"; // Certificate object private X509Certificate serverCert; public MyX509TrustManager(X509Certificate serverCert) { this.serverCert = serverCert; } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {// Go through the certificate for (X509Certificate certificate: Chain) {/ / check whether the legitimacy and expired certificate. The checkValidity (); Try {// verify the PublicKey PublicKey PublicKey = servercert.getpublickey (); certificate.verify(publicKey); } catch (Exception e) { throw new CertificateException(e); } } } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }}Copy the code

In the meantime, we’ll use the keyStore API to get the TrustManager array, httpSutils.java as follows:

public class Https2Utils { private static Handler mUIHandler = new Handler(Looper.getMainLooper()); interface HttpListener { void onSuccess(String content); void onFail(Exception e); } public static void doGet(Context context, String urlStr, HttpListener listener) { new Thread(() -> { Looper.prepare();  try { URL url = new URL(urlStr); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); SSLContext sslContext = SSLContext.getInstance("TLS"); X509Certificate serverCert = getCert(context); String defaultType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(defaultType); keyStore.load(null); / / alias, certificate keyStore. SetCertificateEntry (srca, serverCert); String algorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm); trustManagerFactory.init(keyStore); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); sslContext.init(null, trustManagers, new SecureRandom()); conn.setSSLSocketFactory(sslContext.getSocketFactory()); Conn. setHostnameVerifier((hostname, session) -> { HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier(); return verifier.verify("zouchanglin.cn", session); }); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); conn.connect(); try (InputStream is = conn.getInputStream(); InputStreamReader reader = new InputStreamReader(is) ) { char[] buf = new char[4096]; int len; StringBuilder sb = new StringBuilder(); while ((len = reader.read(buf)) ! = -1) { sb.append(new String(buf, 0, len)); } mUIHandler.post(() -> listener.onSuccess(sb.toString())); } catch (IOException e) { e.printStackTrace(); listener.onFail(e); } }catch (Exception e){ e.printStackTrace(); listener.onFail(e); } }).start(); } private static X509Certificate getCert(Context context) { try { // src/main/assets/srca.cer InputStream inputStream = context.getAssets().open("srca.cer"); CertificateFactory factory = CertificateFactory.getInstance("X.509"); return (X509Certificate) factory.generateCertificate(inputStream); } catch (IOException | CertificateException e) { e.printStackTrace(); } return null; }}Copy the code

Using MainActivity is also simple:

public class MainActivity extends AppCompatActivity {

    private EditText etUrl;
    private TextView tvShow;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        etUrl = findViewById(R.id.et_url);
        tvShow = findViewById(R.id.tv_show);
    }

    public void loadContent(View view) {
        String url = etUrl.getText().toString();
        Https2Utils.doGet(this, url, new Https2Utils.HttpListener() {
            @Override
            public void onSuccess(String content) {
                tvShow.setText(content);
            }

            @Override
            public void onFail(Exception e) {
                Toast.makeText(MainActivity.this, "Failed!", Toast.LENGTH_SHORT).show();
            }
        });
    }
}
Copy the code