From 74842cba92591aa9fbf64e8c6f39900a68b0c11c Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 19 Oct 2018 23:15:13 +0200 Subject: #8919 update dependencies --- .../java/de/blinkt/openvpn/core/ExtAuthHelper.java | 259 +++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 app/src/main/java/de/blinkt/openvpn/core/ExtAuthHelper.java (limited to 'app/src/main/java/de/blinkt/openvpn/core/ExtAuthHelper.java') diff --git a/app/src/main/java/de/blinkt/openvpn/core/ExtAuthHelper.java b/app/src/main/java/de/blinkt/openvpn/core/ExtAuthHelper.java new file mode 100644 index 00000000..166bce12 --- /dev/null +++ b/app/src/main/java/de/blinkt/openvpn/core/ExtAuthHelper.java @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2012-2018 Arne Schwabe + * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt + */ + +package de.blinkt.openvpn.core; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.*; +import android.security.KeyChainException; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.WorkerThread; +import android.widget.ArrayAdapter; +import android.widget.Spinner; +import android.widget.SpinnerAdapter; +import de.blinkt.openvpn.api.ExternalCertificateProvider; + +import java.io.ByteArrayInputStream; +import java.io.Closeable; +import java.io.UnsupportedEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.*; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +public class ExtAuthHelper { + + public static final String ACTION_CERT_CONFIGURATION = "de.blinkt.openvpn.api.ExternalCertificateConfiguration"; + public static final String ACTION_CERT_PROVIDER = "de.blinkt.openvpn.api.ExternalCertificateProvider"; + + public static final String EXTRA_ALIAS = "de.blinkt.openvpn.api.KEY_ALIAS"; + public static final String EXTRA_DESCRIPTION = "de.blinkt.openvpn.api.KEY_DESCRIPTION"; + + + public static void setExternalAuthProviderSpinnerList(Spinner spinner, String selectedApp) { + Context c = spinner.getContext(); + final PackageManager pm = c.getPackageManager(); + ArrayList extProviders = getExternalAuthProviderList(c); + + int selectedPos = -1; + + if (extProviders.size() ==0) + { + selectedApp = ""; + ExternalAuthProvider noauthprovider = new ExternalAuthProvider(); + noauthprovider.label = "No external auth provider found"; + noauthprovider.packageName = selectedApp; + noauthprovider.configurable = false; + extProviders.add(noauthprovider); + } + + + for (int i = 0; i < extProviders.size(); i++) { + if (extProviders.get(i).packageName.equals(selectedApp)) + selectedPos = i; + } + SpinnerAdapter extAppAdapter = new ArrayAdapter(c, android.R.layout.simple_spinner_item, android.R.id.text1, extProviders); + spinner.setAdapter(extAppAdapter); + if (selectedPos != -1) + spinner.setSelection(selectedPos); + } + + static ArrayList getExternalAuthProviderList(Context c) { + Intent configureExtAuth = new Intent(ACTION_CERT_CONFIGURATION); + + final PackageManager packageManager = c.getPackageManager(); + List configureList = + packageManager.queryIntentActivities(configureExtAuth, 0); + + Intent serviceExtAuth = new Intent(ACTION_CERT_PROVIDER); + + List serviceList = + packageManager.queryIntentServices(serviceExtAuth, 0); + + + // For now only list those who appear in both lists + + ArrayList providers = new ArrayList(); + + for (ResolveInfo service : serviceList) { + ExternalAuthProvider ext = new ExternalAuthProvider(); + ext.packageName = service.serviceInfo.packageName; + + ext.label = (String) service.serviceInfo.applicationInfo.loadLabel(packageManager); + + for (ResolveInfo activity : configureList) { + if (service.serviceInfo.packageName.equals(activity.activityInfo.packageName)) { + ext.configurable = true; + } + } + providers.add(ext); + + } + return providers; + + } + + @Nullable + @WorkerThread + public static byte[] signData(@NonNull Context context, + @NonNull String extAuthPackageName, + @NonNull String alias, + @NonNull byte[] data + ) throws KeyChainException, InterruptedException + + { + + + try (ExternalAuthProviderConnection authProviderConnection = bindToExtAuthProvider(context.getApplicationContext(), extAuthPackageName)) { + ExternalCertificateProvider externalAuthProvider = authProviderConnection.getService(); + return externalAuthProvider.getSignedData(alias, data); + + } catch (RemoteException e) { + throw new KeyChainException(e); + } + } + + @Nullable + @WorkerThread + public static X509Certificate[] getCertificateChain(@NonNull Context context, + @NonNull String extAuthPackageName, + @NonNull String alias) throws KeyChainException { + + final byte[] certificateBytes; + try (ExternalAuthProviderConnection authProviderConnection = bindToExtAuthProvider(context.getApplicationContext(), extAuthPackageName)) { + ExternalCertificateProvider externalAuthProvider = authProviderConnection.getService(); + certificateBytes = externalAuthProvider.getCertificateChain(alias); + if (certificateBytes == null) { + return null; + } + Collection chain = toCertificates(certificateBytes); + return chain.toArray(new X509Certificate[chain.size()]); + + } catch (RemoteException | RuntimeException | InterruptedException e) { + throw new KeyChainException(e); + } + } + + public static Bundle getCertificateMetaData(@NonNull Context context, + @NonNull String extAuthPackageName, + String alias) throws KeyChainException + { + try (ExternalAuthProviderConnection authProviderConnection = bindToExtAuthProvider(context.getApplicationContext(), extAuthPackageName)) { + ExternalCertificateProvider externalAuthProvider = authProviderConnection.getService(); + return externalAuthProvider.getCertificateMetaData(alias); + + } catch (RemoteException | RuntimeException | InterruptedException e) { + throw new KeyChainException(e); + } + } + + public static Collection toCertificates(@NonNull byte[] bytes) { + final String BEGINCERT = "-----BEGIN CERTIFICATE-----"; + try { + Vector retCerts = new Vector<>(); + // Java library is broken, although the javadoc says it will extract all certificates from a byte array + // it only extracts the first one + String allcerts = new String(bytes, "iso8859-1"); + String[] certstrings = allcerts.split(BEGINCERT); + for (String certstring: certstrings) { + certstring = BEGINCERT + certstring; + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + retCerts.addAll((Collection) certFactory.generateCertificates( + new ByteArrayInputStream((certstring.getBytes("iso8859-1"))))); + + } + return retCerts; + + } catch (CertificateException e) { + throw new AssertionError(e); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); + } + } + + // adapted form Keychain + @WorkerThread + public static ExternalAuthProviderConnection bindToExtAuthProvider(@NonNull Context context, String packagename) throws KeyChainException, InterruptedException { + ensureNotOnMainThread(context); + final BlockingQueue q = new LinkedBlockingQueue<>(1); + ServiceConnection extAuthServiceConnection = new ServiceConnection() { + volatile boolean mConnectedAtLeastOnce = false; + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + if (!mConnectedAtLeastOnce) { + mConnectedAtLeastOnce = true; + try { + q.put(ExternalCertificateProvider.Stub.asInterface(service)); + } catch (InterruptedException e) { + // will never happen, since the queue starts with one available slot + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + } + }; + Intent intent = new Intent(ACTION_CERT_PROVIDER); + intent.setPackage(packagename); + + if (!context.bindService(intent, extAuthServiceConnection, Context.BIND_AUTO_CREATE)) { + throw new KeyChainException("could not bind to external authticator app: " + packagename); + } + return new ExternalAuthProviderConnection(context, extAuthServiceConnection, q.take()); + } + + private static void ensureNotOnMainThread(@NonNull Context context) { + Looper looper = Looper.myLooper(); + if (looper != null && looper == context.getMainLooper()) { + throw new IllegalStateException( + "calling this from your main thread can lead to deadlock"); + } + } + + public static class ExternalAuthProvider { + + public String packageName; + public boolean configurable = false; + private String label; + + @Override + public String toString() { + return label; + } + } + + public static class ExternalAuthProviderConnection implements Closeable { + private final Context context; + private final ServiceConnection serviceConnection; + private final ExternalCertificateProvider service; + + protected ExternalAuthProviderConnection(Context context, + ServiceConnection serviceConnection, + ExternalCertificateProvider service) { + this.context = context; + this.serviceConnection = serviceConnection; + this.service = service; + } + + @Override + public void close() { + context.unbindService(serviceConnection); + } + + public ExternalCertificateProvider getService() { + return service; + } + } +} -- cgit v1.2.3