Java中HTTPS会遇到的问题

访问自签名的HTTPS网站

高版本JRE访问SSLv3/SSLv2站点

一些银行接口需要加载keystore的场景

* 如果要了解SSL历史也可以看看这篇文章。

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) { }

}

}

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

}

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

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

各版本对SSL的支持情况

JDK8

JDK7

JDK6

TLS Protocols

TLSv1.2 (default)
TLSv1.1
TLSv1
SSLv3

TLSv1.2
TLSv1.1
TLSv1 (default)
SSLv3

TLS v1.1 (JDK 6 update 111 and above)
TLSv1 (default)
SSLv3

Java Cryptography Extension, Unlimited Strength (explained later)

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

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

服务端支持对SSL版本在本地JRE已经被禁用, 例如服务端只支持SSLv3而JDK已经默认关闭了对SSLv3的支持。

服务端使用的JSSE Ciphers和本地支持的JSSE Ciphers没有共同项导致无法正常选择加密算法。

2.1 确认当前JRE启用SSL协议

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

}

}

输出:

Enabled protocols:

TLSv1

TLSv1.1

TLSv1.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_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

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 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, 同时也可以看到当前对应协议支持的加密算法。

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 only

SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(

sslContext,

new String[] { "SSLv3", "TLSv1", "TLSv1.2"},

null,

NoopHostnameVerifier.INSTANCE);

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

附录

java ssl cip_优雅处理HTTPS中的证书问题相关推荐

  1. http java ssl_使用HttpClient通过HTTPS信任所有证书

    使用HttpClient通过HTTPS信任所有证书 最近发布了一个关于HttpClient过度Https 的问题(在这里找到).我已经取得了一些进展,但我遇到了新的问题.和我的上一个问题一样,我似乎无 ...

  2. java ssl是什么意思_java中SSL是什么?包括哪些内容?

    大家都知道,java中关于网络安全的知识是非常多的,这也是java在日常的学习中比较重要的一个点.java中SSL的定义也是需要掌握的,一起来了解一下吧. 首先,SSL代表安全套接字层. 它是一种用于 ...

  3. HTTPS中CA证书的签发及使用过程

    1,HTTPS 简单来讲,HTTPS (Secure Hypertext Transfer Protocol)安全超文本传输协议就是安全的HTTP,我们知道HTTP是运行在TCP层之上的,HTTPS在 ...

  4. 详解HTTPS中的证书

    HTTPS与HTTP 在提到HTTPS的证书之前,我们都不可避免会提及HTTP与HTTPS的区别,两者都是进行报文传输的超文本传输协议,但是HTTPS却多出了一个S也就是SSL层(Secure Soc ...

  5. java ssl https 连接详解 生成证书

    我们在关于Java EE安全的系列文章中,有一篇也详细介绍了如何在Java EE应用中创建SSL连接和证书.正如前面文章提到的,SSL(Secure Sockets Layer,安全套接层)/TLS( ...

  6. Spring Boot(号称Java当前最流行的开发框架) 中启动HTTPS

    Spring Boot(号称Java当前最流行的开发框架) 中启动HTTPS 说实话啊,这个框架是比较简单,但是数据库操作还是那么恶心,好比16岁的花姑娘配了一个80岁的老头,关于这一块,我会单独发布 ...

  7. HTTPS 中双向认证SSL 协议的具体过程

    HTTPS 中双向认证SSL 协议的具体过程: 这里总结为详细的步骤: ① 浏览器发送一个连接请求给安全服务器. ② 服务器将自己的证书,以及同证书相关的信息发送给客户浏览器. ③ 客户浏览器检查服务 ...

  8. java ssl protocol,java – 为HTTPS连接设置https.protocols系统属性的问题

    我有一个 Java实现,它被各种客户端应用程序用来连接到第三方系统.这些第三方系统通过http / https支持不同的协议.在这种情况下,所有客户端应用程序都托管在我的Java实现托管的同一服务器中 ...

  9. Java后台开发Tomcat添加https支持小程序开发过程

    文章原文:blog.ouyangsihai.cn >> Java后台开发Tomcat添加https支持小程序开发过程 1 给自己的域名申请证书 注意:申请好了如果不是在腾讯注册的域名,不会 ...

最新文章

  1. 在一个执行力极差的团队工作是一种怎样的体验?
  2. go语言高并发与微服务实战_go-micro+gin+etcd微服务实战之服务注册与发现
  3. 深度学习(Deep Learning)读书思考三:正则化
  4. 相机标定(1)内\外参矩阵和畸变矩阵
  5. element-ui select单选切换多选问题解决
  6. 基于改进蜂群算法和灰色模型的管道腐蚀预测 - 附代码
  7. 查看一个进程的线程情况
  8. 如何为自定义的控件在工具箱中自定义个性化的图标
  9. Python入门基础教程(非常详细)
  10. E001检测到您的环境不支持HTML5,艾默生变频器常见故障及解决方法
  11. 计算机函数公式法计算出总分的式子,excel计算百分比公式的用法
  12. python提示语法错误_python出现语法错误怎么办
  13. php doctrine datetime,关于php:Doctrine 2.1 – datetime列的默认值
  14. 女神节快乐 | 用编程语言解密京东云程序媛!
  15. CPU, GPU, FPGA计算能力
  16. 西门子PC ADAPTER USB A2无法连接PLC的坑
  17. 利用elementUI里面的message做一个倒计时!简单实用
  18. PHP实现对小程序微信支付v2订单的结果查询
  19. 陈浩洋参访中馥科技集团仓储中心,直播单小时破百万
  20. 7的整除特征 三位一截_能被7整除的数的特征

热门文章

  1. Cookies或Session的区别
  2. 简单前端页面设计(JS+CSS)——魔方变换
  3. 安卓模拟器电脑玩香肠派对手游没有游戏快捷按键怎么办?终于被我找到了
  4. 瑞星:切莫忽视信息安全 携程事件为互联网企业敲响警钟
  5. 00002 贪婪洞窟.003.4:地牢生成
  6. sql如何根据时间查询最新的几条数据?
  7. 美本计算机科学申请美国名校,本科学历背景对申请美国名校的影响大吗?
  8. clickhouse之partition
  9. 经典 IP + Web3|蓝精灵官方游戏 The Smurfs‘ Society 带来全新体验
  10. win10任务栏无法取消固定Microsoft Edge