-
Notifications
You must be signed in to change notification settings - Fork 484
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Using custom truststore more complicated than necessary #494
Comments
Thanks for the feedback. What did you have in mind as a solution? As far as I know this is the Java supported way to customize the truststore and there isn't a lot we can do when using the JDK as the underlying client. |
I think a couple of things can be done. I order of increasing complexity
|
Thanks for your thoughts. Would you create separate tickets for each of these and link back to this issue? Any interested in submitting PRs for the features? |
Any updates on this thread? |
Any update on this thread?? is there any other possible way to use SPRING LDAP with custom truststore? |
public class TruststoreDirContextAuthenticationStrategy
extends SimpleDirContextAuthenticationStrategy {
@Override
public void setupEnvironment(Hashtable<String, Object> env, String userDn, String password) {
super.setupEnvironment(env, userDn, password);
env.put("java.naming.ldap.factory.socket", TruststoreSSLSocketFactoryHolder.class.getName());
}
}
public class TruststoreSSLSocketFactoryHolder implements InitializingBean {
private static SocketFactory instance;
private final SSLContextProvider sslContextProvider;
public TruststoreSSLSocketFactoryHolder(SSLContextProvider sslContextProvider) {
this.sslContextProvider = sslContextProvider;
}
@SuppressWarnings("squid:S2696")
@Override
public void afterPropertiesSet() {
TruststoreSSLSocketFactoryHolder.instance = new TruststoreSSLSocketFactory(sslContextProvider);
}
public static SocketFactory getDefault() {
return instance;
}
}
public class TruststoreSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory socketFactory;
TruststoreSSLSocketFactory(SSLContextProvider contextProvider) {
this.socketFactory = contextProvider.get().getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return socketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return socketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose)
throws IOException {
return socketFactory.createSocket(s, host, port, autoClose);
}
@Override
public Socket createSocket(String host, int port) throws IOException {
return socketFactory.createSocket(host, port);
}
@Override
public Socket createSocket(String host, int port, InetAddress clientAddress, int clientPort)
throws IOException {
return socketFactory.createSocket(host, port, clientAddress, clientPort);
}
@Override
public Socket createSocket(InetAddress address, int port) throws IOException {
return socketFactory.createSocket(address, port);
}
@Override
public Socket createSocket(
InetAddress address, int port, InetAddress clientAddress, int clientPort) throws IOException {
return socketFactory.createSocket(address, port, clientAddress, clientPort);
}
@Override
public Socket createSocket(Socket s, InputStream consumed, boolean autoClose) throws IOException {
return socketFactory.createSocket(s, consumed, autoClose);
}
@Override
public Socket createSocket() throws IOException {
return socketFactory.createSocket();
}
}
public class SSLContextProvider implements Supplier<SSLContext> {
private final TrustManager[] trustManagers;
public SSLContextProvider(TrustManager[] trustManagers) {
this.trustManagers = trustManagers;
}
public SSLContext get() {
try {
SSLContext ssl = SSLContext.getInstance("TLSv1.2");
ssl.init(null, trustManagers, null);
return ssl;
} catch (NoSuchAlgorithmException | KeyManagementException e) {
throw new IllegalStateException(e);
}
}
}
@Configuration
public class SSLSocketFactoryConfiguration {
@Bean
public TruststoreSSLSocketFactoryHolder truststoreSSLSocketFactoryHolder(
TrustManager[] trustManagers) {
return new TruststoreSSLSocketFactoryHolder(buildSslContextProvider(trustManagers));
}
private SSLContextProvider buildSslContextProvider(TrustManager[] trustManagers) {
return new SSLContextProvider(trustManagers);
}
}
public class MyLdapContextSource extends LdapContextSource {
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
var urls = getUrls();
Stream.of(urls)
.filter(isSecured())
.findAny()
.ifPresent(ignored ->
setAuthenticationStrategy(new TruststoreDirContextAuthenticationStrategy()));
}
private static Predicate<String> isSecured() {
return url -> url.startsWith("ldaps://")
}
} Unfortunately, it has a dependency for org.springframework.ws.soap.security.support.TrustManagersFactoryBean (trustManagers) in our applications but we are using SOAP too. |
@marschall i have tried the code which you shared. But i am getting an below exception when i am trying to login(autheticate user from ldap server) 2020-01-08 19:49:19.792 ERROR 44 --- [nio-8888-exec-3] w.a.UsernamePasswordAuthenticationFilter : An internal error occurred while trying to authenticate the user. org.springframework.security.authentication.InternalAuthenticationServiceException: simple bind failed: {service}:636; nested exception is javax.naming.CommunicationException: simple bind failed: {serviceName}:636 [Root exception is java.net.SocketException: Connection or outbound has closed] Could you please help me on this? |
@satyy I would need a full stack trace and also debug logs with |
@marschall The issue is resolved. There was some issue with the tls protocol i was using. After, correcting it, now i am able to connect to server over ldaps with custom truststore using code which you shared. @marschall @shark300 thanks for the help. |
This comment has been minimized.
This comment has been minimized.
With thanks to the examples in this ticket, and in case it helps anyone, this is what I ended up doing. I'm using the excellent sslcontext-kickstart library which lets you use multiple sources for your truststores (e.g. JDK + one from the local filesystem + one from the classpath, etc.).
import org.springframework.ldap.core.support.SimpleDirContextAuthenticationStrategy;
import java.util.Hashtable;
public class TruststoreDirContextAuthenticationStrategy extends SimpleDirContextAuthenticationStrategy {
@Override
public void setupEnvironment(Hashtable<String, Object> env, String userDn, String password) {
super.setupEnvironment(env, userDn, password);
env.put("java.naming.ldap.factory.socket", TruststoreSSLSocketFactory.class.getName());
}
}
import nl.altindag.ssl.SSLFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class TruststoreSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegate;
public TruststoreSSLSocketFactory() {
this.delegate = SSLFactory.builder()
.withSystemTrustMaterial()
.withDefaultTrustMaterial()
.withTrustMaterial("truststore.jks", "changeit".toCharArray())
.build().getSslSocketFactory();
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return delegate.createSocket(address, port, localAddress, localPort);
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return delegate.createSocket(host, port);
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return delegate.createSocket(s, host, port, autoClose);
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return delegate.createSocket(host, port, localHost, localPort);
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return delegate.createSocket(host, port);
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket() throws IOException {
return delegate.createSocket();
}
@Override
public Socket createSocket(Socket s, InputStream consumed, boolean autoClose) throws IOException {
return delegate.createSocket(s, consumed, autoClose);
}
public static SocketFactory getDefault() {
return new TruststoreSSLSocketFactory();
}
} I then have a class annotated with @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
if (url != null && bindUser != null && bindPassword != null) {
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl(url);
contextSource.setUserDn(bindUser);
contextSource.setPassword(bindPassword);
if (new URI(url).getScheme().equalsIgnoreCase("ldaps")) {
contextSource.setAuthenticationStrategy(new TruststoreDirContextAuthenticationStrategy());
}
contextSource.afterPropertiesSet();
auth.ldapAuthentication()
.userDetailsContextMapper(userDetailsContextMapper())
.userSearchFilter(userSearchFilter)
.userSearchBase(userSearchBase)
.contextSource(contextSource)
.groupSearchFilter(groupSearchFilter)
.groupSearchBase(groupSearchBase);
}
} |
Closing in favor of the issues in #494 (comment) |
Hello i want to use a separate truststore for the ldap connection, i have take your example class, i don't understand why it is not working , all the class are called by spring but the setupenvironement function in class TruststoreDirContextAuthenticationStrategy is never called ( but the constructor is called ). I am using spring boot 2.5.4. Thank for any help. |
We want to use Spring LDAP with a custom truststore because we want to use LDAPS and truststore with only the CA certificate of the LDAP servers. We do not want to configure a truststore globally, we are using and following LDAP referrals.
We were initially using a
DefaultTlsDirContextAuthenticationStrategy
and configuring the sslSocketFactory property. We found that was not enough. We only got it working by setting the java.naming.ldap.factory.socket property. This required us to:SSLSocketFactory
SimpleDirContextAuthenticationStrategy
that sets thejava.naming.ldap.factory.socket
property in the#setupEnvironment(Hashtable, String, String)
method.DefaultSpringSecurityContextSource
that in sets thejava.naming.ldap.factory.socket
property in the#getAuthenticatedEnv(String, String)
methodThe text was updated successfully, but these errors were encountered: