Announcement Announcement Module
Collapse
No announcement yet.
LDAPS External: Certificate contains unsupported critical extensions: [2.5.29.17] Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • LDAPS External: Certificate contains unsupported critical extensions: [2.5.29.17]

    Hi there

    I've got some problems setting up my LDAPS connection with External Authentication to my Active Directory. Because I have to do some password operations I have to use LDAPS and can't fallback to LDAP.

    I have got installed the public CA certificate in my truststore, imported my client certificate in the keystore, but still got the following HandshakeException:
    Code:
    Unable to communicate with LDAP server; nested exception is javax.naming.CommunicationException: simple bind failed: ttsrv01:636 [Root exception is javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: Certificate contains unsupported critical extensions: [2.5.29.17]]


    It seems that in my client certificate the SubjectAltName extension (2.5.29.17) is viewed as critical, but it shouldn't. If i view my certificate with the windows mmc certificate snap-in, it is not marked as critical!

    How can I avoid that failure. It is obvious that this handshake should establish at all, because this field is NOT critical. Has someone got this error too and knows how to do a workaround?

    I've been looking around for two days now and I don't know much more than before I started.


    For my SSL setup, I have extended LdapContextSource:
    Code:
    package com.usp.portal.portlets.usermgmt.dao.ldap;
    
    import java.util.Hashtable;
    import javax.naming.Context;
    import org.springframework.ldap.support.LdapContextSource;
    
    public class SecureLdapContextSource
    		extends LdapContextSource {
    	
    	private String keyStore = null;
    	private String keyStorePassword = null;
    	private String trustStore = null;
    	
    
    	public void afterPropertiesSet() throws Exception {
    		super.afterPropertiesSet();
    		
    		Hashtable env = super.getAuthenticatedEnv();
    		
    		//simple authentication needs username and password, external needs a keystore
    		env.put(Context.SECURITY_AUTHENTICATION, "External");
    		//env.put(Context.SECURITY_AUTHENTICATION, "simple");
    		//env.put(Context.SECURITY_PRINCIPAL, super.userName);
    		//env.put(Context.SECURITY_CREDENTIALS, super.password);
    		
    		//specify use of ssl
    		env.put(Context.SECURITY_PROTOCOL, "ssl");
    		
    		//set the environment
    		super.setupAuthenticatedEnvironment(env);
    		
    		System.setProperty("javax.net.ssl.trustStore", trustStore);
    		System.setProperty("javax.net.ssl.keyStore", keyStore);
    		System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword);
    	}
    	
    	public void setKeyStore(String keyStore) {
    		this.keyStore = keyStore;
    	}
    	
    	public void setTrustStore(String trustStore) {
    		this.trustStore = trustStore;
    	}
    	
    	public void setKeyStorePassword(String password) {
    		this.keyStorePassword = password;
    	}
    }
    Last edited by ananasbananashakar; Oct 17th, 2007, 06:23 AM. Reason: solved

  • #2
    More in detail

    OK folks, i found out, that my CA Certificate which is sent in the certificate chain from the AD contains the field 2.5.29.17 marked as critical. It contains the FQDN dNSname of the AD server.

    This is shown in my SSL debug log where the certificate chain from the AD is listed:
    Code:
    [5]: ObjectId: 2.5.29.17 Criticality=true
    SubjectAlternativeName [
    [DNSName: server.domain.com]]
    and this too a little bit later:
    Code:
    http-10001-1, SEND TLSv1 ALERT:  fatal, description = certificate_unknown
    http-10001-1, WRITE: TLSv1 Alert, length = 2
    [Raw write]: length = 7
    0000: 15 03 01 00 02 02 2E                               .......
    http-10001-1, called closeSocket()
    http-10001-1, handling exception: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: Certificate contains unsupported critical extensions: [2.5.29.17]
    user.SaveAction:persistIfValid() - Unknown error while perstisting object:Unable to communicate with LDAP server; nested exception is javax.naming.CommunicationException: simple bind failed: server.domain.com:
    636 [Root exception is javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: Certificate contains unsupported critical extensions: [2.5.29.17]]
    Why is such a well known extension not supported? My problem is now: my sysadmin will not change the CA certificate only because of me (quite understandable ;-)) But I still have to code this interface to the AD. Is it possible to overwrite the Implementation of this certificate validator or what it is? (I know that this kind of question is not for this forum, because it is not about Spring LDAP, but is still related to it).

    Comment


    • #3
      no chance..

      hi there

      I found no way to avoid that problem. I tried really to implement my own PKIXCertPathChecker to process that baaad bad extension. But because Spring LDAP uses the underlying JNDI and JNDI uses only default SSL Trustmanager and Keymanager I was not able to hang in my code where I needed it.

      I really have to change now my server certificate or my AD what a mess..

      Comment


      • #4
        workaround found!

        You will have to implement your own SSLSocketFactory which can be set in JNDI using a system property or in the environment hashtable.

        replace the afterPropertiesSet with the following code:
        Code:
        	public void afterPropertiesSet() throws Exception {
        		super.afterPropertiesSet();
        		
        		Hashtable env = new Hashtable();
        		
        		//simple authentication needs username and password, external needs a keystore
        //		env.put(Context.SECURITY_AUTHENTICATION, "External");
        		env.put(Context.SECURITY_AUTHENTICATION, "simple");
        		env.put(Context.SECURITY_PRINCIPAL, super.userName);
        		env.put(Context.SECURITY_CREDENTIALS, super.password);
        		env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        		//use one of them
        		//env.put("java.naming.ldap.factory.socket", "XXXX.ldap.security.DummySSLSocketFactory");
        		System.setProperty("java.naming.ldap.factory.socket", "XXXX.ldap.security.DummySSLSocketFactory");
        		
        		//specify use of ssl
        		env.put(Context.SECURITY_PROTOCOL, "ssl");
        		
        		//set the environment
        		super.setupAuthenticatedEnvironment(env);
        		// set the base environment again
        		super.setBaseEnvironmentProperties(env);
        		
        		
        		System.setProperty("javax.net.ssl.trustStore", trustStore);
        		System.setProperty("javax.net.ssl.keyStore", keyStore);
        		System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword);
        
        		// it is necessary to call super.afterPropertiesSet() again!!!
        		super.afterPropertiesSet();
        	}
        I implemented a DummySSLSocketFactory which extends the javax.net.ssl.SSLSocketFactory class.
        Code:
        package XXXX.ldap.security;
        
        import java.io.IOException;
        import java.net.InetAddress;
        import java.net.Socket;
        
        import javax.net.SocketFactory;
        import javax.net.ssl.SSLContext;
        import javax.net.ssl.SSLSocketFactory;
        import javax.net.ssl.TrustManager;
        
        public class DummySSLSocketFactory
        		extends SSLSocketFactory {
        	private SSLSocketFactory factory;
        
        	public DummySSLSocketFactory() {
        		try {
        			
        			SSLContext sslcontext = null;
        // here you can instanciate your own ssl context with hardened security and your own trust managers which can check the extension
        //			SSLContext sslcontext = SSLSecurityInitializer.getContext();
        			if (sslcontext == null) {
        				sslcontext = SSLContext.getInstance("TLS");
        				sslcontext.init(null, // No KeyManager required
        						new TrustManager[] { new DummyTrustManager() }, 
        						new java.security.SecureRandom());
        			}
        			
        			factory = (SSLSocketFactory) sslcontext.getSocketFactory();
        
        		} catch (Exception ex) {
        			ex.printStackTrace();
        		}
        	}
        
        	public static SocketFactory getDefault() {
        		return new DummySSLSocketFactory();
        	}
        
        	public Socket createSocket(Socket socket, String s, int i, boolean flag) throws IOException {
        		return factory.createSocket(socket, s, i, flag);
        	}
        
        	public Socket createSocket(InetAddress inaddr, int i, InetAddress inaddr1, int j) throws IOException {
        		return factory.createSocket(inaddr, i, inaddr1, j);
        	}
        
        	public Socket createSocket(InetAddress inaddr, int i) throws IOException {
        		return factory.createSocket(inaddr, i);
        	}
        
        	public Socket createSocket(String s, int i, InetAddress inaddr, int j) throws IOException {
        		return factory.createSocket(s, i, inaddr, j);
        	}
        
        	public Socket createSocket(String s, int i) throws IOException {
        		return factory.createSocket(s, i);
        	}
        
        	public String[] getDefaultCipherSuites() {
        		return factory.getSupportedCipherSuites();
        	}
        
        	public String[] getSupportedCipherSuites() {
        		return factory.getSupportedCipherSuites();
        	}
        
        }

        and here is the DummyTrustManager:
        Code:
        package XXXX.ldap.security;
        
        import java.security.cert.X509Certificate;
        import javax.net.ssl.X509TrustManager;
        
        public class DummyTrustManager implements X509TrustManager {
        	public void checkClientTrusted(X509Certificate[] cert, String authType) {
        		return;
        	}
        
        	public void checkServerTrusted(X509Certificate[] cert, String authType) {
        		return;
        	}
        
        	public X509Certificate[] getAcceptedIssuers() {
        		return new X509Certificate[0];
        	}
        }

        Comment


        • #5
          Excellent workaround. Thanks for sharing.

          Comment


          • #6
            I came accross the same problem when using a CA Certificate from an Active Directory CA. The certificate I was using was exported directly from the CA snapin tool for win2k3.

            To get a CA certificate without the unsupported critical extension I downloaded the CA certificate from the "/certsrv" URL on the AD CA server (running on the IIS server on win2k3).

            This fixed the problem without having to implement a new TrustManager and SSLSocketFactory.

            Hope that helps,

            -- DD

            Comment


            • #7
              Please clarify

              Thanks for the information. I don't quite understand it. I can use firefox to view and/or download the ssl server certificate for the https server. Is this what I need? What would I do with it?

              Sorry, I know nothing about outlook (ask me about linux

              Comment

              Working...
              X