[JAVA] Use pfx certificate with Okhttp3

Postscript history

2020/02/08 Added reference link 2020/02/10 Added the reason for using it asynchronously

Introduction

Keep the code you wrote when you decided to support HTTPS communication using the client certificate as a memo in case you quit the company and can not see the source code. The reason why I stick to Okhttp is that I hated it because I was suffering from it long ago. Http (s) URLConnection There may be some parts that are sloppy in writing and some parts that are like a sermon to Buddha, but please forgive me because it is a memo for me.

Advance preparation

  1. Create an "assets" folder under main (same level as main and res)
  2. Store the pfx certificate in the assets folder (this time, for convenience, use "hogehage.pfx")
  3. Added internet communication permission to "AndroidManifest.xml" (↓ like this) There is no problem if the addition location is between manifests, but do not write between activities
<manifest ・ ・ ・
...
//Permissions for internet connection
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
  1. Add the following to the "dependencies" part of "build.gradle (under app)" The version is 2020/02/05 Check the "Releases" part of https://square.github.io/okhttp/ to see if it's up to date.

build.gradle


・ ・ ・
dependencies {
・ ・ ・
implementation("com.squareup.okhttp3:okhttp:4.3.1")

1 and 2 are for HTTPS communication, 3 and 4 are for Okhttp It's getting harder to write so far ... FE Fukayuki 〇 I want to do it

code

Asynctask, not the main thread ... I'm writing in asynchronous processing I used to write in the main thread (MainActivity, etc.), but ... that? Why did you change it? → Originally, Asynctask used Httpurl connection. I tried using okhttp asynchronously and it was possible, and the operation of the application seems to be lighter, so I adopted it as it is (additional note)

AsyncHttps.java


package com.example.test;

import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
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.util.Arrays;
import java.util.concurrent.TimeUnit;

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

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

//Asynchronous processing
//A place for HTTPS communication
//The caller converted the image into a byte array and passed it to this act
public class AsyncHttps extends AsyncTask<byte[], Void, String>
{

    private Context mContext;
    public AsyncHttps(Context context)
    {mContext = context;}

    public static String res=null;

    final Handler handler = new Handler();
    //For array numbers in URLsList
    public static int url_id = 0;

    //Communication result storage variable
    public static String result = null;

    //Asynchronous processing
    @Override
    protected String doInBackground(byte[]... params)
    {

        //I realized that I should write the url list in another act as an array and send the array number at the source.
        String urlSt = URLsList.URL[url_id];

        byte[] word = params[0];

	//
        return result;
    }


    //For http communication
    public void http_post(byte[] bytes)
    {
        //It seems to be a setting for sending using JSON
	//Media Type needs to be changed according to what is sent(If it is an image"image/jpg"Feeling like)
        MediaType mediaType= MediaType.parse("application/json; charset=utf-8");
        //Request Body creation. Things to send
        RequestBody requestBody = RequestBody.create(mediaType,bytes);
        //It's like making a request and packing mail, right?(Texto)
        Request request = new Request.Builder()
                //Destination
                .url(URLsList.URL[url_id])
                .post(requestBody)      //Contents to send
                .build();               //Build up these


        KeyManagerFactory keyManagerFactory;
        //Password set when creating the certificate
        final char[] PASSWORD = "***Password here***".toCharArray();
        InputStream inputStream;
        TrustManagerFactory trustManagerFactory;
        SSLSocketFactory sslSocketFactory;
        X509TrustManager trustManager;
        try
        {
            //Specifying the client certificate file(Place it in the assets folder)
            inputStream = mContext.getResources().getAssets().open("hogehage.pfx");
            //As a reference, this extension was p12, so isn't that the case?
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            //From here on down, you can copy and paste without thinking about anything
            keyStore.load(inputStream,PASSWORD);
            trustManagerFactory = TrustManagerFactory.getInstance
                    (TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(keyStore);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager))
            {
                throw new IllegalStateException("Unexpected default trust managers:"
                        + Arrays.toString(trustManagers));
            }
            trustManager = (X509TrustManager)trustManagers[0];

            keyManagerFactory = KeyManagerFactory.getInstance("X509");
            keyManagerFactory.init(keyStore,PASSWORD);
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(keyManagerFactory.getKeyManagers(),null,null);
            sslSocketFactory = sslContext.getSocketFactory();
            //Copy and paste so far

            final OkHttpClient client = new OkHttpClient.Builder()
                    //.connectTimeout(10, TimeUnit.SECONDS)	//Timeout setting 3 Musketeer
                    //.readTimeout(10, TimeUnit.SECONDS)	//I don't know what you are setting
                    //.writeTimeout(10, TimeUnit.SECONDS)	//See you later
                    .sslSocketFactory(sslSocketFactory,trustManager)
                    .build();

            client.newCall(request).enqueue(new Callback()
            {
                @Override
                public void onFailure(@NonNull Call call, IOException e)
                {
                    //It seems that the processing when an exception occurs will be stable if it is processed separately.
                    failMessage();
                    e.printStackTrace();
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException
                {
                    result = String.valueOf(response);
                    //If something comes back, I may or may not do something here
                    client.connectionPool().evictAll();
                }
            });
        }catch (IOException e)//From here on, when something explodes, it will appear in the error tab log. There is nothing to touch.
        {Log.e("error contents:", String.valueOf(e));}
        catch (NoSuchAlgorithmException e)
        {Log.e("error contents:", String.valueOf(e));}
        catch (CertificateException e)
        {Log.e("error contents:", String.valueOf(e));}
        catch (UnrecoverableKeyException e) 
        {Log.e("error contents:", String.valueOf(e));}
        catch (KeyStoreException e) 
        {Log.e("error contents:", String.valueOf(e));}
        catch (KeyManagementException e) 
        {Log.e("error contents:", String.valueOf(e));}
    }

    //When an exception occurs(When something goes wrong)Processing
    private void failMessage()
    {Log.d("Communication result:","It was bad…");}

}

reference

Okhttp Official: https://square.github.io/okhttp/ Client certificate authentication within the Android app: https://qiita.com/c_ume/items/d082ffd20b3316aab805 Now that SSLSocketFactory is deprecated on Android, what would be the best way to handle Client Certificate Authentication? https://stackoverflow.com/questions/31002159/now-that-sslsocketfactory-is-deprecated-on-android-what-would-be-the-best-way-t

Recommended Posts

Use pfx certificate with Okhttp3
Use ProGuard with Gradle
Use Puphpeteer with Docker
Use XVim2 with Xcode 12.0.1
Use CentOS with LXD
Use ngrok with Docker
Use webmock with Rspec
Use WebJars with Gradle
Use jlink with gradle
Use Lambda Layers with Java
Use GDAL with Python with Docker
Use Thymeleaf with Azure Functions
Use Bulk API with RestHighLevelClient
Use SDKMAN! With Git Bash
Use multiple databases with Rails 6.0
Use Spring JDBC with Spring Boot
Use Ruby with Google Colab
Use SpatiaLite with Java / JDBC
Use log4j2 with YAML + Gradle
[Docker] Use whenever with Docker + Rails
Use PlantUML with Visual Studio Code
Use Basic Authentication with Spring Boot
Use java with MSYS and Cygwin
Use constructor with arguments in cucumber-picocontainer
Use Microsoft Graph with standard Java
Use PostgreSQL inet type with DbUnit
Why use orchestration tools with Docker
Use bootstrap 4 with PlayFramework 2.6 (no CDN)
Let's use Amazon Textract with Ruby
Use Git with SourceTree and Eclipse
Use Azure Bing SpellCheck with Java
Use JDBC with Java and Scala.
Use DataDog APM with unsupported frameworks
Use Java 11 with Google Cloud Functions
How to use mssql-tools with alpine
Beginning with Spring Boot 0. Use Spring CLI
Use cuda11.0 with pytorch using Docker