* 若是要了解SSL历史也能够看看这篇文章。html
经常看到的回答是直接经过信任全部来支持, 这不优雅; 优雅的操做应该:java
# 方式1: 导出DER格式的证书 # 这里须要经过指定servername来保证导出的证书和当前域名匹配 openssl s_client -showcerts -connect self-signed.badssl.com:443 -servername self-signed.badssl.com </dev/null 2>/dev/null|openssl x509 -outform der >self-signed.badssl.com.der
private SSLContext sslContext(File certificateFile, String certificateType) { InputStream inputStream = null; try { inputStream = new FileInputStream(certificateFile); CertificateFactory cf = CertificateFactory.getInstance(certificateType); Certificate certificate = cf.generateCertificate(inputStream); System.out.println("ca=" + ((X509Certificate) certificate).getSubjectDN()); String alias = ((X509Certificate) certificate).getSubjectDN().toString(); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null, null); keyStore.setCertificateEntry(alias, certificate); // Create a KeyStore containing our trusted CAs SSLContext sslcontext = SSLContexts.custom() .loadTrustMaterial(keyStore, new TrustSelfSignedStrategy()) .build(); return sslcontext; } catch (IOException e) { throw new RuntimeException(e); } catch (CertificateException e) { throw new RuntimeException(e); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } catch (KeyStoreException e) { throw new RuntimeException(e); } catch (KeyManagementException e) { throw new RuntimeException(e); } finally { if (inputStream != null) try { inputStream.close(); } catch (IOException e) { } } } @Test public void testSelfSign() throws IOException { File certFile = ResourceUtils.getFile(this.getClass().getResource("/root.cer")); CloseableHttpClient httpclient = HttpClients.custom() .setSSLContext(sslContext(certFile, "X.509")) // .build(); String body = Executor.newInstance(httpclient).execute(Request .Get("https://self-signed.badssl.com/")) .returnContent() .asString(); System.out.println(body); }
一般你获得的答案是经过修改${JRE_HOME}/lib/security/java.security
目录下某些配置项来取消高版本SDK对某些不安全SSL协议版本或算法的限制。算法
各版本对SSL的支持状况segmentfault
JDK8 | JDK7 | JDK6 | |
---|---|---|---|
TLS Protocols | TLSv1.2 (default)<br/>TLSv1.1<br/>TLSv1<br/>SSLv3 | TLSv1.2<br/>TLSv1.1<br/>TLSv1 (default)<br/>SSLv3 | TLS v1.1 (JDK 6 update 111 and above)<br/>TLSv1 (default)<br/>SSLv3 |
JSSE Ciphers: | Ciphers in JDK 8 | Ciphers in JDK 7 | Ciphers in JDK 6 |
Reference: | JDK 8 JSSE | JDK 7 JSSE | JDK 6 JSSE |
Java Cryptography Extension, Unlimited Strength (explained later) | JCE for JDK 8 | JCE for JDK 7 | JCE for JDK 6 |
* 在2015年1月发布的升级补丁中也已经禁用对SSLv3的支持。安全
因此为何会出如今某些高版本没法访问某些HTTPS站点的缘由就是因为有如下可能:服务器
JSSE Ciphers
和本地支持的JSSE Ciphers
没有共同项致使没法正常选择加密算法。@Test public void sslSupport() throws IOException { SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket soc = (SSLSocket) factory.createSocket(); // Returns the names of the protocol versions which are // currently enabled for use on this connection. String[] protocols = soc.getEnabledProtocols(); System.out.println("Enabled protocols:"); for (String s : protocols) { System.out.println(s); } }
输出:oracle
Enabled protocols: TLSv1 TLSv1.1 TLSv1.2
String[] cipers = soc.getEnabledCipherSuites(); System.out.println("Enabled CipherSutes:"); for (String s : cipers) { System.out.println(s); }
输出tcp
Enabled CipherSutes: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 TLS_RSA_WITH_AES_256_CBC_SHA256 TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA TLS_RSA_WITH_AES_256_CBC_SHA TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA TLS_ECDH_RSA_WITH_AES_256_CBC_SHA TLS_DHE_RSA_WITH_AES_256_CBC_SHA TLS_DHE_DSS_WITH_AES_256_CBC_SHA TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 TLS_RSA_WITH_AES_128_CBC_SHA256 TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA TLS_RSA_WITH_AES_128_CBC_SHA TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA TLS_ECDH_RSA_WITH_AES_128_CBC_SHA TLS_DHE_RSA_WITH_AES_128_CBC_SHA TLS_DHE_DSS_WITH_AES_128_CBC_SHA TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 TLS_RSA_WITH_AES_256_GCM_SHA384 TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 TLS_RSA_WITH_AES_128_GCM_SHA256 TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 TLS_EMPTY_RENEGOTIATION_INFO_SCSV
这里我推荐使用nmap
检测:ide
nmap --script ssl-enum-ciphers -p 443 badssl.com
输出:oop
Starting Nmap 7.70 ( https://nmap.org ) at 2019-01-01 22:26 CST Nmap scan report for badssl.com (104.154.89.105) Host is up (0.32s latency). rDNS record for 104.154.89.105: 105.89.154.104.bc.googleusercontent.com PORT STATE SERVICE 443/tcp open https | ssl-enum-ciphers: | TLSv1.0: | ciphers: | TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A | TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A | TLS_DHE_RSA_WITH_AES_128_CBC_SHA (dh 2048) - A | TLS_DHE_RSA_WITH_AES_256_CBC_SHA (dh 2048) - A | TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA (secp256r1) - C | TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A | TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A | TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C | TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA (dh 2048) - A | TLS_RSA_WITH_CAMELLIA_256_CBC_SHA (rsa 2048) - A | TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA (dh 2048) - A | TLS_RSA_WITH_CAMELLIA_128_CBC_SHA (rsa 2048) - A | compressors: | NULL | cipher preference: server | warnings: | 64-bit block cipher 3DES vulnerable to SWEET32 attack | TLSv1.1: | ciphers: | TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A | TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A | TLS_DHE_RSA_WITH_AES_128_CBC_SHA (dh 2048) - A | TLS_DHE_RSA_WITH_AES_256_CBC_SHA (dh 2048) - A | TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA (secp256r1) - C | TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A | TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A | TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C | TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA (dh 2048) - A | TLS_RSA_WITH_CAMELLIA_256_CBC_SHA (rsa 2048) - A | TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA (dh 2048) - A | TLS_RSA_WITH_CAMELLIA_128_CBC_SHA (rsa 2048) - A | compressors: | NULL | cipher preference: server | warnings: | 64-bit block cipher 3DES vulnerable to SWEET32 attack | TLSv1.2: | ciphers: | TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (secp256r1) - A | TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (secp256r1) - A | TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (dh 2048) - A | TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (dh 2048) - A | TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (secp256r1) - A | TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A | TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (secp256r1) - A | TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A | TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 (dh 2048) - A | TLS_DHE_RSA_WITH_AES_128_CBC_SHA (dh 2048) - A | TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 (dh 2048) - A | TLS_DHE_RSA_WITH_AES_256_CBC_SHA (dh 2048) - A | TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA (secp256r1) - C | TLS_RSA_WITH_AES_128_GCM_SHA256 (rsa 2048) - A | TLS_RSA_WITH_AES_256_GCM_SHA384 (rsa 2048) - A | TLS_RSA_WITH_AES_128_CBC_SHA256 (rsa 2048) - A | TLS_RSA_WITH_AES_256_CBC_SHA256 (rsa 2048) - A | TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A | TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A | TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C | TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA (dh 2048) - A | TLS_RSA_WITH_CAMELLIA_256_CBC_SHA (rsa 2048) - A | TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA (dh 2048) - A | TLS_RSA_WITH_CAMELLIA_128_CBC_SHA (rsa 2048) - A | compressors: | NULL | cipher preference: server | warnings: | 64-bit block cipher 3DES vulnerable to SWEET32 attack |_ least strength: C Nmap done: 1 IP address (1 host up) scanned in 39.91 seconds
根据输出能够看到badssl.com
同时支持TLSv1.0
、TLSv1.1
以及TLSv1.2
, 同时也能够看到当前对应协议支持的加密算法。
JRE在${JRE_HOME}/lib/security/java.security
配置了一些算法的配置, 例如本地个人${JRE_HOME}/lib/security/java.security
配置内容为:
jdk.tls.disabledAlgorithms=SSLv3, RC4, DES, MD5withRSA, DH keySize < 1024, \ EC keySize < 224, 3DES_EDE_CBC
表示当前JRE要禁用SSLv3
协议以及RC4
、DES
等算法。
固然咱们能够经过手动修改该文件来取消这些限制来达到咱们对目的, 但这样程序在部署到新环境就可能不能正常运行, 不优雅! 优雅对操做以下:
jdk.tls.disabledAlgorithms
。// 取消当前运行环境对SSLv三、RC四、DES以及3DES_EDE_CBC的禁用限制 static { String disabledAlgorithms = Security.getProperty("jdk.tls.disabledAlgorithms"); HashSet<String> keys = Sets.newHashSet(disabledAlgorithms.split(", +")); if (keys.contains("SSLv3")) keys.remove("SSLv3"); if (keys.contains("RC4")) keys.remove("RC4"); if (keys.contains("DES")) keys.remove("DES"); if (keys.contains("3DES_EDE_CBC")) keys.remove("3DES_EDE_CBC"); Security.setProperty("jdk.tls.disabledAlgorithms", StringUtils.join(keys, ", ")); log.debug("SECURITY PROPERTY UPDATED \"jdk.tls.disabledAlgorithms\" = " + Security.getProperty("jdk.tls.disabledAlgorithms")); }
SSLConnectionSocketFactory
兼容对低版本协议对支持, 突破JRE默认限制。// Allow SSLv3, TLSv1, TLSv1.2 protocol only SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory( sslContext, new String[] { "SSLv3", "TLSv1", "TLSv1.2"}, null, NoopHostnameVerifier.INSTANCE);
至此你就能够完美且优雅的解决这个问题。
[1] HTTPS
https://en.wikipedia.org/wiki...
[2] Obtain a Certificate from Server
https://ldapwiki.com/wiki/Obt...
[3] Transport Level Security (TLS) and Java
http://www.ateam-oracle.com/t...
[4] Diagnosing TLS, SSL, and HTTPS
https://blogs.oracle.com/java...