idea中jre system library_如何优雅处理HTTPS中的证书问题

本文介绍了Java中处理HTTPS证书问题的优雅方式,包括访问自签名HTTPS网站和高版本JRE处理SSL协议限制。建议下载服务端CA证书并自定义SSLContext,以及动态管理JRE的TLS配置和自定义SSLConnectionSocketFactory以兼容低版本协议。
摘要由CSDN通过智能技术生成

Java中HTTPS会遇到的问题

  • 访问自签名的HTTPS网站
  • 高版本JRE访问SSLv3/SSLv2站点
  • 一些银行接口需要加载keystore的场景

1 访问自签名的HTTPS网站

常常看到的回答是直接通过信任所有来支持, 这不优雅; 优雅的操作应该:

  • 下载服务端的CA证书
# 方式1: 导出DER格式的证书# 这里需要通过指定servername来保证导出的证书和当前域名匹配openssl s_client -showcerts -connect self-signed.badssl.com:443 -servername self-signed.badssl.com /dev/null|openssl x509 -outform der >self-signed.badssl.com.der
  • ​- 代码中通过加载服务端证书后通过自定义SSLContext访问目标服务器:
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) { }    }}@Testpublic 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);}

2 高版本JRE访问SSLv3/SSLv2站点 ​

通常你得到的答案是通过修改`${JRE_HOME}/lib/security/java.security`目录下某些配置项来取消高版本SDK对某些不安全SSL协议版本或算法的限制。

0c9cb05478ef544d1d8079a95dd221d7.png

​各版本对SSL的支持情况

* 在2015年1月发布的升级补丁中也已经禁用对SSLv3的支持。 ​

所以为什么会出现在某些高版本无法访问某些HTTPS站点的原因就是由于有以下可能:

  • 服务端支持对SSL版本在本地JRE已经被禁用, 例如服务端只支持SSLv3而JDK已经默认关闭了对SSLv3的支持。
  • 服务端使用的JSSE Ciphers和本地支持的JSSE Ciphers没有共同项导致无法正常选择加密算法。

​2.1 确认当前JRE启用SSL协议 ​ ​ ​

@Testpublic 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);    }}

​输出

Enabled protocols:TLSv1TLSv1.1TLSv1.2

2.2 确认当前JRE启用CipherSutes ​

String[] cipers = soc.getEnabledCipherSuites();System.out.println("Enabled CipherSutes:");for (String s : cipers) {    System.out.println(s);}

输出 ​

Enabled CipherSutes:TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384TLS_RSA_WITH_AES_256_CBC_SHA256TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384TLS_DHE_RSA_WITH_AES_256_CBC_SHA256TLS_DHE_DSS_WITH_AES_256_CBC_SHA256TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHATLS_ECDHE_RSA_WITH_AES_256_CBC_SHATLS_RSA_WITH_AES_256_CBC_SHATLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA...

​2.3 检查服务器支持的SSL协议 ​

这里我推荐使用nmap检测:

nmap --script ssl-enum-ciphers -p 443 badssl.com

输出

Starting Nmap 7.70 ( https://nmap.org ) at 2019-01-01 22:26 CSTNmap 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.comPORT    STATE SERVICE443/tcp open  https| ssl-enum-ciphers:|   TLSv1.0:|     ciphers:|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - 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_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_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: CNmap done: 1 IP address (1 host up) scanned in 39.91 seconds

​根据输出可以看到badssl.com同时支持TLSv1.0、TLSv1.1以及TLSv1.2, 同时也可以看到当前对应协议支持的加密算法。 ​

2.4 解决方案

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等算法。
当然我们可以通过手动修改该文件来取消这些限制来达到我们对目的, 但这样程序在部署到新环境就可能不能正常运行, 不优雅! 优雅对操作如下:

  • 根据协议版本动态对JRE对设置取消设置, 在JRE中管理相关SSL协议以及算法的配置项主要是jdk.tls.disabledAlgorithms。
// 取消当前运行环境对SSLv3、RC4、DES以及3DES_EDE_CBC的禁用限制static {    String disabledAlgorithms = Security.getProperty("jdk.tls.disabledAlgorithms");    HashSet 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 onlySSLConnectionSocketFactory sslConnectionSocketFactory =  new SSLConnectionSocketFactory(        sslContext,        new String[] { "SSLv3", "TLSv1", "TLSv1.2"},        null,        NoopHostnameVerifier.INSTANCE);

至此你就可以完美且优雅的解决这个问题。

当然到这里可能你依然会质疑何苦呢,而且使用了不安全的协议。哈哈,人生就是这么让你无可奈何,咱也没得选择。

附录

[1] HTTPS

https://en.wikipedia.org/wiki/HTTPS

[2] Obtain a Certificate from Server

https://ldapwiki.com/wiki/Obtain%20a%20Certificate%20from%20Server

[3] Transport Level Security (TLS) and Java

http://www.ateam-oracle.com/tls-and-java/

[4] Diagnosing TLS, SSL, and HTTPS

https://blogs.oracle.com/java-platform-group/diagnosing-tls,-ssl,-and-https

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值