Java Webstart信任库SSL

7

需要一些指导。

我有一个Java Webstart应用程序,我想通过SSL连接到服务器。只需要添加以下属性:System.setProperty("javax.net.ssl.trustStore","my.keystore");

但由于从服务器下载JAWS程序无法正常工作,并且本地文件系统上没有my.keystore。所以我决定将证书分发给所有客户端,我执行了以下操作,它可以工作:

  1. 将此信任存储库读取为流(使用getResourceAsStream方法)。
  2. 将其保存在客户端机器上的任意文件中(例如sometemp)。
  3. 调用System.setProperty("javax.net.ssl.trustStore", trustStorePath);

但我确信肯定有比这更好的解决方案...有什么更好的想法吗?

public boolean validateUserFromActiveDirectory(String userId) {
                    final String MEMBER_GROUP = "CN=asdadasd,OU=asdasdasd Accounts,OU=adasdas,OU=asdasdas,DC=asdasdas,DC=asdasdas,DC=adasdasd,DC=asdasdasd";
            String employeeNumber = "";
            final String LDAP_INIT_CTX = "com.sun.jndi.ldap.LdapCtxFactory";
            final String LDAP_URL = "ldap://xx-ssssssss.eee.eee.eeeee.eeeee:636";
            final String MY_ATTRS[] = { "employeeNumber" };
            String adminPassword = "somepassword";
            String securityProtocol = "ssl";
            boolean isValidUser = false;
            try {

                  Hashtable env = new Hashtable();
                  env.put(Context.INITIAL_CONTEXT_FACTORY, LDAP_INIT_CTX);
                  env.put(Context.PROVIDER_URL, LDAP_URL);
                  env.put(Context.SECURITY_AUTHENTICATION, "simple");
                  env.put(Context.REFERRAL, "follow");
                  env.put(Context.SECURITY_PRINCIPAL, MEMBER_GROUP);
                  env.put(Context.SECURITY_CREDENTIALS, adminPassword);
                  env.put(Context.SECURITY_PROTOCOL, securityProtocol);

            //C:\Documents and Settings\yourusername\Local Settings\Temp
            File tf = File.createTempFile("someTruststore", ".jks");
            tf.deleteOnExit();
            byte buffer[] = new byte[0x1000];
               ClassLoader cl = JNDI.class.getClassLoader();
            InputStream in = cl.getResourceAsStream(
                    "someTruststore.jks");
            FileOutputStream out = new FileOutputStream(tf);
            int cnt;
            while ((cnt = in.read(buffer)) != -1)
                out.write(buffer, 0, cnt);
            in.close();
            out.close();
            System.setProperty("javax.net.ssl.trustStore", tf
                            .getAbsolutePath());

                  DirContext context = new InitialLdapContext(env, null);
                  SearchControls searchControls = new SearchControls();
                  searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
                  NamingEnumeration results = context.search(
                              "XX=ent,XX=abc,XX=aaaaa,XX=aaaa", "(sAMAccountName="
                                          + userId + ")", searchControls);

                  if (results != null && results.hasMore()) {
                      //some logic

                        }
                  }
            } catch (Exception e) {
                  e.printStackTrace();
            }
            return isValidUser;
      }

-Padur ===========================**=============

/**

* */

package util;

/**
 * @author spaduri
 *
 */
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 CustomSSLSocketFactory extends SSLSocketFactory {

    private SSLSocketFactory factory;

    public CustomSSLSocketFactory() {
        try {
            SSLContext sslcontext = null;
              // Call getKeyManagers to get suitable key managers
            KeyManager[] kms=getKeyManagers();
            if (sslcontext == null) {
                sslcontext = SSLContext.getInstance("SSL");
                sslcontext.init(kms,
                new TrustManager[] { new CustomTrustManager() },
                new java.security.SecureRandom());
            }
            factory = (SSLSocketFactory) sslcontext.getSocketFactory();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }


    public static SocketFactory getDefault() {
        return new CustomSSLSocketFactory();
    }

    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();
    }

 protected KeyManager[] getKeyManagers()
        throws IOException, GeneralSecurityException
      {
        // First, get the default KeyManagerFactory.
        String alg=KeyManagerFactory.getDefaultAlgorithm();
        KeyManagerFactory kmFact=KeyManagerFactory.getInstance(alg);

        // Next, set up the KeyStore to use. We need to load the file into
        // a KeyStore instance.

        ClassLoader cl = CustomSSLSocketFactory.class.getClassLoader();
        // read the file someTrustStore from the jar file from a classpath
        InputStream in = cl.getResourceAsStream("ssl/someTruststore.jks");
        //FileInputStream fis=new FileInputStream(adentTruststore.jks);
        KeyStore ks=KeyStore.getInstance("jks");
        ks.load(in, null);
        in.close();

        // Now we initialise the KeyManagerFactory with this KeyStore
        kmFact.init(ks, null);

        // And now get the KeyManagers
        KeyManager[] kms=kmFact.getKeyManagers();
        return kms;
      }
}

package util;
import java.security.cert.X509Certificate;

import javax.net.ssl.X509TrustManager;

public class CustomTrustManager 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];
    }
}

Laz非常感谢您的耐心,我会在有时间的时候尽力学习。目前我正在编写自己的CustomSSLSocketFactory。根据platinum solutions的示例,我正在绕过安全性检查。如果我这样做...信息是否会作为明文在网络上传递?

现在我想知道我持有的信任存储文件"sometruststore.jks"该怎么处理。我应该编写自己的自定义trustmanager软件吗?请指导我正确的方向。

-padur


该信息不会是明文。它将被加密,但不会得到认证,因为此代码将所有证书视为受信任的。您不需要编写自己的TrustManager来处理.jks文件。请查看我下面的答案,并了解您可以将KeyStore实例传递给SSLSocketFactory子类。您可以通过从类路径加载它来以与原始代码相同的方式获取该实例。 - laz
1个回答

3

您可以不依赖系统属性和文件系统即可完成此操作。像您正在做的那样将密钥库作为流读取并创建自己的SSLSocketFactory会更加清晰。

import java.net.URL;
import java.security.KeyStore;
import java.security.SecureRandom;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

...

    // assume keyStore is the KeyStore you read via getResourceAsStream
    final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
    trustManagerFactory.init(keyStore);

    final SSLContext context = SSLContext.getInstance("SSL");
    context.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());

    final URL url = new URL("https://whatever");
    final HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
    urlConnection.setSSLSocketFactory(context.getSocketFactory());

...

我没有验证,但我认为这应该可以通过Webstart工作。

更新:

您提到您正在寻找连接到活动目录,因此我猜您将使用LDAPS作为协议?如果是这样,也许this URL上的代码可以作为灵感?您需要创建javax.net.ssl.SSLSocketFactory的子类(请参见该platinumsolutions链接中的BlindSSLSocketFactoryTest),该子类包装了创建SSLContext的逻辑,并委托调用由context.getSocketFactory()创建的SSLSocketFactory

public class TrustedSSLSocketFactory extends SSLSocketFactory {
    private static SSLContext context;
    public static void initTrustedSSLSocketFactory(final KeyStore keyStore) throws Exception {
        final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
        trustManagerFactory.init(keyStore);

        final SSLContext context = SSLContext.getInstance("SSL");
        context.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
    }

    public static SocketFactory getDefault() {
        return context.getSocketFactory();
    }

    public Socket createSocket(String arg0, int arg1) throws IOException, UnknownHostException {
        return trustedFactory.createSocket(arg0, arg1);
    }

    public Socket createSocket(InetAddress arg0, int arg1) throws IOException {
        return trustedFactory.createSocket(arg0, arg1);
    }

    public Socket createSocket(String arg0, int arg1, InetAddress arg2, int arg3) throws IOException, UnknownHostException {
        return trustedFactory.createSocket(arg0, arg1, arg2, arg3);
    }

    public Socket createSocket(InetAddress arg0, int arg1, InetAddress arg2, int arg3) throws IOException {
        return trustedFactory.createSocket(arg0, arg1, arg2, arg3);
    }
}

希望这可以编译,但我现在无法测试!还要注意在initTrustedSSLSocketFactory上使用throws子句的懒惰。
然后当您设置LDAP环境时,请使用:
TrustedSSLSocketFactory.initTrustedSSLSocketFactory(keyStore);
env.put("java.naming.ldap.factory.socket", TrustedSSLSocketFactory.class.getName())

希望这个示例代码与platinumsolutions类似。希望这更符合您的要求?

感谢laz提供的解决方案。上次我没有正确地提出问题。我应该连接到活动目录而不是HTTPS服务器,我需要验证用户信息。在这种情况下,他们没有提供任何URL,只给了我一个证书.jks文件,因此我认为HTTPURLConnection并不是一个好主意。我想在获取SSLContext之后需要调用不同的API来验证它。如果您有其他想法,请告诉我。-Padur - SPD
是的,那个platinumsolutions代码正在绕过证书验证(请参见http://blog.platinumsolutions.com/node/79)。将其视为概念的示例,而不是您应该执行的确切操作。 - laz
你好Laz,非常感谢你的帮助。 当我尝试使用你的解决方案时,它会抛出以下错误:Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:324) at com.sun.jndi.ldap.Connection.createSocket(Connection.java:311) at com.sun.jndi.ldap.Connection.<init>(Connection.java:181) - SPD
Laz,我尝试了你的新代码,但没有成功,不过我已经编写了自己的示例customSSLSocketFactory。我将其复制到问题文本区域中,请您审核并告诉我如何继续。谢谢 Padur - SPD
看起来这是我更新了新代码的方式。你能看一下吗?谢谢 - Padur - SPD
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接