最近产品中需要在RCP中通过SSL方式来访问Tomcat下的应用,查阅了一些资料经过调试,最终连接成功,整理过程如下

一.使用java的keytool来生成keystore和证书

1、生成服务器证书库


keytool -validity 365 -genkey -v -alias server -keyalg RSA -keystore D:\ssl\server.keystore -dname "CN=127.0.0.1,OU=disoft,O=disoft,L=ChaoYang,ST=Beijing,c=cn" -storepass 123456 -keypass 123456


2、生成客户端证书库


keytool -validity 365 -genkeypair -v -alias client -keyalg RSA -storetype PKCS12 -keystore D:\ssl\client.p12 -dname "CN=client,OU=disoft,O=disoft,L=ChaoYang,ST=Beijing,c=cn" -storepass 123456 -keypass 123456


3、从客户端证书库中导出客户端证书


keytool -export -v -alias client -keystore D:\ssl\client.p12 -storetype PKCS12 -storepass 123456 -rfc -file D:\ssl\client.cer


4、从服务器证书库中导出服务器证书


keytool -export -v -alias server -keystore D:\ssl\server.keystore -storepass 123456 -rfc -file D:\ssl\server.cer


5、生成客户端信任证书库(由服务端证书生成的证书库)


keytool -import -v -alias server -file D:\ssl\server.cer -keystore D:\ssl\client.truststore -storepass 123456


6、将客户端证书导入到服务器证书库(使得服务器信任客户端证书)


keytool -import -v -alias client -file D:\ssl\client.cer -keystore D:\ssl\server.keystore -storepass 123456

之后ssl文件夹下会有5个文件,分别是server.keystore,client.p12,client.cer,server.cer,client.truststore

注意:其中的server.keystore既是服务器的证书库,又作为服务器端对客户端的信任证书库.

步骤1中的CN最好使用IP地址,通过这个IP地址对支持SSL的server进行访问.

之后双击client.cer对客户端证书进行导入以支持浏览器的访问

用keytool生成的证书是私人的证书,并不是权威CA机构颁发的证书,但是并不影响开发。

二.配置Tomcat的SSL

Tomcat的server.xml文件需要进行相应的配置,默认这段是注释掉的,启用即可

<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="true" sslProtocol="TLS"
keystoreFile="D:/ssl/server.keystore" keystorePass="123456"
truststoreFile="D:/ssl/server.keystore" truststorePass="123456"
/>


上面的clientAuth="true"这样的设置是说明需要支持SSL双向认证.keystoreFile和keystorePass配置好自己server的keystore的路径和密码.

启动tomcat,通过https://127.0.0.1:8443 来访问

三.Eclipse RCP中通过SSL连接server

直接上代码

private static SSLContext getSSLContext() {
        if (sslcontext == null) {
            try {
                String keystorePath = System.getProperty(TAC_SSL_SYSTEM_KEY);
                String keyStorePass = System.getProperty(TAC_SSL_SYSTEM_PASS);
                if (keystorePath == null) {
                    // if user does not set the keystore path in the .ini,we need to look for the keystore file under
                    // the root dir of product
                    String userDir = System.getProperty("user.dir");
                    File keystorePathFile = new File(userDir + "/" + TAC_SSL_KEYSTORE);
                    keystorePath = keystorePathFile.getAbsolutePath();
                }
                if (keyStorePass == null) {
                    // if user does not set the password in the .ini,we only can make it empty by
                    // default,but not sure the ssl can connect
                    keyStorePass = "";
                }
                File keystoreFile = new File(keystorePath);
                if (!keystoreFile.canRead()) {
                    throw new RuntimeException("Can't find or read the SSL Keystore file at: '" + keystoreFile.getAbsolutePath()
                            + "'");
                }
                sslcontext = SSLContext.getInstance("SSL");
                KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
                TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
                KeyStore ks = KeyStore.getInstance("JKS");
                KeyStore tks = KeyStore.getInstance("JKS");
                ks.load(new FileInputStream(keystorePath), keyStorePass.toCharArray());
                tks.load(new FileInputStream(keystorePath), keyStorePass.toCharArray());
                kmf.init(ks, keyStorePass.toCharArray());
                tmf.init(tks);
                sslcontext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (KeyManagementException e) {
                e.printStackTrace();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return sslcontext;
    }

上述代码是用来初始化SSL上下文,可以将生成的keystore(这里仍然是server.keystore)的路径和密码配置在RCP产品中的*.ini文件(相当于Eclipse中VM Argument的参数配置)里面,使用的时候用系统属性进行读取.

public static String getContent(String adminUrl, String urlString) throws PersistenceException {
        StringBuffer buffer = new StringBuffer();
        try {
            URL url = new URL(adminUrl + urlString);
            BufferedReader in = null;
            if (adminUrl.startsWith("https://")) {
                final SSLSocketFactory socketFactory = getSSLContext().getSocketFactory();
                HttpsURLConnection httpsCon = (HttpsURLConnection) url.openConnection();
                httpsCon.setSSLSocketFactory(socketFactory);
                httpsCon.setHostnameVerifier(new HostnameVerifier() {
                    @Override
                    public boolean verify(String arg0, SSLSession arg1) {
                        return true;
                    }
                });
                httpsCon.connect();
                in = new BufferedReader(new InputStreamReader(httpsCon.getInputStream()));
            } else {
                in = new BufferedReader(new InputStreamReader(url.openStream()));
            }
            String inputLine;
            while ((inputLine = in.readLine()) != null) {
                buffer.append(inputLine);
            }
            in.close();
        } catch (Exception e) {
            throw new PersistenceException("Unable to access to url : " + adminUrl);
        }
        return buffer.toString();
    }

上述代码用java的HttpsURLConnection来建立SSL连接.
整个配置过程结束,成功连接.