当密钥库文件通过Web应用程序动态更改时,无法正常工作ssl连接

时间:2012-05-07 13:28:58

标签: java ssl keystore keytool

我正在尝试使用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中检索信息。

注意:

如果我设置字符串ldappath2 =&#34; C:\ invalidldap.jks&#34 ;;在开始时,它给出例外。

为什么要这样做?

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

2 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:0)

  

有没有其他方法可以安全地连接ldap而不使用上面的方法   步骤是什么?

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

  

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