主要原理是:使用socket尝试对目标服务器进行通信,如果通信失败,证明没有证书,不过此时的证书已悄悄发送到客户端了。
以前写过这个工具类了:
http://leisuredev.iteye.com/admin/blogs/714742今天稍微注释并整理了一个版本:
1 package com.leisure.cert;
2
3 import java.io.File;
4 import java.io.FileInputStream;
5 import java.io.FileOutputStream;
6 import java.io.InputStream;
7 import java.io.OutputStream;
8 import java.security.KeyStore;
9 import java.security.cert.CertificateException;
10 import java.security.cert.X509Certificate;
11
12 import javax.net.ssl.HostnameVerifier;
13 import javax.net.ssl.HttpsURLConnection;
14 import javax.net.ssl.SSLContext;
15 import javax.net.ssl.SSLSession;
16 import javax.net.ssl.SSLSocket;
17 import javax.net.ssl.SSLSocketFactory;
18 import javax.net.ssl.TrustManager;
19 import javax.net.ssl.TrustManagerFactory;
20 import javax.net.ssl.X509TrustManager;
21
22 /**
23 *
24 * Java在线自动获取并安装证书工具类
25 * @author Leisure
26 * @version 1.1
27 *
28 */
29 public class CertManager {
30
31 public static void main(String[] args) {
32 try {
33 trustCert("d:\\", "www.google.com.hk", 443, "changeit");
34 } catch (Exception e) {
35 e.printStackTrace();
36 }
37 }
38
39 /**
40 *
41 * @param dir 证书所在路径
42 * @param host 主机地址
43 * @param port 端口
44 * @param password 证书密码
45 * @throws Exception
46 */
47 public static void trustCert(String dir, String host, int port, String password) throws Exception {
48 // 如果证书颂给的名称与所通信的域名不一致的话,那么需要重写校验方法
49 HostnameVerifier hv = new HostnameVerifier() {
50 @Override
51 public boolean verify(String urlHostName, SSLSession session) {
52 return urlHostName.equals(session.getPeerHost());
53 }
54 };
55 HttpsURLConnection.setDefaultHostnameVerifier(hv);
56
57 // 信任管理器工厂
58 TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
59 File file = new File(dir + host + ".cer");
60 file = makeSureFile(file);
61 KeyStore ks = getKeyStore(file, password);
62 tmf.init(ks);
63
64 SSLContext context = SSLContext.getInstance("SSL");
65 X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
66 SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
67 context.init(null, new TrustManager[] { tm }, null);
68
69 // 尝试使用socket对目标主机进行通信
70 SSLSocketFactory factory = context.getSocketFactory();
71 SSLSocket socket = (SSLSocket)factory.createSocket(host, port);
72 socket.setSoTimeout(1000);
73 try {
74 // 如果直接通信没问题的话,就不会报错,也不必获取证书
75 // 如果报错的话,很有可能没有证书
76 socket.startHandshake();
77 } catch(Exception e) {
78 e.printStackTrace();
79 } finally {
80 if(socket != null) {
81 try {
82 socket.close();
83 } catch(Exception e) {}
84 socket = null;
85 }
86 X509Certificate[] chain = tm.getChain();
87 if(chain != null) {
88 System.out.println("服务器返回:" + chain.length + " 个证书");
89 OutputStream out = null;
90 for(int i = 0; i < chain.length; i++) {
91 try {
92 X509Certificate x509Cert = chain[i];
93 String alias = host + (i > 0 ? i + "" : "");
94 ks.setCertificateEntry(alias, x509Cert);
95
96 String certFile = dir + alias + ".cer";
97 out = new FileOutputStream(certFile);
98 ks.store(out, password.toCharArray());
99 out.close();
100
101 System.setProperty("javax.net.ssl.trustStore", certFile);
102 System.out.println("第" + (i + 1) + "个证书安装成功");
103 } catch(Exception e) {
104 e.printStackTrace();
105 continue;
106 } finally {
107 try {
108 if(out != null) {
109 out.close();
110 }
111 out = null;
112 } catch(Exception e) {}
113 }
114 }
115 }
116 }
117 }
118
119 /**
120 * 确保文件存在
121 * @param file
122 * @return
123 */
124 private static File makeSureFile(File file) {
125 if (file.isFile() == false) {
126 char SEP = File.separatorChar;
127 File dir = new File(System.getProperty("java.home") + SEP + "lib" + SEP + "security");
128 file = new File(dir, file.getName());
129 if (file.isFile() == false) {
130 file = new File(dir, "cacerts");
131 }
132 }
133 return file;
134 }
135
136 /**
137 * 获取keystore
138 * @param file
139 * @param password
140 * @return
141 * @throws Exception
142 */
143 private static KeyStore getKeyStore(File file, String password) throws Exception {
144 InputStream in = new FileInputStream(file);
145 KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
146 char[] passphrase = password.toCharArray();
147 ks.load(in, passphrase);
148 in.close();
149 return ks;
150 }
151
152 public static class SavingTrustManager implements X509TrustManager {
153 private final X509TrustManager tm;
154 private X509Certificate[] chain;
155
156 public SavingTrustManager(X509TrustManager tm) {
157 this.tm = tm;
158 }
159
160 public X509TrustManager getTM() {
161 return tm;
162 }
163
164 public X509Certificate[] getChain() {
165 return chain;
166 }
167
168 public X509Certificate[] getAcceptedIssuers() {
169 throw new UnsupportedOperationException();
170 }
171
172 public void checkClientTrusted(X509Certificate[] chain, String authType)
173 throws CertificateException {
174 throw new UnsupportedOperationException();
175 }
176
177 public void checkServerTrusted(X509Certificate[] chain, String authType)
178 throws CertificateException {
179 this.chain = chain;
180 tm.checkServerTrusted(chain, authType);
181 }
182 }
183 }
184