PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

 

注:网上搜来的快照,暂未验证

在java代码中请求https链接的时候,可能会报下面这个错误

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

原因是没有证书。在浏览器中直接使用url访问是可以的,应该是浏览器之前就保存过对应的.cer证书。

解决方法有两种,从目标机器获得有效证书或者忽略证书信任问题。

一、获得目标机器有效证书

1、编译安装证书程序 javac InstallCert.java(代码如下)


  1 /*
  2 
  3  * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
  4 
  5  *
  6 
  7  * Redistribution and use in source and binary forms, with or without
  8 
  9  * modification, are permitted provided that the following conditions
 10 
 11  * are met:
 12 
 13  *
 14 
 15  *  - Redistributions of source code must retain the above copyright
 16 
 17  *   notice, this list of conditions and the following disclaimer.
 18 
 19  *
 20 
 21  *  - Redistributions in binary form must reproduce the above copyright
 22 
 23  *   notice, this list of conditions and the following disclaimer in the
 24 
 25  *   documentation and/or other materials provided with the distribution.
 26 
 27  *
 28 
 29  *  - Neither the name of Sun Microsystems nor the names of its
 30 
 31  *   contributors may be used to endorse or promote products derived
 32 
 33  *   from this software without specific prior written permission.
 34 
 35  *
 36 
 37  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 38 
 39  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 40 
 41  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 42 
 43  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 44 
 45  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 46 
 47  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 48 
 49  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 50 
 51  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 52 
 53  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 54 
 55  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 56 
 57  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 58 
 59  */
 60 
 61 /**
 62 
 63  * http://blogs.sun.com/andreas/resource/InstallCert.java
 64 
 65  * Use:
 66 
 67  * java InstallCert hostname
 68 
 69  * Example:
 70 
 71  *% java InstallCert ecc.fedora.redhat.com
 72 
 73  */
 74 
 75 import javax.net.ssl.*;
 76 
 77 import java.io.*;
 78 
 79 import java.security.KeyStore;
 80 
 81 import java.security.MessageDigest;
 82 
 83 import java.security.cert.CertificateException;
 84 
 85 import java.security.cert.X509Certificate;
 86 
 87 /**
 88 
 89  * Class used to add the server's certificate to the KeyStore
 90 
 91  * with your trusted certificates.
 92 
 93  */
 94 
 95 public class InstallCert {
 96 
 97     public static void main(String[] args) throws Exception {
 98 
 99         String host;
100 
101         int port;
102 
103         char[] passphrase;
104 
105         if ((args.length == 1) || (args.length == 2)) {
106 
107             String[] c = args[0].split(":");
108 
109             host = c[0];
110 
111             port = (c.length == 1) ? 443 : Integer.parseint(c[1]);
112 
113             String p = (args.length == 1) ? "changeit" : args[1];
114 
115             passphrase = p.toCharArray();
116 
117         } else {
118 
119             System.out.println("Usage: java InstallCert <host>[:port] [passphrase]");
120 
121             return;
122 
123         }
124 
125         File file = new File("jssecacerts");
126 
127         if (file.isFile() == false) {
128 
129             char SEP = File.separatorchar;
130 
131             File dir = new File(System.getProperty("java.home") + SEP
132 
133                       + "lib" + SEP + "security");
134 
135             file = new File(dir, "jssecacerts");
136 
137             if (file.isFile() == false) {
138 
139                 file = new File(dir, "cacerts");
140 
141             }
142 
143         }
144 
145         System.out.println("Loading KeyStore " + file + "...");
146 
147         InputStream in = new FileInputStream(file);
148 
149         KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
150 
151         ks.load(in, passphrase);
152 
153         in.close();
154 
155         SSLContext context = SSLContext.getInstance("TLS");
156 
157         TrustManagerFactory tmf =
158 
159                 TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
160 
161         tmf.init(ks);
162 
163         X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
164 
165         SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
166 
167         context.init(null, new TrustManager[]{
168 
169             tm
170 
171         }
172 
173         , null);
174 
175         SSLSocketFactory factory = context.getSocketFactory();
176 
177         System.out.println("Opening connection to " + host + ":" + port + "...");
178 
179         SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
180 
181         socket.setSoTimeout(10000);
182 
183         try {
184 
185             System.out.println("Starting SSL handshake...");
186 
187             socket.startHandshake();
188 
189             socket.close();
190 
191             System.out.println();
192 
193             System.out.println("No errors, certificate is already trusted");
194 
195         }
196 
197         catch (SSLException e) {
198 
199             System.out.println();
200 
201             e.printStackTrace(System.out);
202 
203         }
204 
205         X509Certificate[] chain = tm.chain;
206 
207         if (chain == null) {
208 
209             System.out.println("Could not obtain server certificate chain");
210 
211             return;
212 
213         }
214 
215         BufferedReader reader =
216 
217                 new BufferedReader(new InputStreamReader(System.in));
218 
219         System.out.println();
220 
221         System.out.println("Server sent " + chain.length + " certificate(s):");
222 
223         System.out.println();
224 
225         MessageDigest sha1 = MessageDigest.getInstance("SHA1");
226 
227         MessageDigest md5 = MessageDigest.getInstance("MD5");
228 
229         for (int i = 0; i < chain.length; i++) {
230 
231             X509Certificate cert = chain[i];
232 
233             System.out.println
234 
235                       (" " + (i + 1) + " Subject " + cert.getSubjectDN());
236 
237             System.out.println("  Issuer " + cert.getIssuerDN());
238 
239             sha1.update(cert.getEncoded());
240 
241             System.out.println("  sha1  " + toHexString(sha1.digest()));
242 
243             md5.update(cert.getEncoded());
244 
245             System.out.println("  md5   " + toHexString(md5.digest()));
246 
247             System.out.println();
248 
249         }
250 
251         System.out.println("Enter certificate to add to trusted keystore or 'q' to quit: [1]");
252 
253         String line = reader.readLine().trim();
254 
255         int k;
256 
257         try {
258 
259             k = (line.length() == 0) ? 0 : Integer.parseint(line) - 1;
260 
261         }
262 
263         catch (NumberFormatException e) {
264 
265             System.out.println("KeyStore not changed");
266 
267             return;
268 
269         }
270 
271         X509Certificate cert = chain[k];
272 
273         String alias = host + "-" + (k + 1);
274 
275         ks.setCertificateEntry(alias, cert);
276 
277         OutputStream out = new FileOutputStream("jssecacerts");
278 
279         ks.store(out, passphrase);
280 
281         out.close();
282 
283         System.out.println();
284 
285         System.out.println(cert);
286 
287         System.out.println();
288 
289         System.out.println
290 
291                 ("Added certificate to keystore 'jssecacerts' using alias '"
292 
293                     + alias + "'");
294 
295     }
296 
297     private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
298 
299     private static String toHexString(byte[] bytes) {
300 
301         StringBuilder sb = new StringBuilder(bytes.length * 3);
302 
303         for (int b : bytes) {
304 
305             b &= 0xff;
306 
307             sb.append(HEXDIGITS[b >> 4]);
308 
309             sb.append(HEXDIGITS[b & 15]);
310 
311             sb.append(' ');
312 
313         }
314 
315         return sb.toString();
316 
317     }
318 
319     private static class SavingTrustManager implements X509TrustManager {
320 
321         private final X509TrustManager tm;
322 
323         private X509Certificate[] chain;
324 
325         SavingTrustManager(X509TrustManager tm) {
326 
327             this.tm = tm;
328 
329         }
330 
331         public X509Certificate[] getAcceptedIssuers() {
332 
333             throw new UnsupportedOperationException();
334 
335         }
336 
337         public void checkClientTrusted(X509Certificate[] chain, String authType)
338 
339                 throws CertificateException {
340 
341             throw new UnsupportedOperationException();
342 
343         }
344 
345         public void checkServerTrusted(X509Certificate[] chain, String authType)
346 
347                 throws CertificateException {
348 
349             this.chain = chain;
350 
351             tm.checkServerTrusted(chain, authType);
352 
353         }
354 
355     }
356 
357 }

 



2、运行安装证书程序生成证书

java InstallCert www.xxx.com

例如:java InstalCert smtp.zhangsan.com:465 admin
如果不加参数password和host的端口号,上面的获取证书程序中默认给的端口号是:443,密码是:changeit

3、根据运行提示信息,输入1,回车,在当前目录下生成名为: jssecacerts 的证书

将证书放置到$JAVA_HOME/jre/lib/security目录下, 切记该JDK的jre是工程所用的环境!!!

或者:

System.setProperty("javax.net.ssl.trustStore", "你的jssecacerts证书路径");

可以更改密码,在security目录下运行命令

keytool -storepasswd -new xxxcom -keystore cacerts

就可以修改密码,修改后使用命令

keytool -list -v -keystore cacerts

查看文件的信息,会提示需要密码才能查看,如果输入密码与修改后的密码匹配,说明修改成功了。

PS:至此这种方式可以成功使用ssl了,另外再补充一下,根据刚才生成的文件jssecacerts,可以生成cer文件,

命令如下

keytool -export -alias xxx.com-1 -keystore jssecacerts -rfc -file xxx.cer

如上,之前的工具类中默认命名别名是加上"-1"。使用InstallCert设置的密码需要跟cacerts文件中的密码一致,

如果修改过密码,就需要修改InstallCert类中对应的密码字符串,否则会有下面这个异常:

java.security.UnrecoverableKeyException: Password verification failed

二、忽略证书信任问题

源码:http://mengyang.iteye.com/blog/575671

一定要注意需要在connection创建之前调用文章里所述的方法,像这个样子:

trustAllHttpsCertificates();

HostnameVerifier hv = new HostnameVerifier() {

    public boolean verify(String urlHostName, SSLSession session) {

      return true;

    }

  };
    

trustAllHttpsCertificates();

HostnameVerifier hv = new HostnameVerifier() {

    public boolean verify(String urlHostName, SSLSession session) {

      return true;

    }

  };

HttpsURLConnection.setDefaultHostnameVerifier(hv);

connection = (HttpURLConnection) url.openConnection();

posted @ 2020-06-01 09:27  夜旦  阅读(2315)  评论(0编辑  收藏  举报