Merge pull request #883 from SamWhited/ciphers
Harden TLS cipher suites
This commit is contained in:
		
						commit
						df742ce1e1
					
				| @ -29,6 +29,32 @@ public final class Config { | |||||||
| 	public static final long MAM_MAX_CATCHUP =  MILLISECONDS_IN_DAY / 2; | 	public static final long MAM_MAX_CATCHUP =  MILLISECONDS_IN_DAY / 2; | ||||||
| 	public static final int MAM_MAX_MESSAGES = 500; | 	public static final int MAM_MAX_MESSAGES = 500; | ||||||
| 
 | 
 | ||||||
|  | 	public static final String ENABLED_CIPHERS[] = { | ||||||
|  | 		"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", | ||||||
|  | 		"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384", | ||||||
|  | 		"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA256", | ||||||
|  | 		"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", | ||||||
|  | 		"TLS_ECDHE_RSA_AES_128_SHA", | ||||||
|  | 		"TLS_ECDHE_RSA_AES_256_SHA", | ||||||
|  | 
 | ||||||
|  | 		"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", | ||||||
|  | 		"TLS_DHE_RSA_WITH_AES_128_GCM_SHA384", | ||||||
|  | 		"TLS_DHE_RSA_WITH_AES_256_GCM_SHA256", | ||||||
|  | 		"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", | ||||||
|  | 
 | ||||||
|  | 		"TLS_DHE_RSA_WITH_CAMELLIA_256_SHA", | ||||||
|  | 
 | ||||||
|  | 		// Fallback. | ||||||
|  | 		"TLS_RSA_WITH_AES_128_GCM_SHA256", | ||||||
|  | 		"TLS_RSA_WITH_AES_128_GCM_SHA384", | ||||||
|  | 		"TLS_RSA_WITH_AES_256_GCM_SHA256", | ||||||
|  | 		"TLS_RSA_WITH_AES_256_GCM_SHA384", | ||||||
|  | 		"TLS_RSA_WITH_AES_128_CBC_SHA256", | ||||||
|  | 		"TLS_RSA_WITH_AES_128_CBC_SHA384", | ||||||
|  | 		"TLS_RSA_WITH_AES_256_CBC_SHA256", | ||||||
|  | 		"TLS_RSA_WITH_AES_256_CBC_SHA384" | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| 	private Config() { | 	private Config() { | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -20,6 +20,7 @@ import javax.net.ssl.HostnameVerifier; | |||||||
| import javax.net.ssl.HttpsURLConnection; | import javax.net.ssl.HttpsURLConnection; | ||||||
| import javax.net.ssl.SSLContext; | import javax.net.ssl.SSLContext; | ||||||
| import javax.net.ssl.SSLHandshakeException; | import javax.net.ssl.SSLHandshakeException; | ||||||
|  | import javax.net.ssl.SSLSocketFactory; | ||||||
| import javax.net.ssl.X509TrustManager; | import javax.net.ssl.X509TrustManager; | ||||||
| 
 | 
 | ||||||
| import eu.siacs.conversations.Config; | import eu.siacs.conversations.Config; | ||||||
| @ -90,7 +91,7 @@ public class HttpConnection implements Downloadable { | |||||||
| 			if (this.message.getEncryption() == Message.ENCRYPTION_OTR | 			if (this.message.getEncryption() == Message.ENCRYPTION_OTR | ||||||
| 					&& this.file.getKey() == null) { | 					&& this.file.getKey() == null) { | ||||||
| 				this.message.setEncryption(Message.ENCRYPTION_NONE); | 				this.message.setEncryption(Message.ENCRYPTION_NONE); | ||||||
| 			} | 					} | ||||||
| 			checkFileSize(false); | 			checkFileSize(false); | ||||||
| 		} catch (MalformedURLException e) { | 		} catch (MalformedURLException e) { | ||||||
| 			this.cancel(); | 			this.cancel(); | ||||||
| @ -124,33 +125,39 @@ public class HttpConnection implements Downloadable { | |||||||
| 		mXmppConnectionService.updateConversationUi(); | 		mXmppConnectionService.updateConversationUi(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private void setupTrustManager(HttpsURLConnection connection, | 	private void setupTrustManager(final HttpsURLConnection connection, | ||||||
| 								   boolean interactive) { | 			final boolean interactive) { | ||||||
| 		X509TrustManager trustManager; | 		final X509TrustManager trustManager; | ||||||
| 		HostnameVerifier hostnameVerifier; | 		final HostnameVerifier hostnameVerifier; | ||||||
| 		if (interactive) { | 		if (interactive) { | ||||||
| 			trustManager = mXmppConnectionService.getMemorizingTrustManager(); | 			trustManager = mXmppConnectionService.getMemorizingTrustManager(); | ||||||
| 			hostnameVerifier = mXmppConnectionService | 			hostnameVerifier = mXmppConnectionService | ||||||
| 					.getMemorizingTrustManager().wrapHostnameVerifier( | 				.getMemorizingTrustManager().wrapHostnameVerifier( | ||||||
| 							new StrictHostnameVerifier()); | 						new StrictHostnameVerifier()); | ||||||
| 		} else { | 		} else { | ||||||
| 			trustManager = mXmppConnectionService.getMemorizingTrustManager() | 			trustManager = mXmppConnectionService.getMemorizingTrustManager() | ||||||
| 					.getNonInteractive(); | 				.getNonInteractive(); | ||||||
| 			hostnameVerifier = mXmppConnectionService | 			hostnameVerifier = mXmppConnectionService | ||||||
| 					.getMemorizingTrustManager() | 				.getMemorizingTrustManager() | ||||||
| 					.wrapHostnameVerifierNonInteractive( | 				.wrapHostnameVerifierNonInteractive( | ||||||
| 							new StrictHostnameVerifier()); | 						new StrictHostnameVerifier()); | ||||||
| 		} | 		} | ||||||
| 		try { | 		try { | ||||||
| 			SSLContext sc = SSLContext.getInstance("TLS"); | 			final SSLContext sc = SSLContext.getInstance("TLS"); | ||||||
| 			sc.init(null, new X509TrustManager[]{trustManager}, | 			sc.init(null, new X509TrustManager[]{trustManager}, | ||||||
| 					mXmppConnectionService.getRNG()); | 					mXmppConnectionService.getRNG()); | ||||||
| 			connection.setSSLSocketFactory(sc.getSocketFactory()); | 
 | ||||||
|  | 			final SSLSocketFactory sf = sc.getSocketFactory(); | ||||||
|  | 			final String[] cipherSuites = CryptoHelper.getSupportedCipherSuites( | ||||||
|  | 					sf.getSupportedCipherSuites()); | ||||||
|  | 			if (cipherSuites.length > 0) { | ||||||
|  | 				sc.getDefaultSSLParameters().setCipherSuites(cipherSuites); | ||||||
|  | 
 | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			connection.setSSLSocketFactory(sf); | ||||||
| 			connection.setHostnameVerifier(hostnameVerifier); | 			connection.setHostnameVerifier(hostnameVerifier); | ||||||
| 		} catch (KeyManagementException e) { | 		} catch (final KeyManagementException | NoSuchAlgorithmException ignored) { | ||||||
| 			return; |  | ||||||
| 		} catch (NoSuchAlgorithmException e) { |  | ||||||
| 			return; |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -188,24 +195,24 @@ public class HttpConnection implements Downloadable { | |||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		private long retrieveFileSize() throws IOException, | 		private long retrieveFileSize() throws IOException, | ||||||
| 				SSLHandshakeException { | 						SSLHandshakeException { | ||||||
| 			changeStatus(STATUS_CHECKING); | 							changeStatus(STATUS_CHECKING); | ||||||
| 			HttpURLConnection connection = (HttpURLConnection) mUrl | 							HttpURLConnection connection = (HttpURLConnection) mUrl | ||||||
| 					.openConnection(); | 								.openConnection(); | ||||||
| 			connection.setRequestMethod("HEAD"); | 							connection.setRequestMethod("HEAD"); | ||||||
| 			if (connection instanceof HttpsURLConnection) { | 							if (connection instanceof HttpsURLConnection) { | ||||||
| 				setupTrustManager((HttpsURLConnection) connection, interactive); | 								setupTrustManager((HttpsURLConnection) connection, interactive); | ||||||
| 			} | 							} | ||||||
| 			connection.connect(); | 							connection.connect(); | ||||||
| 			String contentLength = connection.getHeaderField("Content-Length"); | 							String contentLength = connection.getHeaderField("Content-Length"); | ||||||
| 			if (contentLength == null) { | 							if (contentLength == null) { | ||||||
| 				throw new IOException(); | 								throw new IOException(); | ||||||
| 			} | 							} | ||||||
| 			try { | 							try { | ||||||
| 				return Long.parseLong(contentLength, 10); | 								return Long.parseLong(contentLength, 10); | ||||||
| 			} catch (NumberFormatException e) { | 							} catch (NumberFormatException e) { | ||||||
| 				throw new IOException(); | 								throw new IOException(); | ||||||
| 			} | 							} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| @ -234,7 +241,7 @@ public class HttpConnection implements Downloadable { | |||||||
| 
 | 
 | ||||||
| 		private void download() throws SSLHandshakeException, IOException { | 		private void download() throws SSLHandshakeException, IOException { | ||||||
| 			HttpURLConnection connection = (HttpURLConnection) mUrl | 			HttpURLConnection connection = (HttpURLConnection) mUrl | ||||||
| 					.openConnection(); | 				.openConnection(); | ||||||
| 			if (connection instanceof HttpsURLConnection) { | 			if (connection instanceof HttpsURLConnection) { | ||||||
| 				setupTrustManager((HttpsURLConnection) connection, interactive); | 				setupTrustManager((HttpsURLConnection) connection, interactive); | ||||||
| 			} | 			} | ||||||
|  | |||||||
| @ -2,12 +2,17 @@ package eu.siacs.conversations.utils; | |||||||
| 
 | 
 | ||||||
| import java.security.SecureRandom; | import java.security.SecureRandom; | ||||||
| import java.text.Normalizer; | import java.text.Normalizer; | ||||||
|  | import java.util.Arrays; | ||||||
|  | import java.util.Collection; | ||||||
|  | import java.util.LinkedHashSet; | ||||||
| 
 | 
 | ||||||
| public class CryptoHelper { | import eu.siacs.conversations.Config; | ||||||
|  | 
 | ||||||
|  | public final class CryptoHelper { | ||||||
| 	public static final String FILETRANSFER = "?FILETRANSFERv1:"; | 	public static final String FILETRANSFER = "?FILETRANSFERv1:"; | ||||||
| 	final protected static char[] hexArray = "0123456789abcdef".toCharArray(); | 	private final static char[] hexArray = "0123456789abcdef".toCharArray(); | ||||||
| 	final protected static char[] vowels = "aeiou".toCharArray(); | 	private final static char[] vowels = "aeiou".toCharArray(); | ||||||
| 	final protected static char[] consonants = "bcdfghjklmnpqrstvwxyz".toCharArray(); | 	private final static char[] consonants = "bcdfghjklmnpqrstvwxyz".toCharArray(); | ||||||
| 	final public static byte[] ONE = new byte[] { 0, 0, 0, 1 }; | 	final public static byte[] ONE = new byte[] { 0, 0, 0, 1 }; | ||||||
| 
 | 
 | ||||||
| 	public static String bytesToHex(byte[] bytes) { | 	public static String bytesToHex(byte[] bytes) { | ||||||
| @ -45,7 +50,7 @@ public class CryptoHelper { | |||||||
| 		return randomWord(3, random) + "." + randomWord(7, random); | 		return randomWord(3, random) + "." + randomWord(7, random); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	protected static String randomWord(int lenght, SecureRandom random) { | 	private static String randomWord(int lenght, SecureRandom random) { | ||||||
| 		StringBuilder builder = new StringBuilder(lenght); | 		StringBuilder builder = new StringBuilder(lenght); | ||||||
| 		for (int i = 0; i < lenght; ++i) { | 		for (int i = 0; i < lenght; ++i) { | ||||||
| 			if (i % 2 == 0) { | 			if (i % 2 == 0) { | ||||||
| @ -91,4 +96,10 @@ public class CryptoHelper { | |||||||
| 		builder.insert(35, " "); | 		builder.insert(35, " "); | ||||||
| 		return builder.toString(); | 		return builder.toString(); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	public static String[] getSupportedCipherSuites(final String[] platformSupportedCipherSuites) { | ||||||
|  | 		final Collection<String> cipherSuites = new LinkedHashSet<>(Arrays.asList(Config.ENABLED_CIPHERS)); | ||||||
|  | 		cipherSuites.retainAll(Arrays.asList(platformSupportedCipherSuites)); | ||||||
|  | 		return cipherSuites.toArray(new String[cipherSuites.size()]); | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -53,6 +53,7 @@ import eu.siacs.conversations.crypto.sasl.ScramSha1; | |||||||
| import eu.siacs.conversations.entities.Account; | import eu.siacs.conversations.entities.Account; | ||||||
| import eu.siacs.conversations.generator.IqGenerator; | import eu.siacs.conversations.generator.IqGenerator; | ||||||
| import eu.siacs.conversations.services.XmppConnectionService; | import eu.siacs.conversations.services.XmppConnectionService; | ||||||
|  | import eu.siacs.conversations.utils.CryptoHelper; | ||||||
| import eu.siacs.conversations.utils.DNSHelper; | import eu.siacs.conversations.utils.DNSHelper; | ||||||
| import eu.siacs.conversations.utils.Xmlns; | import eu.siacs.conversations.utils.Xmlns; | ||||||
| import eu.siacs.conversations.xml.Element; | import eu.siacs.conversations.xml.Element; | ||||||
| @ -514,6 +515,12 @@ public class XmppConnection implements Runnable { | |||||||
| 				supportedProtocols.remove("SSLv3"); | 				supportedProtocols.remove("SSLv3"); | ||||||
| 				supportProtocols = new String[supportedProtocols.size()]; | 				supportProtocols = new String[supportedProtocols.size()]; | ||||||
| 				supportedProtocols.toArray(supportProtocols); | 				supportedProtocols.toArray(supportProtocols); | ||||||
|  | 
 | ||||||
|  | 				final String[] cipherSuites = CryptoHelper.getSupportedCipherSuites( | ||||||
|  | 						sslSocket.getSupportedCipherSuites()); | ||||||
|  | 				if (cipherSuites.length > 0) { | ||||||
|  | 					sslSocket.setEnabledCipherSuites(cipherSuites); | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 			sslSocket.setEnabledProtocols(supportProtocols); | 			sslSocket.setEnabledProtocols(supportProtocols); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -274,7 +274,7 @@ | |||||||
|     <string name="pref_dont_save_encrypted">Don’t save encrypted messages</string> |     <string name="pref_dont_save_encrypted">Don’t save encrypted messages</string> | ||||||
|     <string name="pref_dont_save_encrypted_summary">Warning: This could lead to message loss</string> |     <string name="pref_dont_save_encrypted_summary">Warning: This could lead to message loss</string> | ||||||
|     <string name="pref_enable_legacy_ssl">Enable legacy SSL</string> |     <string name="pref_enable_legacy_ssl">Enable legacy SSL</string> | ||||||
|     <string name="pref_enable_legacy_ssl_summary">Enables SSLv3 support for legacy servers. Warning: SSLv3 is considered insecure.</string> |     <string name="pref_enable_legacy_ssl_summary">Enables legacy SSLv3 support and insecure SSL ciphers.</string> | ||||||
|     <string name="pref_expert_options">Expert options</string> |     <string name="pref_expert_options">Expert options</string> | ||||||
|     <string name="pref_expert_options_summary">Please be careful with these</string> |     <string name="pref_expert_options_summary">Please be careful with these</string> | ||||||
|     <string name="title_activity_about">About Conversations</string> |     <string name="title_activity_about">About Conversations</string> | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Daniel Gultsch
						Daniel Gultsch