如何接受JNDI / LDAP连接的自签名证书?

我需要通过SSL连接到LDAP目录。

在非生产环境中,我们使用自签名证书,当然,这些证书无法通过以下方式validation:

javax.naming.CommunicationException: simple bind failed: ldapserver:636 [Root exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target] at com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:197) at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2694) at com.sun.jndi.ldap.LdapCtx.(LdapCtx.java:293) at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:175) at com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(LdapCtxFactory.java:193) at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:136) at com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(LdapCtxFactory.java:66) at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:667) at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:288) at javax.naming.InitialContext.init(InitialContext.java:223) at javax.naming.ldap.InitialLdapContext.(InitialLdapContext.java:134) 

我知道如何使用自定义信任管理器进行启用SSL的连接 ,但是不知道如何使用连接JNDI API的连接,而我不管理实际的连接。 也就是说,以下标准设置在哪里可以插入信任管理器?

提前致谢。

 Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, "ldaps://ldapserver:636"); env.put(Context.SECURITY_PROTOCOL, "ssl"); env.put(Context.SECURITY_AUTHENTICATION, "simple"); env.put(Context.SECURITY_PRINCIPAL, "myUser"); env.put(Context.SECURITY_CREDENTIALS, "myPassword"); LdapContext ctx = new InitialLdapContext(env, null); ctx.search (...) 

根据JNDI文档,似乎可以设置自定义SSLSocketFactory

http://download.oracle.com/javase/1.5.0/docs/guide/jndi/jndi-ldap-gl.html#socket

 public class MySSLSocketFactory extends SocketFactory { private SSLSocketFactory sf; public MySSLSocketFactory() { KeyStore keyStore = ... /* Get a keystore containing the self-signed certificate) */ TrustManagerFactory tmf = TrustManagerFactory.getInstance(); tmf.init(keyStore); SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, tmf.getTrustManagers(), null); sf = ctx.getSocketFactory(); } /* delegate SSLSocketFactory public methods to sf */ ... } 

配置环境以使用此套接字工厂

 env.put("java.naming.ldap.factory.socket", "com.example.MySSLSocketFactory"); 

覆盖Trustmanager时,您可以接受任何证书:

DummyTrustmanager.java

 public class DummyTrustmanager implements X509TrustManager { public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { // do nothing } public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { // do nothing } public X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[0]; } } 

MySSLSocketFactory.java

 public class MySSLSocketFactory extends SSLSocketFactory { private SSLSocketFactory socketFactory; public MySSLSocketFactory() { try { SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, new TrustManager[] { new DummyTrustmanager() }, new SecureRandom()); socketFactory = ctx.getSocketFactory(); } catch (Exception ex) { ex.printStackTrace(System.err); /* handle exception */ } } public static SocketFactory getDefault() { return new MySSLSocketFactory(); } @Override public String[] getDefaultCipherSuites() { return socketFactory.getDefaultCipherSuites(); } @Override public String[] getSupportedCipherSuites() { return socketFactory.getSupportedCipherSuites(); } @Override public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException { return socketFactory.createSocket(socket, string, i, bln); } @Override public Socket createSocket(String string, int i) throws IOException, UnknownHostException { return socketFactory.createSocket(string, i); } @Override public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException, UnknownHostException { return socketFactory.createSocket(string, i, ia, i1); } @Override public Socket createSocket(InetAddress ia, int i) throws IOException { return socketFactory.createSocket(ia, i); } @Override public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException { return socketFactory.createSocket(ia, i, ia1, i1); } } 

Main.java

 public class Main { public static void main(String[] args) throws NamingException { Hashtable env = new Hashtable(11); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, "ldaps://ldapserver:636/"); env.put(Context.SECURITY_PROTOCOL, "ssl"); env.put(Context.SECURITY_AUTHENTICATION, "simple"); env.put(Context.SECURITY_PRINCIPAL, "myUser"); env.put(Context.SECURITY_CREDENTIALS, "myPassword"); env.put("java.naming.ldap.factory.socket", "ldapsecure.MySSLSocketFactory"); LdapContext ctx = new InitialLdapContext(env, null); } } 

无需代码。 只需将证书添加到测试客户端信任库即可。

不要为此编写额外的代码。 它是不安全的,并且您承担了代码泄漏到生产中的主要风险。 你真的不想那样。

@Jcs答案是正确的,但如果您不想使用自定义TrustManager,并且您愿意将该特定自签名证书作为VM中其他用途的可信CA,您可以:

  • 将此证书添加到您的默认信任库(通常是JRE安全目录下的cacerts )或
  • 在不同位置(可能基于默认cacerts的副本)创建一个新的信任库,该信任库将包含该特定证书,并将其用作默认信任库,但设置javax.net.ssl.trustStore*系统属性。

自从我上次这样做了大约5年以来,所以我的答案有点模糊,我害怕; 但我认为如果你在java.security中可信任的提供者,你应该能够在/ usr / java / jre / lib / security /下找到它。 然后它会接受证书作为可信任的。

我此刻无法访问我的笔记,但我稍后会仔细阅读

完全放弃JNDI, 一些框架将引用LOCAL SYSTEM LDAP配置(这通常是/etc/ldap.conf或类似的东西)。 当我说LOCAL时,我指的是运行JNDI操作的系统。

至少在大多数支持LDAP的Linux操作系统的情况下,这样的LDAP配置文件内部(通常)是一行:

 TLS_REQCERT demand 

这是默认的TLS / SSL关键性设置(并且是最严格的),因为如果证书的ANYTHING WHATSOEVER错误(包括自签名),它将使连接失败。

您可以尝试使用不同的设置而不是“需求”(使用“允许”或“从不”)。 完成此操作后,再次尝试操作,看看问题是否已解决。 同样,并非所有此类内容都会读取甚至检测您的本地系统设置。 有些人有,有些则没有。 值得一看。

我希望这有帮助…

马克斯