1) https是什么?
https是一种协议,在说https之前我们先说一下什么是http,http就是我们平时浏览网页时使用的一种协议,http协议传输的数据都是未加密的,也就是明文的,因此使用http协议传输隐私信息非常不安全。为了保证这些隐私数据能够加密传输,王景公司设计了SSL(Secure Socket Layer安全套接字层)协议用于对http协议传输的数据进行加密,从而诞生了https。SSL目前的版本是3.0,被IETF定义在RFC6101中,之后IETF对SSL3.0进行升级,于是出现了TLS(Transport Layer Security)1.0,定义在RFC2246中。实际上我们现在使用的https协议都是TLS协议,但是由于SSL出现的时间比较早,并且依旧被现在的浏览器支持,因此SSL依然是https的代名词,但是无论是TLS还是SSL都是上个世纪的事情,SSL最后一个版本是3.0,今后TLS将会继承SSL优良传统继续为我们进行加密服务。
2) TLS/SSL中使用的几种加密方法,包括非对称加密、对称加密以及HASH算法。
非对称加密算法:
非对称加密算法实现机密信息交换的基本过程是:
甲方生成一对密钥并将其中的一把作为共用密钥向其他方公开;
得到该公用密钥的乙方使用该密钥对机密信息进行加密后再发送给甲方;
甲方再用自己保存的另一把专用密钥对加密后的信息进行解密。
非对称加密算法的保密性比较好,它消除了最终用户交换密钥解密的需要,但加密和解密花费时间长、速度慢,他不适合对文件加密而只适用于对少量数据进行加密。
经典的非对称加密算法如RSA算法等安全性都相当高,非对称加密的典型应用是数字签名。
DH(Diffie-Hellman)
Diffie-Hellman算法(D-H算法),密钥一致协议。是由公开密钥密码体制的奠基人Diffie和Hellman所提出的一种思想。简单的说就是允许两名用户在公开媒体上交换信息以生成"一致"的、可以共享的密钥。换句话说,就是由甲方产出一对密钥(公钥、私钥),乙方依照甲方公钥产生乙方密钥对(公钥、私钥)。以此为基线,作为数据传输保密基础,同时双方使用同一种对称加密算法构建本地密钥(SecretKey)对数据加密。这样,在互通了本地密钥(SecretKey)算法后,甲乙双方公开自己的公钥,使用对方的公钥和刚才产生的私钥加密数据,同时可以使用对方的公钥和自己的私钥对数据解密。不单单是甲乙双方两方,可以扩展为多方共享数据通讯,这样就完成了网络交互数据的安全通讯!该算法源于中国的同余定理——中国馀数定理。
RSA
RSA取名是来自三个开发者的名字。RSA是目前最有影响力的公钥加密算法,它能够抵抗到目前为止已知的所有密码攻击,已经被ISO推荐为公钥数据加密标准。RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
EL Gamal
EL Gamal算法是公钥密码体制中的一种,在密码学中占有重要的地位。但是算法所采用的幂剩余计算耗时太多的问题,一直是制约其广泛应用的瓶颈问题。提出一种通过建表,以及对传统二进制算法进行改进,即将指数进行2k进制化,减少原BR算法迭代次数,提高加密解密速度的算法。
ECC
ECC算法以椭圆曲线理论为基础,在创建密钥时可以更快,更小,并且更有效,他是用最大质数的积来产生。
目前Java6提供了DH和RSA两种算法实现,通过Bouncy Castle可以实现Elmal算法支持,另ECC加密算法,目前没有开源组件提供支持。
对称加密算法:
对加密和解密使用相同密钥的加密算法。由于其速度,对称性加密通常在消息发送方需要加密大量数据时使用。对称加密也称为密钥加密。所谓对称,就是采用这种加密方法的双方使用相同的密钥进行加密和解密。
常用的对称加密有:
DES、IDEA、RC2、RC4、SKIPJACK算法等。
为什么讲对称加密本身是不安全的?
下面举个例子来简要说明一下对称加密的工作过程。甲和乙是一对生意搭档,他们住在不同的城市。由于生意上的需要,他们经常会相互之间邮寄重要的货物。为了保证货物的安全,他们商定制作一个保险盒,将物品放入其中。他们打造了两把相同的钥匙分别保管,以便在收到包裹时用这个钥匙打开保险盒,以及在邮寄货物前用这把钥匙锁上保险盒。
上面是一个将重要资源安全传递到目的地的传统方式,只要甲乙小心保管好钥匙,那么就算有人得到保险盒,也无法打开。这个思想被用到了现代计算机通信的信息加密中。在对称加密中,数据发送方将明文(原始数据)和加密密钥一起经过特殊加密算法处理后,使其变成复杂的加密密文发送出去。接收方收到密文后,若想解读原文,则需要使用加密密钥及相同算法的逆算法对密文进行解密,才能使其恢复成可读明文。在对称加密算法中,使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密。
只要你拿到了这把钥匙,你就可以打开保险盒。
HASH算法:
常用的摘要算法包括MD5、SHA1,SHA256
消息摘要算法的特点:
1) 无论输入的消息有多长,计算出来的消息摘要的长度总是固定的
2) 消息摘要看起来是随机的,看上去是胡乱的凑在一起
3) 只要输入的消息不同,对其进行摘要以后产生的摘要消息也不相同;但相同的输入必会产生相同的输出
4) 消息摘要函数只能进行正向的信息摘要,而无法从摘要中恢复出任何的消息,甚至根本找不到任何与原信息相关的信息。
消息摘要算法的主要特征是加密过程不需要密钥,并且经过加密的数据无法被破解,只有输入相同的明文数据经过相同的消息摘要算法才能得到相同的密文。消息摘要不存在密钥的管理与分发问题,适合分布式网络相同上使用。由于其加密计算的工作量相当客观,所以以前这种算法通常只用于数据量有限的情况下加密,例如计算机口令就是用不可逆算法加密的。
现在好多应用系统的数据库中保存的登录密码采用MD5加密,由于HASH算法的特点是输入相同的消息,最后出来的摘要是固定的特点,所以获得了MD5加密后的密码,我们可以通过暴力破解的方式,与密码库中的密码进行比对,也很容易破解。所以还是要设置的密码复杂点。
3) JavaWeb项目中使用https之后,以前的代码需要修改吗?
这里分几种情况,第一种是你的应用调用外部接口或者系统,那么需要修改这部分的代码。如果你的程序是web项目,并且采用浏览器访问的方式访问系统,那么只需要调整相关的配置就可以了。
我们主要对第二种情况进行说明:
第一步:生成证书文件
可以参考:http://ln-ydc.iteye.com/blog/1335213
第二步:修改web项目中的web.xml文件,增加下面的内容:
<security-constraint>
<web-resource-collection>
<web-resource-name>SSL</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
第三步:修改Tomcat下的conf文件夹中的server.xml文件,把项目端口访问的部分做相应的调整
<Connector port="8443"protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" clientAuth="false"keystoreFile="D:/AppServer/Tomcat/apache-tomcat-6.0.32/conf/tomcat.keystore" keystorePass="deleiguo" sslProtocol="TLS" />
注:
keystoreFile:证书文件的位置
方框中的keystore的密码,就是刚才我们设置的“123456”
完成上面的3步,项目就可以采用https访问了。
我们在程序中有的时候使用HttpConnection访问某些链接,如果我们想访问https链接怎么操作呢?
请参考http://www.68idc.cn/help/buildlang/java/20150729465845.html文章。
在上面链接中采用的是JSSE,涉及到了一些类,我们简单把JSSE中涉及到的类说明一下(请参考:http://www.2cto.com/kf/201408/323963.html):
这张图上描述了JSSE中使用的类的结构,下面分别说明一下:
(1) 通信核心类:SSLSoket和SSLServerSocket。对于使用过socket进行通信开发的朋友比较好理解,他们对应就是Socket和ServerSocket,只是表示实现了SSL协议的Socket和ServerSocket,同时他们也是Socket与ServerSocket的子类。SSLSocket负责的事情包括加密套件、管理SSL会话、处理握手结束时间、设置客户端模式或服务器模式。
(2) 客户端与服务器端Socket工厂:SSLSocketFactory和SSLServerSocketFactory。在设计模式中工厂模式是专门用于生产出需要的实例,这里也是把SSLSocket和SSLServerSocket对象创建的工作交给这两个工厂类。
(3) SSL会话:SSLSession。安全通信握手过程需要一个会话,为了提高通信的效率,SSL协议允许多个SSLSocket共享一个SSL会话,在同一个会话中,只有第一个打开的SSLSocket需要进行SSL握手,负责生成密钥及交换密钥,其余的SSLSocket都共享密钥信息。
(4) SSL上下文:SSLContext。它是对整个SSL/TLS协议的封装,为了安全套接字协议的实现,主要负责设置安全通信过程中的各种信息,例如跟证书相关的信息。并且负责构建SSLSocketFactory、SSLServerSocketFactory和SSLEngine等工厂类。
(5) SSL非阻塞引擎:SSLEngine。假如你要进行NIO通信,那么将使用这个类,他让通过过程支持非阻塞的安全通信。
什么是NIO?
传统的Socket IO操作,需要为每个连接创建一个线程,当并发的连接数量非常大的时候,线程占用的栈内存和CPU线程切换的开销将非常巨大。使用NIO,不再需要为每个线程创建单独的线程,可以用一个含有限数量的线程的线程池,甚至一个线程来为任意数量的连接服务。由于线程数量小于连接数量,所以每个线程进行IO操作的时候就不能阻塞,如果阻塞的话,有些连接就得不到处理,NIO提供了这种非阻塞的能力。
详细内容可以参考:http://blog.csdn.net/zhouhl_cn/article/details/6568119
(6) 密钥管理器:KeyManager。此接口负责选择用于证实自己身份的安全证书,发给通信另一方。KeyManager对象由KeyManager工厂类生成。
(7) 信任管理器:TrustManager。此接口负责判断决定是否信任对方的安全证书,TrustManager对象由TrustManagerFactory工厂类生成。
(8) 密钥证书存储设施:KeyStore。这个对象用于存放安全证书,安全证书一般以文件形式存放,KeyStore负责将证书加载到内存。
SSL协议通信中涉及密钥存储的文件格式比较多,容易搞混,下面的图可以简单的描述:
名称 | 作用 |
.cer | 俗称证书,但是这个证书没有私钥,只包含了公钥 |
.pfx | 也称为证书,它一般提供浏览器使用,不仅包含公钥,还包含了私钥,这个私钥是加密的 |
.jks(javakey store) | 密钥存储器,可以同时容纳N个公钥跟私钥,是一个密钥库 |
.keystore | 密钥存储器,跟.jks基本是一样的,不同的公司叫法不一样,默认生成的证书存储库格式 |
.truststore | 信任证书存储库,仅仅包含了通信对方的公钥,你也可以直接把通信对方的jsk作为信任库 |
有的时候,需要把证书(pfx或cer)转化为jsk以便于用Java进行ssl通信,例如一个公司只提供了pfx证书,而我们想用java进行ssl通信时就要把pfx转化为jsk格式。 证书转化可以参考(没有测试):http://blog.csdn.net/a351945755/article/details/23195713 |
我的代码内容:
package com.yimian.test;
import java.io.InputStreamReader;
import java.NET.URL;
import javax.net.ssl.HttpsURLConnection;
import javax.Net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
public class MyX509Test {
public static void main(String[] args) {
try {
goHttps("https://zym:9999/");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void goHttps(String url)throws Exception{
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = { newMyX509TrustManager() };
SSLContext sslContext =SSLContext.getInstance("SSL","SunJSSE");
sslContext.init(null, tm,newjava.security.SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();
// 创建URL对象
URL myURL = new URL(url);
// 创建HttpsURLConnection对象,并设置其SSLSocketFactory对象
HttpsURLConnection httpsConn= (HttpsURLConnection) myURL.openConnection();
httpsConn.setSSLSocketFactory(ssf);
// 取得该连接的输入流,以读取响应内容
InputStreamReader insr = newInputStreamReader(httpsConn.getInputStream(),"UTF-8");
// 读取服务器的响应内容并显示
int respInt = insr.read();
while (respInt != -1) {
System.out.print((char) respInt);
respInt = insr.read();
}
}
}
package com.yimian.test;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
/**
* 自定义信任管理器
* https加密
*/
public class MyX509TrustManager implements X509TrustManager{
X509TrustManager sunJSSEX509TrustManager;
private static final String TRUST_MANAGER_ALGORITHM ="SunX509";
private static final String TRUEST_PROVIDER ="SunJSSE";
private static final String CERTS_PWD ="keystore888";
private static final String FILE_PATH ="D:\\tomcat.keystore";
// 构造方法初始化证书信息
MyX509TrustManager() throws Exception{
// 获得keystore实例
KeyStore ks = KeyStore.getInstance("JKS");
// keystore文件流、密码
ks.load(new FileInputStream(FILE_PATH),CERTS_PWD.toCharArray());
// algorithm:加密方式
// provider:提供者
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TRUST_MANAGER_ALGORITHM,TRUEST_PROVIDER);
// 信任管理器初始化证书
tmf.init(ks);
TrustManager[] tms =tmf.getTrustManagers();
for(TrustManager tm:tms){
if (tminstanceofX509TrustManager) {
sunJSSEX509TrustManager = (X509TrustManager) tm;
return;
}
}
// 如果都没有发现,抛出异常
throw new Exception("Couldn'tinitialize!");
}
// 检测客户端是否信任程序
public void checkClientTrusted(X509Certificate[] chain, StringauthType){
try {
sunJSSEX509TrustManager.checkClientTrusted(chain,authType);
} catch (CertificateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 检测服务器端是否信任程序
public void checkServerTrusted(X509Certificate[] chain, StringauthType){
try {
sunJSSEX509TrustManager.checkServerTrusted(chain,authType);
} catch (CertificateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 获得信任的发型证书
public X509Certificate[] getAcceptedIssuers() {
returnsunJSSEX509TrustManager.getAcceptedIssuers();
}
}
转载:http://blog.csdn.net/zhangzuomian/article/details/50324395 感谢博主分享