Change TrustManager to return list of "accepted issuers"

This commit is contained in:
cketti 2017-08-12 02:35:52 +02:00
parent 0f1bc05eef
commit 51839cc51f
6 changed files with 97 additions and 27 deletions

View file

@ -0,0 +1,61 @@
package com.fsck.k9.mail.helpers;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.X509Certificate;
public class KeyStoreProvider {
private static final String KEYSTORE_PASSWORD = "password";
private static final String KEYSTORE_RESOURCE = "/keystore.jks";
private static final String SERVER_CERTIFICATE_ALIAS = "mockimapserver";
private final KeyStore keyStore;
public static KeyStoreProvider getInstance() {
KeyStore keyStore = loadKeyStore();
return new KeyStoreProvider(keyStore);
}
private static KeyStore loadKeyStore() {
try {
KeyStore keyStore = KeyStore.getInstance("JKS");
InputStream keyStoreInputStream = KeyStoreProvider.class.getResourceAsStream(KEYSTORE_RESOURCE);
try {
keyStore.load(keyStoreInputStream, KEYSTORE_PASSWORD.toCharArray());
} finally {
keyStoreInputStream.close();
}
return keyStore;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private KeyStoreProvider(KeyStore keyStore) {
this.keyStore = keyStore;
}
public KeyStore getKeyStore() {
return keyStore;
}
public char[] getPassword() {
return KEYSTORE_PASSWORD.toCharArray();
}
public X509Certificate getServerCertificate() {
try {
KeyStore keyStore = loadKeyStore();
return (X509Certificate) keyStore.getCertificate(SERVER_CERTIFICATE_ALIAS);
} catch (KeyStoreException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -5,6 +5,7 @@ import java.io.IOException;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.ssl.TrustedSocketFactory;
@ -14,11 +15,23 @@ import javax.net.ssl.TrustManager;
public class TestTrustedSocketFactory implements TrustedSocketFactory {
private final X509Certificate serverCertificate;
public static TestTrustedSocketFactory newInstance() {
X509Certificate serverCertificate = KeyStoreProvider.getInstance().getServerCertificate();
return new TestTrustedSocketFactory(serverCertificate);
}
private TestTrustedSocketFactory(X509Certificate serverCertificate) {
this.serverCertificate = serverCertificate;
}
@Override
public Socket createSocket(Socket socket, String host, int port, String clientCertificateAlias)
throws NoSuchAlgorithmException, KeyManagementException, MessagingException, IOException {
TrustManager[] trustManagers = new TrustManager[] { new VeryTrustingTrustManager() };
TrustManager[] trustManagers = new TrustManager[] { new VeryTrustingTrustManager(serverCertificate) };
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagers, null);

View file

@ -10,6 +10,13 @@ import javax.net.ssl.X509TrustManager;
@SuppressLint("TrustAllX509TrustManager")
class VeryTrustingTrustManager implements X509TrustManager {
private final X509Certificate serverCertificate;
public VeryTrustingTrustManager(X509Certificate serverCertificate) {
this.serverCertificate = serverCertificate;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// Accept all certificates
@ -22,7 +29,7 @@ class VeryTrustingTrustManager implements X509TrustManager {
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
return new X509Certificate[] { serverCertificate };
}
}

View file

@ -62,7 +62,7 @@ public class ImapConnectionTest {
public void setUp() throws Exception {
connectivityManager = mock(ConnectivityManager.class);
oAuth2TokenProvider = createOAuth2TokenProvider();
socketFactory = new TestTrustedSocketFactory();
socketFactory = TestTrustedSocketFactory.newInstance();
settings = new SimpleImapSettings();
settings.setUsername(USERNAME);

View file

@ -23,6 +23,7 @@ import java.util.zip.InflaterInputStream;
import android.annotation.SuppressLint;
import com.fsck.k9.mail.helpers.KeyStoreProvider;
import com.jcraft.jzlib.JZlib;
import com.jcraft.jzlib.ZOutputStream;
import javax.net.ssl.KeyManagerFactory;
@ -37,15 +38,13 @@ import org.apache.commons.io.IOUtils;
@SuppressLint("NewApi")
public class MockImapServer {
private static final String KEYSTORE_PASSWORD = "password";
private static final String KEYSTORE_RESOURCE = "/keystore.jks";
private static final byte[] CRLF = { '\r', '\n' };
private final Deque<ImapInteraction> interactions = new ConcurrentLinkedDeque<>();
private final CountDownLatch waitForConnectionClosed = new CountDownLatch(1);
private final CountDownLatch waitForAllExpectedCommands = new CountDownLatch(1);
private final KeyStoreProvider keyStoreProvider;
private final Logger logger;
private MockServerThread mockServerThread;
@ -54,10 +53,11 @@ public class MockImapServer {
public MockImapServer() {
this(new DefaultLogger());
this(KeyStoreProvider.getInstance(), new DefaultLogger());
}
public MockImapServer(Logger logger) {
public MockImapServer(KeyStoreProvider keyStoreProvider, Logger logger) {
this.keyStoreProvider = keyStoreProvider;
this.logger = logger;
}
@ -96,7 +96,7 @@ public class MockImapServer {
port = serverSocket.getLocalPort();
mockServerThread = new MockServerThread(serverSocket, interactions, waitForConnectionClosed,
waitForAllExpectedCommands, logger);
waitForAllExpectedCommands, logger, keyStoreProvider);
mockServerThread.start();
}
@ -236,6 +236,7 @@ public class MockImapServer {
private final CountDownLatch waitForConnectionClosed;
private final CountDownLatch waitForAllExpectedCommands;
private final Logger logger;
private final KeyStoreProvider keyStoreProvider;
private volatile boolean shouldStop = false;
private volatile Socket clientSocket;
@ -246,13 +247,15 @@ public class MockImapServer {
public MockServerThread(ServerSocket serverSocket, Deque<ImapInteraction> interactions,
CountDownLatch waitForConnectionClosed, CountDownLatch waitForAllExpectedCommands, Logger logger) {
CountDownLatch waitForConnectionClosed, CountDownLatch waitForAllExpectedCommands, Logger logger,
KeyStoreProvider keyStoreProvider) {
super("MockImapServer");
this.serverSocket = serverSocket;
this.interactions = interactions;
this.waitForConnectionClosed = waitForConnectionClosed;
this.waitForAllExpectedCommands = waitForAllExpectedCommands;
this.logger = logger;
this.keyStoreProvider = keyStoreProvider;
}
@Override
@ -356,11 +359,11 @@ public class MockImapServer {
private void upgradeToTls(Socket socket) throws KeyStoreException, IOException, NoSuchAlgorithmException,
CertificateException, UnrecoverableKeyException, KeyManagementException {
KeyStore keyStore = loadKeyStore();
KeyStore keyStore = keyStoreProvider.getKeyStore();
String defaultAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(defaultAlgorithm);
keyManagerFactory.init(keyStore, KEYSTORE_PASSWORD.toCharArray());
keyManagerFactory.init(keyStore, keyStoreProvider.getPassword());
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), null, null);
@ -375,20 +378,6 @@ public class MockImapServer {
output = Okio.buffer(Okio.sink(sslSocket.getOutputStream()));
}
private KeyStore loadKeyStore() throws KeyStoreException, IOException, NoSuchAlgorithmException,
CertificateException {
KeyStore keyStore = KeyStore.getInstance("JKS");
InputStream keyStoreInputStream = getClass().getResourceAsStream(KEYSTORE_RESOURCE);
try {
keyStore.load(keyStoreInputStream, KEYSTORE_PASSWORD.toCharArray());
} finally {
keyStoreInputStream.close();
}
return keyStore;
}
private void readAdditionalCommands() throws IOException {
String command = input.readUtf8Line();
if (command == null) {

View file

@ -52,7 +52,7 @@ public class SmtpTransportTest {
@Before
public void before() throws AuthenticationFailedException {
socketFactory = new TestTrustedSocketFactory();
socketFactory = TestTrustedSocketFactory.newInstance();
oAuth2TokenProvider = mock(OAuth2TokenProvider.class);
when(oAuth2TokenProvider.getToken(eq(USERNAME), anyInt()))
.thenReturn("oldToken").thenReturn("newToken");