2

我正在尝试使用 java Web 应用程序动态更改信任库路径。

我正在开发 struts 应用程序,登录是基于 ldap 通过安全套接字层(ssl)连接。

为了与 ssl 连接,我使用 java keytool 选项创建了 .cer 文件。

现在我可以连接 ldap 并且可以从 ldap 中检索用户信息。

当我动态更改 ssl 证书(用于测试的无效证书)时,它不会给出任何例外。但是当我重新启动tomcat服务器时它可以工作。

以下是我正在尝试的代码

try{
        java.io.InputStream in = new java.io.FileInputStream("C:\\test.cer");
        java.security.cert.Certificate c = java.security.cert.CertificateFactory.getInstance("X.509").generateCertificate(in);
        java.security.KeyStore ks = java.security.KeyStore.getInstance("JKS");
        ks.load(null); 
        if (!ks.containsAlias("alias ldap")) { 
            ks.setCertificateEntry("alias ldap", c);
        }
        java.io.OutputStream out = new java.io.FileOutputStream("C:\\ldap.jks");
        char[] kspass = "changeit".toCharArray();
        ks.store(out, kspass);
        out.close();
        System.setProperty("javax.net.ssl.trustStore", "C:\\ldap.jks");
        System.setProperty("javax.net.ssl.trustStorePassword", "changeit");    
    }catch(Exception e){
        e.printStackTrace();
    }

我对代码有什么错误吗?我需要添加任何新代码来动态连接吗?

注意:我动态给出了无效文件,而不是 c:\ldap.jks 文件。它没有给出任何例外。

已编辑(使用自定义 TrustManager 检查):

我还实现了 TrustManager 并使用自定义信任管理器初始化 ssl 上下文。但我无法获得预期的行为

你能帮帮我吗?我试过的代码是

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.UUID;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

public class TestDynamicSSLCert {

    public static void main(String[] args)throws NamingException,IOException {
        DataInputStream din = new DataInputStream (System.in);
        String yes = "yes";

        String certpath = "C:\\cert.cer";
        String ldappath1 = "C:\\ldap.jks";
        String ldappath2 = "C:\\ldap.jks";    // setting valid key store path    

        while("yes".equalsIgnoreCase(yes.trim())){            
            System.out.println(" ldappath2 : "+ldappath2);
            Hashtable env = new Hashtable();
            env.put(Context.SECURITY_AUTHENTICATION, "simple");
            env.put(Context.SECURITY_PRINCIPAL,"uid=admin,ou=system");
            env.put(Context.SECURITY_CREDENTIALS, "secret");
            env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
            env.put(Context.PROVIDER_URL, "ldaps://172.16.12.4:636/ou=system");
            try {

                java.io.InputStream in = new java.io.FileInputStream(certpath);
                java.security.cert.Certificate c = java.security.cert.CertificateFactory.getInstance("X.509").generateCertificate(in);
                java.security.KeyStore ks = java.security.KeyStore.getInstance("JKS");
                ks.load(null);
                if (!ks.containsAlias("alias ldap")) {
                    ks.setCertificateEntry("alias ldap", c);
                }
                java.io.OutputStream out = new java.io.FileOutputStream(ldappath1);
                char[] kspass = "changeit".toCharArray();
                ks.store(out, kspass);
                out.close();
                System.setProperty("javax.net.ssl.trustStore", ldappath2);
                System.setProperty("javax.net.ssl.trustStorePassword", "changeit");              
                // Custorm trust manager 
                MyX509TrustManager reload = new MyX509TrustManager(ldappath2,c);
                TrustManager[] tms = new TrustManager[] { reload };
                javax.net.ssl.SSLContext sslCtx = javax.net.ssl.SSLContext.getInstance("SSL");
                sslCtx.init(null, tms, null);  
                // Custom trust manager
            } catch (Exception e) {
                e.printStackTrace();
            }
            DirContext ctx = new InitialDirContext(env);
            NamingEnumeration enm = ctx.list("");
            while (enm.hasMore()) {
                System.out.println(enm.next());
            }                    
            ctx.close();
            System.out.println(" Go again by yes/no :");
            yes = din.readLine();
            ldappath2 = "C:\\invalidldap.jks"; // setting invalid keystore path

        }
    }
}

class MyX509TrustManager implements X509TrustManager {

    private final String trustStorePath;
    private X509TrustManager trustManager;
    private List<Certificate> tempCertList = new ArrayList<Certificate>();

    public MyX509TrustManager(String tspath,Certificate cert)throws Exception{
        this.trustStorePath = tspath;        
        tempCertList.add(cert);
        reloadTrustManager();
    }

    public MyX509TrustManager(String tspath)
            throws Exception {
        this.trustStorePath = tspath;
        reloadTrustManager();
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain,
            String authType) throws CertificateException {
        trustManager.checkClientTrusted(chain, authType);
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain,
            String authType) throws CertificateException {
        try {
            trustManager.checkServerTrusted(chain, authType);
        } catch (CertificateException cx) {
            addServerCertAndReload(chain[0], true);
            trustManager.checkServerTrusted(chain, authType);
        }
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        X509Certificate[] issuers = trustManager.getAcceptedIssuers();
        return issuers;
    }

    private void reloadTrustManager() throws Exception {

        // load keystore from specified cert store (or default)
        KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
        InputStream in = new FileInputStream(trustStorePath);
        try {
            ts.load(in, null);
        } finally {
            in.close();
        }

        // add all temporary certs to KeyStore (ts)
        for (Certificate cert : tempCertList) {         
            ts.setCertificateEntry(UUID.randomUUID().toString(), cert);
        }

        // initialize a new TMF with the ts we just loaded
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ts);

        // acquire X509 trust manager from factory
        TrustManager tms[] = tmf.getTrustManagers();

        for (int i = 0; i < tms.length; i++) {
            if (tms[i] instanceof X509TrustManager) {                
                trustManager = (X509TrustManager) tms[i];
                return;
            }
        }

        throw new NoSuchAlgorithmException("No X509TrustManager in TrustManagerFactory");
    }

    private void addServerCertAndReload(Certificate cert,
            boolean permanent) {
        try {
            if (permanent) {
                // import the cert into file trust store
                // Google "java keytool source" or just ...
                Runtime.getRuntime().exec("keytool -importcert ...");
            } else {
                tempCertList.add(cert);
            }
            reloadTrustManager();
        } catch (Exception ex) { /* ... */ }
    }
}

预期行为:

ldap 连接应该使用有效的密钥库文件成功(在第一个循环期间)。如果用户给出是,则分配无效的密钥库并且需要产生异常并且不应该连接到 ldap

实际行为:

对于两个有效的密钥库文件,我都能够从 ldap 检索信息。

笔记 :

如果我设置 String ldappath2 = "C:\invalidldap.jks"; 一开始,它给出了例外。

为什么要这样做?

@EJP,因为,我需要安全地开发基于 ldap 身份验证的模块。模块应该支持多个 ldap 服务器。ldap 设置可以从 UI(具有 ui 以获取 ldaphost、端口、basedn 和 ssl 证书等详细信息的网页)插入,并且此详细信息应进入数据库。同时证书也存在于数据库中。模块的工作只是从 ldap 检索用户并将其存储到另一个表中。因此,如果我们使用新的证书方式更改新的 ldap 服务器设置,System.setProperty("javax.net.ssl.trustStore","truststorepath") 将失败。你同意我的解释吗?

4

2 回答 2

3

你是对的。更改密钥库或信任库时必须重新启动 Tomcat。您不需要编写代码来加载客户端证书,您只需要确保您正在处理其证书由您信任的 CA 签名的服务器。在运行时添加新证书是非常不安全的。

于 2012-05-08T01:14:49.233 回答
0

有没有其他方法可以在不使用上述步骤的情况下安全地连接 ldap?

是的,但你为什么认为你需要知道?

每当更新 trustStore 属性时,是否应该重新启动应用程序(tomcat 或单个 java 文件)?

是的。

于 2012-05-09T10:24:20.493 回答