mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-06-16 21:10:57 +08:00
Certificate export now uses the MitmService API
This commit is contained in:
parent
6f42015873
commit
00984fc224
225
app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java
Normal file
225
app/src/main/java/com/emanuelef/remote_capture/MitmAddon.java
Normal file
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* This file is part of PCAPdroid.
|
||||
*
|
||||
* PCAPdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* PCAPdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with PCAPdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2022 - Emanuele Faranda
|
||||
*/
|
||||
|
||||
package com.emanuelef.remote_capture;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.emanuelef.remote_capture.interfaces.MitmListener;
|
||||
import com.emanuelef.remote_capture.model.Prefs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class MitmAddon {
|
||||
/* API */
|
||||
public static final String PACKAGE_NAME = "com.pcapdroid.mitm";
|
||||
public static final String MITM_PERMISSION = "com.pcapdroid.permission.MITM";
|
||||
public static final String MITM_SERVICE = PACKAGE_NAME + ".MitmService";
|
||||
|
||||
public static final int MSG_START_MITM = 1;
|
||||
public static final int MSG_GET_CA_CERTIFICATE = 2;
|
||||
public static final String CERTIFICATE_RESULT = "certificate";
|
||||
/* END API */
|
||||
|
||||
private static final String TAG = "MitmAddon";
|
||||
private final Context mContext;
|
||||
private final MitmListener mReceiver;
|
||||
private final Messenger mMessenger;
|
||||
private Messenger mService;
|
||||
|
||||
public MitmAddon(Context ctx, MitmListener receiver) {
|
||||
// Important: the application context is required here, otherwise bind/unbind will not work properly
|
||||
mContext = ctx.getApplicationContext();
|
||||
mReceiver = receiver;
|
||||
mMessenger = new Messenger(new ReplyHandler(receiver));
|
||||
}
|
||||
|
||||
private final ServiceConnection mConnection = new ServiceConnection() {
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
Log.d(TAG, "Service connected");
|
||||
mService = new Messenger(service);
|
||||
mReceiver.onMitmServiceConnect();
|
||||
}
|
||||
|
||||
public void onServiceDisconnected(ComponentName className) {
|
||||
Log.d(TAG, "Service disconnected");
|
||||
disconnect(); // call unbind to prevent new connections
|
||||
mReceiver.onMitmServiceDisconnect();
|
||||
}
|
||||
};
|
||||
|
||||
public static boolean isInstalled(Context ctx) {
|
||||
try {
|
||||
ctx.getPackageManager().getPackageInfo(PACKAGE_NAME, 0);
|
||||
return true;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasMitmPermission(Context ctx) {
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
return ctx.checkSelfPermission(MitmAddon.MITM_PERMISSION) == PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void setDecryptionSetupDone(Context ctx, boolean done) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||
prefs.edit()
|
||||
.putBoolean(Prefs.PREF_TLS_DECRYPTION_SETUP_DONE, done)
|
||||
.apply();
|
||||
}
|
||||
|
||||
public static boolean needsSetup(Context ctx) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||
|
||||
if(!Prefs.isTLSDecryptionSetupDone(prefs))
|
||||
return true;
|
||||
|
||||
// Perform some other quick checks just in case the env has changed
|
||||
if(!isInstalled(ctx) || !hasMitmPermission(ctx)) {
|
||||
setDecryptionSetupDone(ctx, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class ReplyHandler extends Handler {
|
||||
private final WeakReference<MitmListener> mReceiver;
|
||||
|
||||
ReplyHandler(MitmListener receiver) {
|
||||
mReceiver = new WeakReference<>(receiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(@NonNull Message msg) {
|
||||
Log.d(TAG, "Message: " + msg.what);
|
||||
|
||||
MitmListener receiver = mReceiver.get();
|
||||
if(receiver == null)
|
||||
return;
|
||||
|
||||
if(msg.what == MitmAddon.MSG_GET_CA_CERTIFICATE) {
|
||||
String ca_pem = null;
|
||||
|
||||
if(msg.getData() != null) {
|
||||
Bundle res = msg.getData();
|
||||
ca_pem = res.getString(MitmAddon.CERTIFICATE_RESULT);
|
||||
}
|
||||
|
||||
receiver.onMitmGetCaCertificateResult(ca_pem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Asynchronously connect to the service. The onConnect callback will be called.
|
||||
public boolean connect(int extra_flags) {
|
||||
Intent intent = new Intent();
|
||||
intent.setComponent(new ComponentName(MitmAddon.PACKAGE_NAME, MitmAddon.MITM_SERVICE));
|
||||
|
||||
if(!mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE | extra_flags)) {
|
||||
mContext.unbindService(mConnection);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// This must be always called after connect, e.g. in the OnDestroy
|
||||
public void disconnect() {
|
||||
if(mService != null) {
|
||||
Log.d(TAG, "Unbinding service...");
|
||||
mContext.unbindService(mConnection);
|
||||
mService = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return (mService != null);
|
||||
}
|
||||
|
||||
public boolean requestCaCertificate() {
|
||||
if(mService == null) {
|
||||
Log.e(TAG, "Not connected");
|
||||
return false;
|
||||
}
|
||||
|
||||
Message msg = Message.obtain(null, MitmAddon.MSG_GET_CA_CERTIFICATE);
|
||||
msg.replyTo = mMessenger;
|
||||
try {
|
||||
mService.send(msg);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Start the mitm proxy and returns a ParcelFileDescriptor for the data communication.
|
||||
// The proxy can be stopped by closing the descriptor and then calling disconnect().
|
||||
public ParcelFileDescriptor startProxy(int port) {
|
||||
if(mService == null) {
|
||||
Log.e(TAG, "Not connected");
|
||||
return null;
|
||||
}
|
||||
|
||||
ParcelFileDescriptor[] pair;
|
||||
try {
|
||||
pair = ParcelFileDescriptor.createReliableSocketPair();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
Message msg = Message.obtain(null, MitmAddon.MSG_START_MITM, port, 0, pair[0]);
|
||||
|
||||
try {
|
||||
mService.send(msg);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
Utils.safeClose(pair[0]);
|
||||
Utils.safeClose(pair[1]);
|
||||
return null;
|
||||
}
|
||||
|
||||
// The other end of the pipe is sent, close it locally
|
||||
Utils.safeClose(pair[0]);
|
||||
|
||||
return pair[1];
|
||||
}
|
||||
}
|
||||
@ -22,22 +22,21 @@ package com.emanuelef.remote_capture;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.util.LruCache;
|
||||
|
||||
import com.emanuelef.remote_capture.activities.MitmSetupWizard;
|
||||
import com.emanuelef.remote_capture.interfaces.ConnectionsListener;
|
||||
import com.emanuelef.remote_capture.interfaces.MitmListener;
|
||||
import com.emanuelef.remote_capture.model.ConnectionDescriptor;
|
||||
import com.emanuelef.remote_capture.model.MitmAddon;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
@ -53,70 +52,23 @@ import java.util.StringTokenizer;
|
||||
*
|
||||
* The raw payload data follows the header.
|
||||
*/
|
||||
public class MitmReceiver implements Runnable, ConnectionsListener {
|
||||
public class MitmReceiver implements Runnable, ConnectionsListener, MitmListener {
|
||||
private static final String TAG = "MitmReceiver";
|
||||
public static final int MAX_PLAINTEXT_LENGTH = 1024; // sync with pcapdroid.h
|
||||
public static final int TLS_DECRYPTION_PROXY_PORT = 7780;
|
||||
private Thread mThread;
|
||||
private final ConnectionsRegister mReg;
|
||||
private final Context mContext;
|
||||
private Messenger mService;
|
||||
private boolean bound;
|
||||
private final MitmAddon mAddon;
|
||||
private ParcelFileDescriptor mSocketFd;
|
||||
|
||||
private final ServiceConnection mConnection = new ServiceConnection() {
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
Log.d(TAG, "Service connected");
|
||||
mService = new Messenger(service);
|
||||
|
||||
ParcelFileDescriptor[] pair;
|
||||
try {
|
||||
// Create a pair of connected fds
|
||||
pair = ParcelFileDescriptor.createReliableSocketPair();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
mSocketFd = pair[1];
|
||||
|
||||
Message msg = Message.obtain(null, MitmAddon.MSG_START_MITM, TLS_DECRYPTION_PROXY_PORT, 0, pair[0]);
|
||||
|
||||
try {
|
||||
mService.send(msg);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
Utils.safeClose(pair[0]);
|
||||
Utils.safeClose(pair[1]);
|
||||
return;
|
||||
}
|
||||
bound = true;
|
||||
|
||||
// Sent, close here
|
||||
Utils.safeClose(pair[0]);
|
||||
|
||||
if(mThread != null)
|
||||
mThread.interrupt();
|
||||
|
||||
mThread = new Thread(MitmReceiver.this);
|
||||
mThread.start();
|
||||
}
|
||||
|
||||
public void onServiceDisconnected(ComponentName className) {
|
||||
Log.d(TAG, "Service disconnected");
|
||||
mService = null;
|
||||
Utils.safeClose(mSocketFd);
|
||||
mSocketFd = null;
|
||||
bound = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Shared state
|
||||
private final LruCache<Integer, Integer> mPortToConnId = new LruCache<>(64);
|
||||
|
||||
public MitmReceiver(Context ctx) {
|
||||
// Important: the application context is required here, otherwise bind/unbind will not work properly
|
||||
mContext = ctx.getApplicationContext();
|
||||
mContext = ctx;
|
||||
mReg = CaptureService.requireConnsRegister();
|
||||
mAddon = new MitmAddon(mContext, this);
|
||||
}
|
||||
|
||||
public boolean start() throws IOException {
|
||||
@ -125,8 +77,7 @@ public class MitmReceiver implements Runnable, ConnectionsListener {
|
||||
Intent intent = new Intent();
|
||||
intent.setComponent(new ComponentName(MitmAddon.PACKAGE_NAME, MitmAddon.MITM_SERVICE));
|
||||
|
||||
if(!mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT)) {
|
||||
mContext.unbindService(mConnection);
|
||||
if(!mAddon.connect(Context.BIND_IMPORTANT)) {
|
||||
Utils.showToastLong(mContext, R.string.mitm_start_failed);
|
||||
return false;
|
||||
}
|
||||
@ -152,19 +103,17 @@ public class MitmReceiver implements Runnable, ConnectionsListener {
|
||||
}
|
||||
mThread = null;
|
||||
|
||||
if(bound) {
|
||||
Log.d(TAG, "Unbinding service...");
|
||||
mContext.unbindService(mConnection);
|
||||
bound = false;
|
||||
}
|
||||
mAddon.disconnect();
|
||||
|
||||
Log.d(TAG, "stop done");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Log.d(TAG, "Receiving data...");
|
||||
|
||||
try(DataInputStream istream = new DataInputStream(new ParcelFileDescriptor.AutoCloseInputStream(mSocketFd))) {
|
||||
while(bound) {
|
||||
while(mAddon.isConnected()) {
|
||||
String payload_type;
|
||||
int port;
|
||||
int payload_len;
|
||||
@ -214,6 +163,8 @@ public class MitmReceiver implements Runnable, ConnectionsListener {
|
||||
if(mSocketFd != null) // ignore termination
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Log.d(TAG, "End receiving data");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -232,6 +183,47 @@ public class MitmReceiver implements Runnable, ConnectionsListener {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMitmServiceConnect() {
|
||||
// when connected, verify that the certificate is installed before starting the proxy.
|
||||
// will continue on onMitmGetCaCertificateResult.
|
||||
if(!mAddon.requestCaCertificate())
|
||||
mAddon.disconnect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMitmGetCaCertificateResult(@Nullable String ca_pem) {
|
||||
if(!Utils.isCAInstalled(ca_pem)) {
|
||||
// The certificate has been uninstalled from the system
|
||||
Utils.showToastLong(mContext, R.string.cert_reinstall_required);
|
||||
MitmAddon.setDecryptionSetupDone(mContext, false);
|
||||
CaptureService.stopService();
|
||||
return;
|
||||
}
|
||||
|
||||
// Certificate installation verified, start the proxy
|
||||
mSocketFd = mAddon.startProxy(TLS_DECRYPTION_PROXY_PORT);
|
||||
if(mSocketFd == null) {
|
||||
mAddon.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
if(mThread != null)
|
||||
mThread.interrupt();
|
||||
|
||||
mThread = new Thread(MitmReceiver.this);
|
||||
mThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMitmServiceDisconnect() {
|
||||
Utils.safeClose(mSocketFd);
|
||||
mSocketFd = null;
|
||||
|
||||
// Stop the capture if running
|
||||
CaptureService.stopService();
|
||||
}
|
||||
|
||||
ConnectionDescriptor getConnByLocalPort(int local_port) {
|
||||
Integer conn_id;
|
||||
|
||||
|
||||
@ -975,4 +975,15 @@ public class Utils {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isCAInstalled(String ca_pem) {
|
||||
if(ca_pem == null)
|
||||
return false;
|
||||
|
||||
X509Certificate ca_cert = x509FromPem(ca_pem);
|
||||
if(ca_cert == null)
|
||||
return false;
|
||||
|
||||
return isCAInstalled(ca_cert);
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ import com.emanuelef.remote_capture.model.AppState;
|
||||
import com.emanuelef.remote_capture.CaptureService;
|
||||
import com.emanuelef.remote_capture.model.CaptureSettings;
|
||||
import com.emanuelef.remote_capture.model.ListInfo;
|
||||
import com.emanuelef.remote_capture.model.MitmAddon;
|
||||
import com.emanuelef.remote_capture.MitmAddon;
|
||||
import com.emanuelef.remote_capture.model.Prefs;
|
||||
import com.emanuelef.remote_capture.R;
|
||||
import com.emanuelef.remote_capture.Utils;
|
||||
|
||||
@ -42,7 +42,7 @@ import androidx.preference.SwitchPreference;
|
||||
import com.emanuelef.remote_capture.Billing;
|
||||
import com.emanuelef.remote_capture.PCAPdroid;
|
||||
import com.emanuelef.remote_capture.Utils;
|
||||
import com.emanuelef.remote_capture.model.MitmAddon;
|
||||
import com.emanuelef.remote_capture.MitmAddon;
|
||||
import com.emanuelef.remote_capture.model.Prefs;
|
||||
import com.emanuelef.remote_capture.R;
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import com.emanuelef.remote_capture.R;
|
||||
import com.emanuelef.remote_capture.Utils;
|
||||
import com.emanuelef.remote_capture.model.MitmAddon;
|
||||
import com.emanuelef.remote_capture.MitmAddon;
|
||||
|
||||
public class GrantPermission extends StepFragment {
|
||||
private final ActivityResultLauncher<String> requestPermissionLauncher =
|
||||
|
||||
@ -26,7 +26,7 @@ import androidx.annotation.Nullable;
|
||||
import android.view.View;
|
||||
|
||||
import com.emanuelef.remote_capture.R;
|
||||
import com.emanuelef.remote_capture.model.MitmAddon;
|
||||
import com.emanuelef.remote_capture.MitmAddon;
|
||||
|
||||
public class InstallAddon extends StepFragment {
|
||||
@Override
|
||||
|
||||
@ -25,6 +25,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.activity.result.ActivityResult;
|
||||
@ -35,18 +36,19 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import com.emanuelef.remote_capture.R;
|
||||
import com.emanuelef.remote_capture.Utils;
|
||||
import com.emanuelef.remote_capture.model.MitmAddon;
|
||||
import com.emanuelef.remote_capture.interfaces.MitmListener;
|
||||
import com.emanuelef.remote_capture.MitmAddon;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
public class InstallCertificate extends StepFragment {
|
||||
public class InstallCertificate extends StepFragment implements MitmListener {
|
||||
private static final String TAG = "InstallCertificate";
|
||||
private MitmAddon mAddon;
|
||||
private String mCaPem;
|
||||
private X509Certificate mCaCert;
|
||||
|
||||
private final ActivityResultLauncher<Intent> mitmCtrlLauncher =
|
||||
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), this::mitmCtrlResult);
|
||||
private final ActivityResultLauncher<Intent> certFileLauncher =
|
||||
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), this::certFileResult);
|
||||
|
||||
@ -57,15 +59,15 @@ public class InstallCertificate extends StepFragment {
|
||||
mStepButton.setText(R.string.export_action);
|
||||
mStepButton.setEnabled(false);
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setClassName(MitmAddon.PACKAGE_NAME, MitmAddon.CONTROL_ACTIVITY);
|
||||
intent.putExtra(MitmAddon.ACTION_EXTRA, MitmAddon.ACTION_GET_CA_CERTIFICATE);
|
||||
mAddon = new MitmAddon(requireContext(), this);
|
||||
if(!mAddon.connect(0))
|
||||
certFail();
|
||||
}
|
||||
|
||||
try {
|
||||
mitmCtrlLauncher.launch(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
mStepLabel.setText(R.string.no_intent_handler_found);
|
||||
}
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
mAddon.disconnect();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -76,28 +78,6 @@ public class InstallCertificate extends StepFragment {
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
private void mitmCtrlResult(final ActivityResult result) {
|
||||
if((result.getResultCode() == Activity.RESULT_OK) && (result.getData() != null)) {
|
||||
Intent res = result.getData();
|
||||
mCaPem = res.getStringExtra(MitmAddon.CERTIFICATE_RESULT);
|
||||
|
||||
if(mCaPem != null) {
|
||||
//Log.d(TAG, "certificate: " + cert_str);
|
||||
mCaCert = Utils.x509FromPem(mCaPem);
|
||||
|
||||
if(mCaCert != null) {
|
||||
if(Utils.isCAInstalled(mCaCert))
|
||||
certOk();
|
||||
else {
|
||||
MitmAddon.setDecryptionSetupDone(requireContext(), false);
|
||||
installCaCertificate();
|
||||
}
|
||||
} else
|
||||
certFail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void certOk() {
|
||||
MitmAddon.setDecryptionSetupDone(requireContext(), true);
|
||||
mStepLabel.setText(R.string.cert_installed_correctly);
|
||||
@ -154,4 +134,38 @@ public class InstallCertificate extends StepFragment {
|
||||
Utils.showToastLong(ctx, R.string.cert_exported_now_installed);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMitmGetCaCertificateResult(@Nullable String ca_pem) {
|
||||
mAddon.disconnect();
|
||||
mCaPem = ca_pem;
|
||||
|
||||
if(mCaPem != null) {
|
||||
Log.d(TAG, "Got certificate");
|
||||
//Log.d(TAG, "certificate: " + cert_str);
|
||||
mCaCert = Utils.x509FromPem(mCaPem);
|
||||
|
||||
if(mCaCert != null) {
|
||||
if(Utils.isCAInstalled(mCaCert))
|
||||
certOk();
|
||||
else {
|
||||
MitmAddon.setDecryptionSetupDone(requireContext(), false);
|
||||
installCaCertificate();
|
||||
}
|
||||
} else
|
||||
certFail();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMitmServiceConnect() {
|
||||
if(!mAddon.requestCaCertificate())
|
||||
certFail();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMitmServiceDisconnect() {
|
||||
if(mCaPem == null)
|
||||
certFail();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* This file is part of PCAPdroid.
|
||||
*
|
||||
* PCAPdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* PCAPdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with PCAPdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2020-21 - Emanuele Faranda
|
||||
*/
|
||||
|
||||
package com.emanuelef.remote_capture.interfaces;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface MitmListener {
|
||||
void onMitmGetCaCertificateResult(@Nullable String ca_pem);
|
||||
void onMitmServiceConnect();
|
||||
void onMitmServiceDisconnect();
|
||||
}
|
||||
@ -1,78 +0,0 @@
|
||||
/*
|
||||
* This file is part of PCAPdroid.
|
||||
*
|
||||
* PCAPdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* PCAPdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with PCAPdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2022 - Emanuele Faranda
|
||||
*/
|
||||
|
||||
package com.emanuelef.remote_capture.model;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
public class MitmAddon {
|
||||
public static final String PACKAGE_NAME = "com.pcapdroid.mitm";
|
||||
public static final String MITM_PERMISSION = "com.pcapdroid.permission.MITM";
|
||||
|
||||
public static final String MITM_SERVICE = PACKAGE_NAME + ".MitmService";
|
||||
public static final int MSG_START_MITM = 1;
|
||||
|
||||
public static final String CONTROL_ACTIVITY = PACKAGE_NAME + ".MitmCtrl";
|
||||
public static final String ACTION_EXTRA = "action";
|
||||
public static final String ACTION_GET_CA_CERTIFICATE = "getCAcert";
|
||||
public static final String CERTIFICATE_RESULT = "certificate";
|
||||
|
||||
public static boolean isInstalled(Context ctx) {
|
||||
try {
|
||||
ctx.getPackageManager().getPackageInfo(PACKAGE_NAME, 0);
|
||||
return true;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasMitmPermission(Context ctx) {
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
return ctx.checkSelfPermission(MitmAddon.MITM_PERMISSION) == PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void setDecryptionSetupDone(Context ctx, boolean done) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||
prefs.edit()
|
||||
.putBoolean(Prefs.PREF_TLS_DECRYPTION_SETUP_DONE, done)
|
||||
.apply();
|
||||
}
|
||||
|
||||
public static boolean needsSetup(Context ctx) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||
|
||||
if(!Prefs.isTLSDecryptionSetupDone(prefs))
|
||||
return true;
|
||||
|
||||
// Perform some other quick checks just in case the env has changed
|
||||
if(!isInstalled(ctx) || !hasMitmPermission(ctx)) {
|
||||
setDecryptionSetupDone(ctx, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -279,5 +279,6 @@
|
||||
<string name="ca_installation_failed">CA certificate installation failed</string>
|
||||
<string name="cert_exported_now_installed">Certificate exported, now install it from the Android settings</string>
|
||||
<string name="cert_installed_correctly">The CA certificate is installed</string>
|
||||
<string name="cert_reinstall_required">The CA certificate is not installed, run the mitm setup wizard</string>
|
||||
<string name="done">Done</string>
|
||||
</resources>
|
||||
Loading…
Reference in New Issue
Block a user