* 若是要了解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...