From c155cfcf03e095a854eaeaabe87c492ea5a06b5c Mon Sep 17 00:00:00 2001 From: casey langen Date: Tue, 2 May 2017 10:15:58 -0700 Subject: [PATCH] Oops, these changes didn't get merged into the last commit -- added the ability to disable ssl cert validation (useful on a local network). --- .../remote/playback/ExoPlayerWrapper.java | 30 ++++++++-- .../remote/ui/activity/SettingsActivity.java | 19 +++++- .../musikcube/remote/util/NetworkUtil.java | 59 +++++++++++++++++++ .../remote/websocket/WebSocketService.java | 5 ++ .../src/main/res/layout/activity_settings.xml | 9 +++ .../app/src/main/res/values/strings.xml | 3 + 6 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 src/musikdroid/app/src/main/java/io/casey/musikcube/remote/util/NetworkUtil.java diff --git a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/playback/ExoPlayerWrapper.java b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/playback/ExoPlayerWrapper.java index 3250982e4..93c63851a 100644 --- a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/playback/ExoPlayerWrapper.java +++ b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/playback/ExoPlayerWrapper.java @@ -1,6 +1,7 @@ package io.casey.musikcube.remote.playback; import android.content.Context; +import android.content.SharedPreferences; import android.net.Uri; import com.google.android.exoplayer2.ExoPlaybackException; @@ -28,26 +29,40 @@ import com.google.android.exoplayer2.util.Util; import java.io.File; import io.casey.musikcube.remote.Application; +import io.casey.musikcube.remote.util.NetworkUtil; import okhttp3.Cache; import okhttp3.OkHttpClient; public class ExoPlayerWrapper extends PlayerWrapper { private static OkHttpClient audioStreamClient = null; + private static boolean certValidationDisabled = false; + private DefaultBandwidthMeter bandwidth; private DataSource.Factory datasources; private ExtractorsFactory extractors; private MediaSource source; private SimpleExoPlayer player; private boolean prefetch; + private Context context; + private SharedPreferences prefs; public ExoPlayerWrapper() { - final Context c = Application.getInstance(); + this.context = Application.getInstance(); + this.prefs = context.getSharedPreferences("prefs", Context.MODE_PRIVATE); this.bandwidth = new DefaultBandwidthMeter(); final TrackSelection.Factory trackFactory = new AdaptiveTrackSelection.Factory(bandwidth); final TrackSelector trackSelector = new DefaultTrackSelector(trackFactory); - this.player = ExoPlayerFactory.newSimpleInstance(c, trackSelector); + this.player = ExoPlayerFactory.newSimpleInstance(this.context, trackSelector); this.extractors = new DefaultExtractorsFactory(); this.player.addListener(eventListener); + + synchronized (ExoPlayerWrapper.class) { + final boolean disabled = this.prefs.getBoolean("cert_validation_disabled", false); + if (disabled != certValidationDisabled) { + audioStreamClient = null; + certValidationDisabled = disabled; + } + } } private void initDataSourceFactory(final String uri) { @@ -57,9 +72,14 @@ public class ExoPlayerWrapper extends PlayerWrapper { if (audioStreamClient == null) { final File path = new File(context.getExternalCacheDir(), "audio"); - audioStreamClient = new OkHttpClient.Builder() - .cache(new Cache(path, 1048576 * 256)) /* 256 meg cache */ - .build(); + OkHttpClient.Builder builder = new OkHttpClient.Builder() + .cache(new Cache(path, 1048576 * 256)); /* 256 meg cache */ + + if (certValidationDisabled) { + NetworkUtil.disableCertificateValidation(builder); + } + + audioStreamClient = builder.build(); } } diff --git a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/activity/SettingsActivity.java b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/activity/SettingsActivity.java index a8ae6b43d..a6cca7ec7 100644 --- a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/activity/SettingsActivity.java +++ b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/activity/SettingsActivity.java @@ -29,7 +29,7 @@ import io.casey.musikcube.remote.websocket.WebSocketService; public class SettingsActivity extends AppCompatActivity { private EditText addressText, portText, httpPortText, passwordText; private CheckBox albumArtCheckbox, messageCompressionCheckbox, softwareVolume; - private CheckBox sslCheckbox; + private CheckBox sslCheckbox, certCheckbox; private Spinner playbackModeSpinner; private SharedPreferences prefs; @@ -74,6 +74,7 @@ public class SettingsActivity extends AppCompatActivity { this.albumArtCheckbox.setChecked(this.prefs.getBoolean("album_art_enabled", true)); this.messageCompressionCheckbox.setChecked(this.prefs.getBoolean("message_compression_enabled", true)); this.softwareVolume.setChecked(this.prefs.getBoolean("software_volume", false)); + this.certCheckbox.setChecked(this.prefs.getBoolean("cert_validation_disabled", false)); Views.setCheckWithoutEvent( this.sslCheckbox, @@ -91,6 +92,10 @@ public class SettingsActivity extends AppCompatActivity { return this.playbackModeSpinner.getSelectedItemPosition() == 1; } + private void onDisableSslFromDialog() { + Views.setCheckWithoutEvent(this.sslCheckbox, false, sslCheckChanged); + } + private CheckBox.OnCheckedChangeListener sslCheckChanged = (button, value) -> { if (value) { if (getSupportFragmentManager().findFragmentByTag(SslAlertDialog.TAG) == null) { @@ -109,6 +114,7 @@ public class SettingsActivity extends AppCompatActivity { this.softwareVolume = (CheckBox) findViewById(R.id.software_volume); this.playbackModeSpinner = (Spinner) findViewById(R.id.playback_mode_spinner); this.sslCheckbox = (CheckBox) findViewById(R.id.ssl_checkbox); + this.certCheckbox = (CheckBox) findViewById(R.id.cert_validation); final boolean wasStreaming = isStreamingEnabled(); @@ -128,6 +134,7 @@ public class SettingsActivity extends AppCompatActivity { .putBoolean("streaming_playback", isStreamingSelected()) .putBoolean("software_volume", softwareVolume.isChecked()) .putBoolean("ssl_enabled", sslCheckbox.isChecked()) + .putBoolean("cert_validation_disabled", certCheckbox.isChecked()) .apply(); if (!softwareVolume.isChecked()) { @@ -154,10 +161,13 @@ public class SettingsActivity extends AppCompatActivity { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - return new AlertDialog.Builder(getActivity()) + final AlertDialog dlg = new AlertDialog.Builder(getActivity()) .setTitle(R.string.settings_ssl_dialog_title) .setMessage(R.string.settings_ssl_dialog_message) - .setPositiveButton(R.string.button_ok, null) + .setPositiveButton(R.string.button_enable, null) + .setNegativeButton(R.string.button_disable, (dialog, which) -> { + ((SettingsActivity) getActivity()).onDisableSslFromDialog(); + }) .setNeutralButton(R.string.button_learn_more, (dialog, which) -> { try { final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(LEARN_MORE_URL)); @@ -167,6 +177,9 @@ public class SettingsActivity extends AppCompatActivity { } }) .create(); + + dlg.setCancelable(false); + return dlg; } } } diff --git a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/util/NetworkUtil.java b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/util/NetworkUtil.java new file mode 100644 index 000000000..f0aeb6cc8 --- /dev/null +++ b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/util/NetworkUtil.java @@ -0,0 +1,59 @@ +package io.casey.musikcube.remote.util; + +import com.neovisionaries.ws.client.WebSocketFactory; + +import java.security.cert.CertificateException; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import okhttp3.OkHttpClient; + +public class NetworkUtil { + private static SSLContext sslContext; + private static SSLSocketFactory sslSocketFactory; + + public static void disableCertificateValidation(final OkHttpClient.Builder okHttpClient) { + init(); + okHttpClient.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]); + okHttpClient.hostnameVerifier((hostname, session) -> true); + } + + public static void disableCertificateValidation(final WebSocketFactory socketFactory) { + socketFactory.setSSLContext(sslContext); + } + + private synchronized static void init() { + try { + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); + sslSocketFactory = sslContext.getSocketFactory(); + } + catch (Exception ex) { + throw new RuntimeException (ex); + } + } + + private static final TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() { + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[]{}; + } + } + }; + + private NetworkUtil() { + + } +} diff --git a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/websocket/WebSocketService.java b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/websocket/WebSocketService.java index 699b8c02d..d03592851 100644 --- a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/websocket/WebSocketService.java +++ b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/websocket/WebSocketService.java @@ -26,6 +26,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; +import io.casey.musikcube.remote.util.NetworkUtil; import io.casey.musikcube.remote.util.Preconditions; import io.reactivex.Observable; import io.reactivex.ObservableEmitter; @@ -497,6 +498,10 @@ public class WebSocketService { try { final WebSocketFactory factory = new WebSocketFactory(); + if (prefs.getBoolean("cert_validation_disabled", false)) { + NetworkUtil.disableCertificateValidation(factory); + } + final String protocol = prefs.getBoolean("ssl_enabled", false) ? "wss" : "ws"; final String host = String.format( diff --git a/src/musikdroid/app/src/main/res/layout/activity_settings.xml b/src/musikdroid/app/src/main/res/layout/activity_settings.xml index a4f69e2ac..4548149dd 100644 --- a/src/musikdroid/app/src/main/res/layout/activity_settings.xml +++ b/src/musikdroid/app/src/main/res/layout/activity_settings.xml @@ -133,6 +133,15 @@ android:layout_marginLeft="24dp" android:text="@string/settings_use_ssl"/> + + save settings close + enable + disable ok learn more invalid password @@ -57,6 +59,7 @@ enable message compression enable streaming playback use software volume while streaming + disable ssl certificate validation ssl information the musikcube server doesn\'t support ssl by default. it does, however, work very well with nginx and ssl termination. press \'learn more\' for configuration information. [unknown artist]