Added option to harden SSL connections

pull/172/head
M66B 5 years ago
parent 19053f6337
commit 208ec02cd3

@ -56,6 +56,7 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
private Spinner spDownload;
private SwitchCompat swRoaming;
private SwitchCompat swRlah;
private SwitchCompat swSslHarden;
private SwitchCompat swSocks;
private EditText etSocks;
private Button btnSocks;
@ -64,7 +65,7 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
private TextView tvConnectionRoaming;
private final static String[] RESET_OPTIONS = new String[]{
"metered", "download", "roaming", "rlah", "socks_enabled", "socks_proxy"
"metered", "download", "roaming", "rlah", "ssl_harden", "socks_enabled", "socks_proxy"
};
@Override
@ -81,6 +82,7 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
spDownload = view.findViewById(R.id.spDownload);
swRoaming = view.findViewById(R.id.swRoaming);
swRlah = view.findViewById(R.id.swRlah);
swSslHarden = view.findViewById(R.id.swSslHarden);
swSocks = view.findViewById(R.id.swSocks);
etSocks = view.findViewById(R.id.etSocks);
btnSocks = view.findViewById(R.id.btnSocks);
@ -129,6 +131,13 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
}
});
swSslHarden.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
prefs.edit().putBoolean("ssl_harden", checked).apply();
}
});
swSocks.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
@ -246,6 +255,7 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
swRoaming.setChecked(prefs.getBoolean("roaming", true));
swRlah.setChecked(prefs.getBoolean("rlah", true));
swSslHarden.setChecked(prefs.getBoolean("ssl_harden", false));
swSocks.setChecked(prefs.getBoolean("socks_enabled", false));
etSocks.setText(prefs.getString("socks_proxy", null));
etSocks.setEnabled(swSocks.isChecked());

@ -4,7 +4,6 @@ import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.text.TextUtils;
import androidx.annotation.NonNull;
@ -49,6 +48,7 @@ import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Semaphore;
import java.util.regex.Pattern;
import javax.mail.AuthenticationFailedException;
import javax.mail.Folder;
@ -58,9 +58,7 @@ import javax.mail.Service;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.event.StoreListener;
import javax.net.ssl.SNIHostName;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
@ -71,6 +69,7 @@ public class MailService implements AutoCloseable {
private Context context;
private String protocol;
private boolean insecure;
private boolean harden;
private boolean useip;
private boolean debug;
private Properties properties;
@ -93,7 +92,12 @@ public class MailService implements AutoCloseable {
private static final int APPEND_BUFFER_SIZE = 4 * 1024 * 1024; // bytes
private static final List<String> SSL_PROTOCOL_BLACKLIST = Collections.unmodifiableList(Arrays.asList(
"SSLv2", "SSLv3", "TLSv1", "TLSv1.1"
));
private MailService() {
// Prevent instantiation
}
MailService(Context context, String protocol, String realm, boolean insecure, boolean check, boolean debug) throws NoSuchProviderException {
@ -105,6 +109,8 @@ public class MailService implements AutoCloseable {
properties = MessageHelper.getSessionProperties();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
this.harden = prefs.getBoolean("ssl_harden", false);
boolean socks_enabled = prefs.getBoolean("socks_enabled", false);
String socks_proxy = prefs.getString("socks_proxy", "localhost:9050");
@ -230,7 +236,7 @@ public class MailService implements AutoCloseable {
String fingerprint) throws MessagingException {
SSLSocketFactoryService factory = null;
try {
factory = new SSLSocketFactoryService(host, insecure, fingerprint);
factory = new SSLSocketFactoryService(host, insecure, harden, fingerprint);
properties.put("mail." + protocol + ".ssl.socketFactory", factory);
properties.put("mail." + protocol + ".socketFactory.fallback", "false");
properties.put("mail." + protocol + ".ssl.checkserveridentity", "false");
@ -533,13 +539,15 @@ public class MailService implements AutoCloseable {
// openssl s_client -connect host:port < /dev/null 2>/dev/null | openssl x509 -fingerprint -noout -in /dev/stdin
private String server;
private boolean secure;
private boolean harden;
private String trustedFingerprint;
private SSLSocketFactory factory;
private X509Certificate certificate;
SSLSocketFactoryService(String host, boolean insecure, String fingerprint) throws GeneralSecurityException {
SSLSocketFactoryService(String host, boolean insecure, boolean harden, String fingerprint) throws GeneralSecurityException {
this.server = host;
this.secure = !insecure;
this.harden = harden;
this.trustedFingerprint = fingerprint;
SSLContext sslContext = SSLContext.getInstance("TLS");
@ -651,19 +659,31 @@ public class MailService implements AutoCloseable {
}
private Socket configure(Socket socket) {
/*
if (socket instanceof SSLSocket
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
SSLParameters params = ((SSLSocket) socket).getSSLParameters();
if (params == null)
params = new SSLParameters();
Log.i("SNI server names=" + params.getServerNames());
params.setEndpointIdentificationAlgorithm("HTTPS");
params.setServerNames(Arrays.asList(new SNIHostName(server)));
Log.i("SNI server name=" + params.getServerNames().get(0));
((SSLSocket) socket).setSSLParameters(params);
if (harden && socket instanceof SSLSocket) {
// https://developer.android.com/reference/javax/net/ssl/SSLSocket.html
SSLSocket sslSocket = (SSLSocket) socket;
List<String> protocols = new ArrayList<>();
for (String protocol : sslSocket.getEnabledProtocols())
if (SSL_PROTOCOL_BLACKLIST.contains(protocol))
Log.i("SSL disabling protocol=" + protocol);
else
protocols.add(protocol);
Log.i("SSL protocols=" + TextUtils.join(",", protocols));
sslSocket.setEnabledProtocols(protocols.toArray(new String[0]));
ArrayList<String> ciphers = new ArrayList<>();
Pattern pattern = Pattern.compile(".*(_DES|DH_|DSS|EXPORT|MD5|NULL|RC4|TLS_FALLBACK_SCSV).*");
for (String cipher : sslSocket.getEnabledCipherSuites()) {
if (pattern.matcher(cipher).matches())
Log.i("SSL disabling cipher=" + cipher);
else
ciphers.add(cipher);
}
Log.i("SSL ciphers=" + TextUtils.join(",", ciphers));
sslSocket.setEnabledCipherSuites(ciphers.toArray(new String[0]));
}
*/
return socket;
}

@ -116,6 +116,7 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences
private static final List<String> PREF_RELOAD = Collections.unmodifiableList(Arrays.asList(
"metered", "roaming", "rlah", // force reconnect
"ssl_harden", // force reconnect
"socks_enabled", "socks_proxy", // force reconnect
"subscribed_only", // force folder sync
"badge", "unseen_ignored", // force update badge/widget

@ -119,6 +119,30 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/swRlah" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/swSslHarden"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:checked="true"
android:text="@string/title_advanced_ssl_harden"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvRlahHint"
app:switchPadding="12dp" />
<TextView
android:id="@+id/tvSslHardenHint"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="48dp"
android:text="@string/title_advanced_ssl_harden_hint"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textStyle="italic"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/swSslHarden" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/swSocks"
android:layout_width="0dp"
@ -128,7 +152,7 @@
android:text="@string/title_advanced_socks"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvRlahHint"
app:layout_constraintTop_toBottomOf="@id/tvSslHardenHint"
app:switchPadding="12dp" />
<EditText

@ -279,6 +279,7 @@
<string name="title_advanced_download">Automatically download messages and attachments on a metered connection up to</string>
<string name="title_advanced_roaming">Download messages and attachments while roaming</string>
<string name="title_advanced_rlah">Roam like at home</string>
<string name="title_advanced_ssl_harden">Harden SSL connections</string>
<string name="title_advanced_socks">Use SOCKS proxy</string>
<string name="title_advanced_manage_connectivity">Manage connectivity</string>
@ -423,6 +424,7 @@
<string name="title_advanced_metered_hint">Metered connections are generally mobile connections or paid Wi-Fi hotspots</string>
<string name="title_advanced_metered_warning">Disabling this option will disable receiving and sending messages on mobile internet connections</string>
<string name="title_advanced_rlah_hint">Assuming no roaming within the EU</string>
<string name="title_advanced_ssl_harden_hint">Enabling this will disable weak SSL protocols and ciphers</string>
<string name="title_advanced_socks_hint">Using a remote proxy server is insecure because proxy connections are not encrypted</string>
<string name="title_advanced_roaming_hint">Messages headers will always be fetched when roaming. You can use the device\'s roaming setting to disable internet while roaming.</string>

Loading…
Cancel
Save