Problemns connecting Spring to Active Directory - spring

I'm trying to connect to my company's active diretory to create an appplication capable of adding, updating and removing users from it, I configured may LdapContextSource with all the information my collegue who created de AD environment but when I try to do a simple search it doesn't work and give me this error:
org.springframework.ldap.AuthenticationNotSupportedException: [LDAP: error code 8 - BindSimple: Transport encryption required.]; nested exception is javax.naming.AuthenticationNotSupportedException: [LDAP: error code 8 - BindSimple: Transport encryption required.]
Here is my simple code:
public User getUserByLogin(String login) {
LdapContextSource contextSource = new LdapContextSource();
try {
contextSource.setUrl("secret");
contextSource.setBase("secret");
contextSource.setUserDn("secret");
contextSource.setPassword("secret");
contextSource.afterPropertiesSet();
LdapTemplate ldapTemplate = new LdapTemplate(contextSource);
User user = ldapTemplate.findOne(query().where("uid").is(login), User.class);
return user;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
May be relevant to let you guys to know that we're using GSS-API
Thanks in advance, hope someone can help me

The error indicates that transport encryption is required -- this generally means you need to change the LDAP server URI from ldap://server.domain.gTLD to ldaps://server.domain.gTLD but since few LDAP servers use certs issued from a public certificate authority (CA), you'll also need to set up the certificate trust. I use a private CA to sign all certificates, so can import the CA public key into the Java truststore.

Related

Micrometer with Elasticsearch over SSL

I'm trying to use Micrometer with Elasticsearch over SSL.
I use Micrometer in version 1.8.0, Elasticsearch in version 7.16.3 and OpenJDK 11.0.2 .
Because I know that it's not possible to use a built-in configuration (link) I tried to inject a custom HttpUrlConnectionSender as in the following class SecureHttpSender:
public class SecureHttpSender extends HttpUrlConnectionSender {
...
public SecureHttpSender(ElasticProperties properties, SecureElasticProperties secureElasticProperties) {
super(properties.getConnectTimeout(), properties.getReadTimeout());
this.secureElasticProperties = secureElasticProperties;
this.sslSocketFactory = buildSslSocketFactory();
}
#Override
public Response send(Request request) throws IOException {
HttpURLConnection httpURLConnection = null;
try {
httpURLConnection = (HttpURLConnection) request.getUrl().openConnection();
// if the connection is an instance of the HttpsURLConnection class, the ssl configuration will always been applied.
if (httpURLConnection instanceof HttpsURLConnection) {
// - hostname verifier
if (!secureElasticProperties.isVerifyHostname()) {
logger.debug("setting the hostname verifier to: {}", NoopHostnameVerifier.INSTANCE);
((HttpsURLConnection) httpURLConnection).setHostnameVerifier(NoopHostnameVerifier.INSTANCE);
}
// - trust store configuration
((HttpsURLConnection) httpURLConnection).setSSLSocketFactory(sslSocketFactory);
}
return super.send(request);
} finally {
try {
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
} catch (Exception ignore) {
}
}
}
private SSLSocketFactory buildSslSocketFactory() {
SSLSocketFactory sslSocketFactory;
try (InputStream is = getInputStream(secureElasticProperties.getTrustStorePath())) {
KeyStore truststore = KeyStore.getInstance(secureElasticProperties.getTrustStoreType());
truststore.load(is, secureElasticProperties.getTrustStorePassword().toCharArray());
SSLContextBuilder sslBuilder = SSLContexts.custom().loadTrustMaterial(truststore, null);
final SSLContext sslContext = sslBuilder.build();
sslSocketFactory = sslContext.getSocketFactory();
} catch (IOException | CertificateException | KeyStoreException | NoSuchAlgorithmException | KeyManagementException e) {
String message = String.format("error while loading the security configuration from: %s", secureElasticProperties);
logger.error(message, e);
throw new RuntimeException("management.metrics.export.elastic.ssl");
}
return sslSocketFactory;
}
private InputStream getInputStream(String trustStorePathString) throws IOException {
PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource resource = pathMatchingResourcePatternResolver.getResource(trustStorePathString);
return resource.getInputStream();
}
}
that I injected with Spring Boot so I can apply the desired configuration, but I got the following error:
ERROR 10912 --- [trics-publisher] i.m.elastic.ElasticMeterRegistry : failed to send metrics to elastic
javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
...
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
...
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
...
The server certificate and the client truststore are valid because I already used them with success.
I also tried to force a specific version of the TLS protocol during the handshake phase: TLSv1.3 and TLSv1.2 but the error still occurs.
Anyone have any suggestions on how to fix it? thanks
Check what super.send does, it creates a new connection without using the one you created. I'm not recommending using a self-signed cert and a custom truststore but you can set a default HostnameVerifier using
HttpsURLConnection.setDefaultHostnameVerifier.
Since this is static, it will work for all HttpsURLConnection instances so you don't need to inject anything into Micrometer.
The right solution would be either using a non-self-signed cert or a proper truststore (e.g.: via javax.net.ssl.trustStore).
I did a test with a simple change to the code I had posted and I solved it:
I copied all code of the super.send() method, adding the additional code to set the custom SslSocketFactory and all was OK!
so the reason was that
it creates a new connection without using the one you created
as Jonatan said... a my trivial mistake. :)

Problem with kerberos iis authentication in spring boot application on windows

i'a trying to deploy my jar spring boot application on windows but get error : [Krb5LoginModule] authentication failed
KrbException: Cannot locate default realm
In my localhost, everything is OK with the authentication but whene i deploy the jar in the production server i got the error even if both windows are in the same campany doamin.
the system administrator told me that for other application, the authentication is based on Kerberos and iis so the ticket exchange for authentication is very easy.
Here's my security config :
#Bean
public KerberosAuthenticationProvider kerberosAuthenticationProvider() {
KerberosAuthenticationProvider provider =
new KerberosAuthenticationProvider();
SunJaasKerberosClient client = new SunJaasKerberosClient();
client.setDebug(true);
provider.setKerberosClient(client);
provider.setUserDetailsService(dummyUserDetailsService());
return provider;
}
#Bean
public SpnegoEntryPoint spnegoEntryPoint() {
//return new SpnegoEntryPoint("/login");
return new SpnegoEntryPoint();
}
#Bean
public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
AuthenticationManager authenticationManager) {
SpnegoAuthenticationProcessingFilter filter =
new SpnegoAuthenticationProcessingFilter();
filter.setAuthenticationManager(authenticationManager);
return filter;
}
#Bean
public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
KerberosServiceAuthenticationProvider provider =
new KerberosServiceAuthenticationProvider();
provider.setTicketValidator(sunJaasKerberosTicketValidator());
provider.setUserDetailsService(dummyUserDetailsService());
return provider;
}
#Bean
public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
SunJaasKerberosTicketValidator ticketValidator =
new SunJaasKerberosTicketValidator();
ticketValidator.setServicePrincipal("HTTP/localhost#fgao.fr");
ticketValidator.setKeyTabLocation(new
FileSystemResource("c:\\user\\tomcat.keytab"));
ticketValidator.setDebug(true);
return ticketValidator;
}
#Bean
public DummyUserDetailsService dummyUserDetailsService() {
return new DummyUserDetailsService();
}
I don't know if i have to specify the keytab file because on windows no keytab or kb5.conf file is needed so the c:\user\tomcat.keytab file is empty.
Can someone help me with this please
You will need a Keytab file.
Keytab file contains keys which are required by kerberos module to decrypt the incoming kerberos token.
Keytab file is not out of the box present as it is specific to a user account in AD.It has to be generated by your system admin and give it to you.
You will need a service user (dedicated for your application). Generate keytab for it.
Copy it on your application server and specify its path in spring.
Check ktpass command on windows for more details about creating keytab.
You may also need to check for krb5 conf file, what it contains and how you can specify it inside Spring.

how can I solve ldap socket closed error in spring LDAP?

I tried to make the sample program for my project. It is LDAP with spring boot.
I tested it in my development environment. Then, it works well. However, when I test it in the deployment environment, It occurs socket closed error.
The difference is just the LDAP URL and password(I couldn't make a password of admin with special characters, eg. #, #).
So, I tried to access LDAP using ldapsearch in deployment environment. Then, I got some errors. However, when I search for this error, I couldn't search a suitable solution for me.
This is my spring configuration for access to LDAP.
#Bean
public ContextSource contextSource() {
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl("ldap://192.168.113.12");
contextSource.setBase("dc=test,dc=test");
contextSource.setUserDn("cn=admin,dc=test,dc=test");
contextSource.setPassword("test2019!#");
contextSource.afterPropertiesSet();
//for develop
// contextSource.setUrl("ldap://192.168.0.192");
// contextSource.setPassword("test2019");
PoolingContextSource pcs = new PoolingContextSource();
pcs.setDirContextValidator(new DefaultDirContextValidator());
pcs.setContextSource(contextSource);
TransactionAwareContextSourceProxy proxy = new TransactionAwareContextSourceProxy(pcs);
return proxy;
}
#Bean
public LdapTemplate ldapTemplate() {
return new LdapTemplate(contextSource());
}
This is error pictures when access to LDAP using spring LDAP.
This is error pictures using ldapsearch.
Help me.
ps. I didn't know how implemented the LDAP server, because it is installed by another team...
I would say the port is missing ;)
contextSource.setUrl("ldap://192.168.113.12:389");
Btw. in my opinion a nicer approach is to set the properties like this:
application.yml (or application.properties)
ldap:
contextSource:
url: ldap://192.168.113.12:389 #Local
base: dc=test,dc=test
userDn: cn=admin,dc=test,dc=test
password: test2019!#
and in the config class:
#Configuration
public class LdapConfiguration {
#Bean
#ConfigurationProperties(prefix="ldap.context-source")
public LdapContextSource contextSource() {
return new LdapContextSource();
}
#Bean
public ContextSource poolingLdapContextSource() {
PoolingContextSource pcs = new PoolingContextSource();
pcs.setDirContextValidator(new DefaultDirContextValidator());
pcs.setContextSource(contextSource());
TransactionAwareContextSourceProxy proxy = new TransactionAwareContextSourceProxy(pcs);
return proxy;
}
// other configs like ldaptemplate
}

How to diagnose/fix Signature Digest Verification Failure on web service response?

I am using wss4jSecurityInterceptor for validation and securement actions on my webservice. The SOAP messages back and forth are secured with Signature, Encryption and Timestamp. The problem I am having is, the recipient is unable to verify my response signature and gets the following error:
oracle.wsm.security.SecurityException: WSM-00061 : Signature digest verification failure. The system property xml.debug.verify should be enabled for the details about the digest calculations during verification phase (note xml.debug.verify slows down the signature verification for very large messages).
Here is the code that I am using to perform validation and securement actions:
#EnableWs
#Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
private static Log log = LogFactory.getLog(WebServiceConfig.class);
#Bean
public KeyStoreCallbackHandler securityCallbackHandler() {
KeyStoreCallbackHandler callbackHandler = new KeyStoreCallbackHandler();
callbackHandler.setPrivateKeyPassword("changeit");
return callbackHandler;
}
#Bean
public Wss4jSecurityInterceptor securityInterceptor() throws Exception {
Wss4jSecurityInterceptor securityInterceptor = new Wss4jSecurityInterceptor();
// validate incoming request
securityInterceptor.setValidationActions("Timestamp Signature Encrypt");
securityInterceptor.setValidationSignatureCrypto(getCryptoFactoryBean().getObject());
securityInterceptor.setValidationDecryptionCrypto(getCryptoFactoryBean().getObject());
securityInterceptor.setValidationCallbackHandler(securityCallbackHandler());
// encrypt the response
securityInterceptor.setSecurementEncryptionUser("client_pub"); // alias of client public key
securityInterceptor.setSecurementEncryptionParts("{Content}{}Body");
securityInterceptor.setSecurementEncryptionKeyIdentifier("DirectReference");
securityInterceptor.setSecurementEncryptionCrypto(getCryptoFactoryBean().getObject());
// sign the response
securityInterceptor.setSecurementSignatureAlgorithm(WSS4JConstants.RSA_SHA1);
securityInterceptor.setSecurementSignatureDigestAlgorithm(WSS4JConstants.SHA1);
securityInterceptor.setSecurementSignatureKeyIdentifier("DirectReference");
securityInterceptor.setSecurementActions("Encrypt Signature Timestamp");
securityInterceptor.setSecurementUsername("secret"); // alias of the private key
securityInterceptor.setSecurementPassword("changeit");
securityInterceptor.setSecurementSignatureCrypto(getCryptoFactoryBean().getObject());
return securityInterceptor;
}
#Bean
public CryptoFactoryBean getCryptoFactoryBean() throws IOException {
CryptoFactoryBean cryptoFactoryBean = new CryptoFactoryBean();
cryptoFactoryBean.setKeyStorePassword("changeit");
cryptoFactoryBean.setKeyStoreLocation(new ClassPathResource("cert/myKeystore.jks"));
return cryptoFactoryBean;
}
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
try {
interceptors.add(securityInterceptor());
} catch (Exception e) {
throw new RuntimeException("could not initialize security interceptor");
}
}
#Bean
public ServletRegistrationBean<MessageDispatcherServlet> registerMessageDispatcherServlet(
ApplicationContext context) {
MessageDispatcherServlet messageDispatcherServlet = new MessageDispatcherServlet();
messageDispatcherServlet.setApplicationContext(context);
messageDispatcherServlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean<MessageDispatcherServlet>(messageDispatcherServlet, "/ws/*");
}
The receiving side suspects that the error is caused by the fact that the certificate sent as has been corrupted by carriage return character sequence. Here is what it looks like:
<wsse:BinarySecurityToken
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"
wsu:Id="X509-124b3a03-73e3-4838-a254-f20883ff4184">MIIG1zCCBb+gAwIBAgIRAIVi4APh2tBtAAAAAFDvSXwwDQYJKoZIhvcNAQELBQAwgboxCzAJBgNV
BAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3Qu
 bmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1
 dGhvcml6ZWQgdXNlIG9ubHkxLjAsBgNVBAMTJUVudHJ1c3QgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkgLSBMMUswHhcNMTkwMzIyMTY1ODA5WhcNMjAwMzIyMTcyODA4WjCBgjELMAkGA1UEBhMCVVMx
 EzARBgNVBAgTCkNhbGlmb3JuaWExETAPBgNVBAcTCEJlcmtlbGV5MS4wLAYDVQQKEyVMYXdyZW5j
 ZSBCZXJrZWxleSBOYXRpb25hbCBMYWJvcmF0b3J5MRswGQYDVQQDExJtc2cudWNwLWl0LmxibC5n
b3YwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9fSfO1cKz1D34dQaQF1ScY0eEGALm
 s3hkRRWMYPWTH8gZRpfP4iFLF9Sx09vagGFHUrru1xf8BnVyB2CsZnvHtE+bDrK4kp4hQpO8aXqf
 VEpn+SpY38aQDfZUqjRK6HJM5qxF/2xjNKSw41MPkXALK04yga07IwSUE+GpPt8i2noQPKKJufzf
guA8Plmsnf9uNcpxFezNXC1k+HQBKeO0V+CZ4K5FKyckq64eT8VWbawF5CFoNigtbxTuHHClECV0
 VrzNNf5jj/cpymwOu51ljnPhGEY/f73DlEBZg8jpFcDSnAyYyJH2GFgz2wa0TiOpdxItTWHMXalb
 YtDH9VvRAgMBAAGjggMMMIIDCDAdBgNVHREEFjAUghJtc2cudWNwLWl0LmxibC5nb3YwggF/Bgor
BgEEAdZ5AgQCBIIBbwSCAWsBaQB2AFWB1MIWkDYBSuoLm1c8U/DA5Dh4cCUIFy+jqh0HE9MMAAAB
 aaZyHvYAAAQDAEcwRQIhAJtC0LBFOfupRDy6BOvRrH6pAlvAJFFUWxMbbSRV59nOAiB/c2D5VOwS
 aBeC4czjDjUAAfeunN/npVLyX7i6v69aLgB3AId1v+dZfPiMQ5lfvfNu/1aNR1Y2/0q1YMG06v9e
oIMPAAABaaZyHwQAAAQDAEgwRgIhAJj/g/g+UjWaoHCl7ldHG08zuUrL8XbAkR8Op4G+//nvAiEA
 sLoq29TrwnOtWa+O1+PRHCCllKD22yeKxdMUNrR2Pu0AdgCyHgXMi6LNiiBOh2b5K7mKJSBna9r6
 cOeySVMt74uQXgAAAWmmch7YAAAEAwBHMEUCIBVGxbR2c/ByOtfDRMlpIFPRYA5+dGkB4zJ7tjQ6
moYYAiEAmFbeJdg9rCZt6qUqhRH2mxJlotRjp+mZmiHyRFmulykwDgYDVR0PAQH/BAQDAgWgMB0G
 A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js
 LmVudHJ1c3QubmV0L2xldmVsMWsuY3JsMEsGA1UdIAREMEIwNgYKYIZIAYb6bAoBBTAoMCYGCCsG
AQUFBwIBFhpodHRwOi8vd3d3LmVudHJ1c3QubmV0L3JwYTAIBgZngQwBAgIwaAYIKwYBBQUHAQEE
 XDBaMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAzBggrBgEFBQcwAoYnaHR0
 cDovL2FpYS5lbnRydXN0Lm5ldC9sMWstY2hhaW4yNTYuY2VyMB8GA1UdIwQYMBaAFIKicHTdvFM/
z3vU981/p2DGCky/MB0GA1UdDgQWBBRzakStBRG3O6NBdLt1XUkvwwKn4jAJBgNVHRMEAjAAMA0G
 CSqGSIb3DQEBCwUAA4IBAQAqt00w3oV4XgcdhcanJdgY/SUCtmN9I4jdras8vRson+wK6WJy6Em8
 EF5wyRDLOwUD75Rm9Ak23lKjYOcDTWQGG3YXrYh7xe3J8C+plEY1NAwNPXflCzxkDcdJ4pX0LDfj
aC5FniAvKJ9ET7ZQvpCjBRd7wgOrkuhf3YjCFC3hM4E6D7cGb6DEhCh9nOy+e9woQ/C1LnDil3NX
 Vgqk3PMZYkUeHjxqItnDcOAeJaeqsAUiTxZbC8sQQ9ae/CPahghgSWEwL7tMrAH4nGT3yeBQl0I9
 O7H9xWj0ooQ8Wt6nO7pq64X5uc7W6iA3/1Of5uCntmMrsqPlvSscP6QVuPw6</wsse:BinarySecurityToken>
My keystore is in PKCS format and I have also converted to jks. No dice. The certs in the keystore verify just fine with openssl command, so I know they are good.
I do my builds on a mac and deploy on CentOS. I have also copied the sources to CentOS and run the build there. Again, made no difference.
Has anyone run into this issue and solved it? Would appreciate any help!
Thanks much!
xmlsec library upgraded to 2.1.3.
wss4j-ws-security-dom upgraded to 2.2.3
Details: The problem was narrow and very obscure. I haven't put everything I did back to the way it was to see if the problem returns, but could do that easily. We use spring-ws-security and WSS4Jfrom apache to perform the validation and securement actions. Both libraries include xmlsec.jar which provides this functionality. In fact wss4j includes it twice since it also embeds opensaml , which in turn contains xmlsec. Spring-ws-security also contains its own xmlsec. They are all different versions. The problem with carriage returns is reported for xmlsec versions 2.1.0 and above.
https://issues.apache.org/jira/browse/SANTUARIO-482
https://issues.apache.org/jira/browse/SANTUARIO-494
I updated wss4j to ver 2.2.3 (where this is ostensibly fixed) and also added the fixed version of xmlsec.jar directly for good measure.
Hope it helps someone else out. :-)

Programmatic login with liberty profile without password

I try to migrate our application from WAS 8.0 to Liberty Profile at the moment.
In our application I need the possibility to do a programmatic login without having the password of the user.
In WAS 8.0 this was done with the following code snippet:
import com.ibm.websphere.security.auth.WSSubject;
import com.ibm.ws.security.core.ContextManagerFactory;
import com.ibm.websphere.security.auth.callback.WSCallbackHandlerImpl;
public class SecurityConfigJaasWasImpl implements ISecurityConfig {
public Object doAsWithoutPwd(String user, String[] roles, final ISecuredCode code) throws Exception {
final String mName ="doAs(String, String[], ISecuredCode)";
Object ret = null;
try {
if (code != null) {
ret = WSSubject.doAs(ContextManagerFactory.getInstance().login("REALM", user), new PrivilegedExceptionAction() {
/* (non-Javadoc)
* #see java.security.PrivilegedExceptionAction#run()
*/
public Object run() throws Exception {
return code.run();
}
});
}
} catch (LoginException e) {
throw new SecurityConfigException("Error login user " + user);
}
}
Unfortunately the class ContextManagerFactory is not known in Liberty.
All examples for programmatic login with liberty profile are using WSCallbackHandlerImpl to do a Jaas login. But for that I need to know the password of the user.
Is there any possibility to do something similar to my WAS 8.0 code in liberty profile?
I had this same problem when porting our application from WAS-ND 7 to Liberty. Unfortunately, there is no way to perform a programmatic login on Liberty without having access to the user's password. I have an open PMR with IBM on this (25293,082,000), and I was told that the feature is "under consideration". I also have an RFE open on this:
https://www.ibm.com/developerworks/rfe/execute?use_case=viewRfe&CR_ID=100438

Resources