如何在Java LDAP JNDI LDAP API中以编程方式禁用证书主机名验证?

时间:2018-10-01 23:35:54

标签: java ssl ldap ssl-certificate

Java 8u181进行了一项更改,当使用Java JNDI LDAP API连接到LDAPS(TLS)服务器时,启用证书主机名验证。

请参阅:https://www.oracle.com/technetwork/java/javase/8u181-relnotes-4479407.html#JDK-8200666

如何禁用此主机名验证,或者最好指定一个自定义javax.net.ssl.HostnameVerifier类。 Oracle的文档仅指定Java环境属性来禁用验证,但没有指出任何有问题的方法来完成此操作,这对于无法(或不希望)更改其JVM的位/开关的环境至关重要。运行。

这个问题:How to disable endpoint identification for java 1.8.181 version提出了类似的问题,但是解决方案是通过命令行更改Java环境。我在问如何在不切换环境的情况下以编程方式完成它。

还有其他有关在Java中为其他类型的SSL连接禁用主机名验证的问题/答案,但这些答案不适用于JNDI LDAP API。

2 个答案:

答案 0 :(得分:1)

就像@ Patrick-Mevzek所说的:不要这样做!

但是,如果确实需要,请按以下步骤操作:

您需要一个SocketFactory,其中包括一个虚拟的TrustManager,它只会忽略任何内容。那里有许多示例说明如何创建这样的东西。不幸的是,大多数(所有人)都使用X509TrustManager来完成工作。这将对无效的证书有效,但不会处理错误或丢失的主机名。为此,您需要一个```X509ExtendedTrustManager`:

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;

import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedTrustManager;

/**
 * This Socket factory will accept all certificates and all hostnames
 */
public class NonVerifyingSSLSocketFactory extends SocketFactory {
   private static SocketFactory nonVerifyingSSLSochetFactory;

   static {
      TrustManager [] distrustManager = new TrustManager [] {new X509ExtendedTrustManager () {
         @Override
         public void checkClientTrusted (X509Certificate [] chain, String authType, Socket socket) {

         }

         @Override
         public void checkServerTrusted (X509Certificate [] chain, String authType, Socket socket) {

         }

         @Override
         public void checkClientTrusted (X509Certificate [] chain, String authType, SSLEngine engine) {

         }

         @Override
         public void checkServerTrusted (X509Certificate [] chain, String authType, SSLEngine engine) {

         }

         public X509Certificate [] getAcceptedIssuers () {
            return null;
         }

         public void checkClientTrusted (X509Certificate [] c, String a) {
         }

         public void checkServerTrusted (X509Certificate [] c, String a) {
         }
      }};

      try {
         SSLContext sc = SSLContext.getInstance ("SSL");
         sc.init (null, distrustManager, new java.security.SecureRandom ());
         nonVerifyingSSLSochetFactory = sc.getSocketFactory ();
      } catch (GeneralSecurityException e) {
         throw new RuntimeException (e);
      }
   }

   /**
    * This method is needed. It is called by the LDAP Context to create the connection
    *
    * @see SocketFactory#getDefault()
    */
   @SuppressWarnings ("unused")
   public static SocketFactory getDefault () {
      return new NonVerifyingSSLSocketFactory ();
   }

   /**
    * @see SocketFactory#createSocket(String, int)
    */
   public Socket createSocket (String arg0, int arg1) throws IOException {
      return nonVerifyingSSLSochetFactory.createSocket (arg0, arg1);
   }

   /**
    * @see SocketFactory#createSocket(java.net.InetAddress, int)
    */
   public Socket createSocket (InetAddress arg0, int arg1) throws IOException {
      return nonVerifyingSSLSochetFactory.createSocket (arg0, arg1);
   }

   /**
    * @see SocketFactory#createSocket(String, int, InetAddress, int)
    */
   public Socket createSocket (String arg0, int arg1, InetAddress arg2, int arg3) throws IOException {
      return nonVerifyingSSLSochetFactory.createSocket (arg0, arg1, arg2, arg3);
   }

   /**
    * @see SocketFactory#createSocket(InetAddress, int, InetAddress, int)
    */
   public Socket createSocket (InetAddress arg0, int arg1, InetAddress arg2,
                               int arg3) throws IOException {
      return nonVerifyingSSLSochetFactory.createSocket (arg0, arg1, arg2, arg3);
   }

}

在InitialLdapContext环境中使用它来激活它:

env.put ("java.naming.ldap.factory.socket", NonVerifyingSSLSocketFactory.class.getName ());

经过测试:

  • openjdk版本“ 1.8.0_191”
  • oraclejdk版本“ 1.8.0_25”(此版本不需要它,但是无论如何它都不会损坏任何东西)

答案 1 :(得分:0)

在创建httpclient实例之前设置系统属性

System.setProperty("jdk.internal.httpclient.disableHostnameVerification", "true");