Skip to content

Commit

Permalink
Added an ability to provide additional key stores to be used in the
Browse files Browse the repository at this point in the history
redmine authentication process.
  • Loading branch information
Maxim Karvonen authored and Maxim Karvonen committed Oct 10, 2015
1 parent cc3041c commit eac07d1
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 1 deletion.
3 changes: 2 additions & 1 deletion .gitignore
Expand Up @@ -3,9 +3,10 @@
.idea
build
target
bin

/.settings/**
/.classpath
/.project
/.settings
/.nb-gradle/**
/.nb-gradle/**
Expand Up @@ -4,10 +4,12 @@
import com.taskadapter.redmineapi.internal.URIConfigurator;

import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Collection;

import org.apache.http.HttpHost;
import org.apache.http.HttpVersion;
Expand All @@ -26,6 +28,7 @@
import org.apache.http.protocol.HTTP;

import com.taskadapter.redmineapi.internal.comm.ConnectionEvictor;
import com.taskadapter.redmineapi.internal.comm.betterssl.BetterSSLFactory;
import com.taskadapter.redmineapi.internal.comm.naivessl.NaiveSSLFactory;

/**
Expand Down Expand Up @@ -143,14 +146,28 @@ public static RedmineManager createWithUserAuth(String uri, String login,
* Creates default insecure connection manager.
*
* @return default insecure connection manager.
* @deprecated Use better key-managed factory with additional keystores.
*/
@Deprecated
public static PoolingClientConnectionManager createInsecureConnectionManager()
throws KeyStoreException, NoSuchAlgorithmException,
CertificateException, KeyManagementException,
UnrecoverableKeyException {
return createConnectionManager(Integer.MAX_VALUE,
NaiveSSLFactory.createNaiveSSLSocketFactory());
}

/**
* Creates a connection manager with extended trust relations. It would
* use both default system trusted certificates as well as all certificates
* defined in the <code>trustStores</code>.
* @param trustStores list of additional trust stores.
* @return connection manager with extended trust relationship.
*/
public static PoolingClientConnectionManager createConnectionManagerWithExtraTrust(Collection<KeyStore> trustStores) throws KeyManagementException, KeyStoreException {
return createConnectionManager(Integer.MAX_VALUE,
BetterSSLFactory.createSocketFactory(trustStores));
}

/**
* Creates default connection manager.
Expand Down
@@ -0,0 +1,75 @@
package com.taskadapter.redmineapi.internal.comm.betterssl;

import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import org.apache.http.conn.ssl.SSLSocketFactory;

/**
* SSL Socket factory. Provides more authentication than the naive one and
* allows stored (custom) certificates to be added into the trust chain.
* <p>
* This work is based on
* http://codyaray.com/2013/04/java-ssl-with-multiple-keystores and common
* sense.
*/
public class BetterSSLFactory {
/**
* Creates a new SSL socket factory which supports both system-installed
* keys and all additional keys in the provided keystores.
*
* @param extraStores
* extra keystores containing root certificate authorities.
* @return Socket factory supporting authorization for both system (default)
* keystores and all the extraStores.
* @throws KeyStoreException if key store have problems.
* @throws KeyManagementException if new SSL context could not be initialized.
*/
public static SSLSocketFactory createSocketFactory(Collection<KeyStore> extraStores) throws KeyStoreException, KeyManagementException {
final Collection<X509TrustManager> managers = new ArrayList<X509TrustManager>();
for (KeyStore ks : extraStores) {
addX509Managers(managers, ks);
}
/* Add default manager. */
addX509Managers(managers, null);
final TrustManager tm = new CompositeTrustManager(managers);

try {
final SSLContext ctx = SSLContext.getInstance("SSL");
ctx.init(null, new TrustManager[] {tm}, null);
return new SSLSocketFactory(ctx);
} catch (NoSuchAlgorithmException e) {
throw new Error("No SSL protocols supported :(", e);
}
}

/**
* Adds X509 keystore-backed trust manager into the list of managers.
* @param managers list of the managers to add to.
* @param ks key store with target keys.
* @throws KeyStoreException if key store could not be accessed.
*/
private static void addX509Managers(final Collection<X509TrustManager> managers, KeyStore ks)
throws KeyStoreException, Error {
try {
final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {
managers.add((X509TrustManager) tm);
}
}
} catch (NoSuchAlgorithmException e) {
throw new Error("Default trust manager algorithm is not supported!", e);
}
}
}
@@ -0,0 +1,67 @@
package com.taskadapter.redmineapi.internal.comm.betterssl;

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import javax.net.ssl.X509TrustManager;

/**
* Trust manager which trusts a host when at least one peer trusts the target.
*/
final class CompositeTrustManager implements X509TrustManager {

/** Peers to delegate to. */
private final Collection<X509TrustManager> peers;

/** All accepted issuers. */
private final X509Certificate[] allCerts;

/**
* Creates a new composite manager.
* @param peers peers to delegate to.
*/
CompositeTrustManager(Collection<X509TrustManager> peers) {
this.peers = peers;
final List<X509Certificate> certs = new ArrayList<X509Certificate>();
for (X509TrustManager peer: peers) {
certs.addAll(Arrays.asList(peer.getAcceptedIssuers()));
}
this.allCerts = certs.toArray(new X509Certificate[certs.size()]);
}

@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
for (X509TrustManager peer : peers) {
try {
peer.checkClientTrusted(chain, authType);
return;
} catch (CertificateException e) {
//Let other manager to check this.
}
}
throw new CertificateException("Could not authenticate client, nobody trusts it.");
}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
for (X509TrustManager peer : peers) {
try {
peer.checkServerTrusted(chain, authType);
return;
} catch (CertificateException e) {
//Let other manager to check this.
}
}
throw new CertificateException("Could not authenticate server, nobody trusts it.");
}

@Override
public X509Certificate[] getAcceptedIssuers() {
return allCerts;
}

}

0 comments on commit eac07d1

Please sign in to comment.