加密算法 HTTP TLS SSL 数字签名 数字证书 HTTPS 以及JAVA数字签名HTTPS实现 学习笔记

加密方法:

对称加密:加密和解密的密钥一样,比如用123加密就是用123解密,但是实际中密码都是普通数据在互联网传输的,这样一点密码被中间人截取并破解,加密直接被攻破。
非对称加密:把密钥分为公钥和私钥,公钥是公开的所有人都可以认领,私钥是保密的只有一个人知道。假设A要发送一封Email给B,他不想让任何其他人在传输中看到Email的内容,做法就是使用B的公钥对Email加密,只有B的私钥能够解密(B的私钥唯一性保证信件不会泄露)。
某天出意外了,有黑客冒充A给B发送Email,并且也用B的公钥加密,导致B无法区分这封邮件是否来自A。怎么办?此时A可以用自己的私钥加密,那么B收到邮件后如果用A的公钥可以解密邮件,那么证明这封信肯定来自于A。
OK,通过这个例子我想你们基本明白非对称加密了!我总结了下面几点:
公钥的作用:对内容本身加密,保证不被其他人看到。
私钥的作用:证明内容的来源
公钥和私钥是配对关系,公钥加密就用私钥解密,反之亦然,用错的密钥来尝试解密会报错。
非对称加密算法:RSA,DSA/DSS
对称加密算法:AES,RC4,3DES
HASH算法:MD5,SHA1,SHA256

数字签名

接着聊上面发邮件的例子,假设A用自己的私钥对Email加密发送,这存在下面问题:

对文件本身加密可能是个耗时过程,比如这封Email足够大,那么私钥加密整个文件以及拿到文件后的解密无疑是巨大的开销。
数字签名可以解决这个问题:
1.A先对这封Email执行哈希运算得到hash值简称“摘要”,取名h1
2.然后用自己私钥对摘要加密,生成的东西叫“数字签名”
3.把数字签名加在Email正文后面,一起发送给B
(当然,为了防止邮件被窃听你可以用继续公钥加密,这个不属于数字签名范畴)
4.B收到邮件后用A的公钥对数字签名解密,成功则代表Email确实来自A,失败说明有人冒充
5.B对邮件正文执行哈希运算得到hash值,取名h2
6.B 会对比第4步数字签名的hash值h1和自己运算得到的h2,一致则说明邮件未被篡改。
在这里插入图片描述

看完这个过程,是不是觉得数字签名不过如此。其实就是利用算法(不一定是非对称算法)对原文hash值加密,然后附着到原文的一段数据。数字签名的作用就是验证数据来源以及数据完整性!解密过程则称为数字签名验证。
不过先别着急,我在梳理数字签名流程时候有下面几点疑惑,不知你也是否一样?

如果中间人同时篡改了Email正文和数字签名,那B收到邮件无法察觉啊。
答案:数字签名的生成需要对方私钥,所以数字签名很难被伪造。万一私钥泄漏了呢,不好意思,你私钥都能弄丢了那这篇文章当我白写。(私钥绝对保密不参与传输)
公钥是公开的并且可以自行导入到电脑,如果有人比如C偷偷在B的电脑用自己公钥替换了A的公钥,然后用自己的私钥给B发送Email,这时B收到邮件其实是被C冒充的但是他无法察觉。
答案:确实存在这种情况!解决办法就是数字证书,一环套一环请接着看。

数字证书

上面第2点描述的安全漏洞根源在哪?就是A的公钥很容易被替换!那么数字证书是怎么生成的呢?以及如何配合数字签名工作呢?

首先A去找"证书中心"(certificate authority,简称CA),为公钥做认证。证书中心用自己的私钥,对A的公钥和一些相关信息一起加密,生成"数字证书"(Digital Certificate):

在这里插入图片描述

Http

HyperText Transfer Protocol,超文本传输协议,是互联网上使用最广泛的一种协议,所有WWW文件必须遵循的标准。HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全。

使用TCP端口为:80

Https

**
Hyper Text Transfer Protocol over Secure Socket Layer,安全的超文本传输协议,网景公式设计了SSL(Secure Sockets Layer)协议用于对Http协议传输的数据进行加密,保证会话过程中的安全性。

使用TCP端口默认为443

SSL协议加密方式

SSL协议即用到了对称加密也用到了非对称加密(公钥加密),在建立传输链路时,SSL首先对对称加密的密钥使用公钥进行非对称加密,链路建立好之后,SSL对传输内容使用对称加密。

对称加密
速度高,可加密内容较大,用来加密会话过程中的消息

公钥加密 (非对称加密)
加密速度较慢,但能提供更好的身份认证技术,用来加密对称加密的密钥

SSL和TLS的区别

SSL 是洋文“Secure Sockets Layer”的缩写,中文叫做“安全套接层”。它是在上世纪90年代中期,由网景公司设计的。(顺便插一句,网景公司不光发明了 SSL,还发明了很多 Web 的基础设施——比如“CSS 样式表”和“JS 脚本”)
为啥要发明 SSL 这个协议捏?因为原先互联网上使用的 HTTP 协议是明文的,存在很多缺点——比如传输内容会被偷窥(嗅探)和篡改。发明 SSL 协议,就是为了解决这些问题。
到了1999年,SSL 因为应用广泛,已经成为互联网上的事实标准。IETF 就在那年把 SSL 标准化。标准化之后的名称改为 TLS(是“Transport Layer Security”的缩写),中文叫做“传输层安全协议”。
很多相关的文章都把这两者并列称呼(SSL/TLS),因为这两者可以视作同一个东西的不同阶段。

https的交互过程

客户端发起HTTPS请求

1.1 比如在浏览器里输入https://xxxx.com,然后请求到server的443端口。浏览器会把自身支持的一系列Cipher Suite(密钥算法套件,简称Cipher)发送给服务端。

1.2 服务端返回
服务端接收到客户端所有的Cipher后与自身支持的对比,如果不支持则连接断开,反之则会从中选出一种加密算法和HASH算法以证书的形式返回给客户端,证书中还包含了公钥、颁证机构、网址、失效日期等等。

注:采用HTTPS协议的服务器必须要有一套数字证书,可以自己制作,也可以向组织申请。区别就是自己颁发的证书需要客户端验证通过,才可以继续访问,而使用受信任的公司申请的证书则不会弹出提示页面。这套证书其实就是一对公钥和私钥,用于非对称加密。

1.3 客户端验证证书并响应
客户端收到服务端响应后会做以下几件事:

1.3.1 验证证书的合法性
颁发证书的机构是否合法与是否过期,证书中包含的网站地址是否与正在访问的地址一致等。证书验证通过后,在浏览器的地址栏提示网站安全。

1.3.2 生成随机密码
如果证书验证通过,或者用户接受了不授信的证书,此时客户端会生成一串随机数,用于后续进行对称加密,以后称之为密钥,然后用证书中的公钥加密这个密钥。

1.3.3 HASH握手信息
用最开始约定好的HASH方式,把握手消息取HASH值,然后用密钥加密 “握手消息+握手消息的HASH值(即签名)” 。在这里之所以要加上握手消息的HASH值,主要是把握手消息做一个签名,用于验证握手消息在传输过程中没有被篡改过。

最后将之前生成的所有信息发送给服务端。

1.4 服务端解析和验证
服务端接收客户端发来的数据之后要做以下的操作:

1.4.1 解析和验证
使用自己的私钥将客户端使用公钥加密的信息解密得到之前客户端生成的密钥。然后使用密钥解密客户端发来的加密后的握手消息得到 “握手消息+握手消息的HASH值(即签名)” ,最后同样对握手信息进行HASH签名,比对和客户端传来的HASH值是否一致,如果一致则HASH验证通过,即表明握手消息在传输过程中没有被篡改过。

1.4.2 返回
同样使用密钥加密一段握手消息,发送给客户端。

1.5 客户端解析和验证
客户端用密钥解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束。

1.6 后续通信
由于RSA等非对称加密算法的性能是非常低的,原因在于寻找大素数、大数计算、数据分割需要耗费很多的CPU周期,所以一般的HTTPS连接只在第一次握手时使用非对称加密,通过握手交换对称加密的密钥,之后所有的通信数据将由之前客户端生成的密钥进行对称加密。

因为这串密钥只有客户端和服务端知道,所以即使中间请求被拦截也是没法解密数据的,以此保证了通信的安全。

单向认证

在这里插入图片描述

双向认证

双向认证和单向认证原理基本差不多,只是除了客户端需要认证服务端以外,增加了服务端对客户端的认证,具体过程如下:
在这里插入图片描述
tomcat证书配置
利用JDK的Keytool配置Tomcat7.0的SSL协议:
1.用JDK自带的Keytool生成服务器证书:
1)打开终端控制台,转向tomcat主目录,执行生成keystore文件命令:
keytool -genkey -alias tomcat -keyalg RSA -keypass tomcat -storepass tomcat -keystore server.keystore -validity 3600
——在tomcat主目录下生成server.keystore文件;
2)根据keystore文件产生的证书请求,向CA申请服务器数字证书:
keytool -export -trustcacerts -alias tomcat -file server.cer -keystore server.keystore -storepass tomcat
——在tomcat主目录下生成server.cer文件;
3)将信息中心签发的服务器证书server.cer导入到server.keystore文件:
keytool -import -trustcacerts -alias tomcat -file server.cer -keystore server.keystore -storepass tomcat

2.用JDK自带的Keytool生成客户端证书:
1)为支持证书顺利导入到IE和Firefor,证书格式为PKCS12,命令如下:
keytool -genkey -v -alias client -keyalg RSA -storetype PKCS12 -validity 3600 -keystore client.p12 -storepass client -keypass client
——在tomcat主目录下生成client.p12文件;
2)让服务器信任客户端证书:
双向SSL认证,服务器要信任客户端证书,因此要把客户端证书添加为服务器的信任认证,由于不能直接将PKCS12格式的证书导入,要先把客户端证书导出为一个单独的CER文件,命令:
keytool -export -alias client -keystore client.p12 -storetype PKCS12 -storepass client -rfc -file client.cer
——在tomcat主目录下生成client.cer文件;
将client.cer导入到服务器的证书库server.keystore,添加为一个信任证书:
keytool -import -v -file client.cer -keystore server.keystore -storepass tomcat
——认证已添加至keystore中
3)通过list命令查看服务器的证书库,可以看到两个输入,一个是服务器证书,一个是受信任的客户端证书:keytool -list -keystore server.keystore -storepass tomcat

4)删除命令:keytool -delete -alias myKey -keystore server.keystore -storepass tomcat
3.修改tomcat配置:conf/server.xml


1)clientAuth=“true”,双向认证;
2)clientAuth=“false”,单向认证;
4.重新启动tomcat,执行./bin/startup.sh命令,访问https://127.0.0.1:8443
1)clientAuth=“false”,单向认证,成功打开tomcat首页;
2)clientAuth=“true”,双向认证,打开Firefox菜单:编辑->首选项->高级->加密->查看证书->你的证书,将client.p12导入到IE中,按照Firefox提示完成登录tomcat首页;
5.服务器SSl证书获取代码
if(request.isSecure())//如果是SSL通信
{
java.security.cert.X509Certificate[] certs=(java.security.cert.X509Certificate[])request.getAttribute(“javax.servlet.request.X509Certificate”);
if(certs!=null && certs.lengtt>0)
{
subjectDN=“Certificates found”;
}
}
使用java解析证书可获取证书中用户信息
issue=certs.getIssuerDN().toString();//证书签发者
subject=certs.getSubjectDN().getName();//证书所有者
after=certs.getNotAfter().toString();//证书起效时间
before=certs.getNotBefore().toString();//证书到期时间
version=Integer.toString(certs.getVersion());//证书版本
serialno=certs.getSerialNumber().toString();//证书序列号

5.参考地址:
http://tomcat.apache.org/tomcat-6.0-doc/ssl-howto.html //tomcat6.0 ssl配置
http://wenku.baidu.com/view/e7b22df0f90f76c661371a6f.html //Tomcat SSL配置

数字签名JAVA源码

package bean;

import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * DSA-Digital Signature Algorithm 是Schnorr和ElGamal签名算法的变种,被美国NIST作为DSS(DigitalSignature Standard)。
 * 简单的说,这是一种更高级的验证方式,用作数字签名。不单单只有公钥、私钥,还有数字签名。私钥加密生成数字签名,公钥验证数据及签名。
 * 如果数据和签名不匹配则认为验证失败!即 传输中的数据 可以不再加密,接收方获得数据后,拿到公钥与签名 验证数据是否有效
 * 
 * @author stone
 * @date 2014-03-11 09:50:51
 */
public class DSA {
	//不仅可以使用DSA算法,同样也可以使用RSA算法做数字签名
	public static final String KEY_ALGORITHM = "RSA";
	public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
	
	//public static final String KEY_ALGORITHM = "DSA";
	//public static final String SIGNATURE_ALGORITHM = "DSA";
	
	public static final String DEFAULT_SEED = "$%^*%^()(HJG8awfjas7"; //默认种子
	public static final String PUBLIC_KEY = "DSAPublicKey";
	public static final String PRIVATE_KEY = "DSAPrivateKey";
	
	public static void main(String[] args) throws Exception {
		String str = "!@#$!#^$#&ZXVDF呆军工路11111爱着你*()_+";
		byte[] data = str.getBytes();
		
		Map<String, Object> keyMap = initKey();// 构建密钥
		PublicKey publicKey = (PublicKey) keyMap.get(PUBLIC_KEY);
		PrivateKey privateKey = (PrivateKey) keyMap.get(PRIVATE_KEY);
		System.out.println("私钥format:" + privateKey.getFormat());
		System.out.println("公钥format:" + publicKey.getFormat());
		
		
		// 产生签名
		String sign = sign(data, getPrivateKey(keyMap));
		
		// 验证签名 
		boolean verify1 = verify("aaa".getBytes(), getPublicKey(keyMap), sign);
		System.err.println("经验证 数据和签名匹配:" + verify1);   
		
		boolean verify = verify(data, getPublicKey(keyMap), sign);
		System.err.println("经验证 数据和签名匹配:" + verify);   
	}
	
    /**  
     * 生成密钥  
     *   
     * @param seed 种子  
     * @return 密钥对象  
     * @throws Exception  
     */
	public static Map<String, Object> initKey(String seed) throws Exception {
		System.out.println("生成密钥");
		
		KeyPairGenerator keygen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
		SecureRandom secureRandom = new SecureRandom();   
        secureRandom.setSeed(seed.getBytes()); 
        //Modulus size must range from 512 to 1024 and be a multiple of 64
        keygen.initialize(640, secureRandom);  
        
        KeyPair keys = keygen.genKeyPair();
        PrivateKey privateKey = keys.getPrivate();
        PublicKey publicKey = keys.getPublic();
        
        Map<String, Object> map = new HashMap<String, Object>(2);
        map.put(PUBLIC_KEY, publicKey);   
        map.put(PRIVATE_KEY, privateKey);
        return map;
	}
	
	/**  
     * 生成默认密钥  
     *   
     * @return 密钥对象  
     * @throws Exception  
     */  
    public static Map<String, Object> initKey() throws Exception {   
        return initKey(DEFAULT_SEED);   
    }
    
    /**  
     * 取得私钥  
     *   
     * @param keyMap  
     * @return  
     * @throws Exception  
     */  
    public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {   
        Key key = (Key) keyMap.get(PRIVATE_KEY);   
        return encryptBASE64(key.getEncoded()); //base64加密私钥
    }   
  
    /**  
     * 取得公钥  
     *   
     * @param keyMap  
     * @return  
     * @throws Exception  
     */  
    public static String getPublicKey(Map<String, Object> keyMap) throws Exception {   
        Key key = (Key) keyMap.get(PUBLIC_KEY);   
        return encryptBASE64(key.getEncoded()); //base64加密公钥
    }   
	
	/**
	 *  用私钥对信息进行数字签名
	 * @param data	加密数据
	 * @param privateKey 私钥-base64加密的
	 * @return 
	 * @throws Exception
	 */
	public static String sign(byte[] data, String privateKey) throws Exception {
		System.out.println("用私钥对信息进行数字签名");
		
		byte[] keyBytes = decryptBASE64(privateKey);
		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
		PrivateKey priKey = factory.generatePrivate(keySpec);//生成 私钥
		
		//用私钥对信息进行数字签名
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initSign(priKey);
		signature.update(data);
		return encryptBASE64(signature.sign());
	}
	
	/**
	 * BASE64Encoder 加密
	 * @param data 要加密的数据
	 * @return 加密后的字符串
	 */
	private static String encryptBASE64(byte[] data) {
		BASE64Encoder encoder = new BASE64Encoder();
		String encode = encoder.encode(data);
		return encode;
	}
	
	/**
	 * BASE64Decoder 解密
	 * @param data 要解密的字符串
	 * @return 解密后的byte[]
	 * @throws Exception 
	 */
	private static byte[] decryptBASE64(String data) throws Exception {
		BASE64Decoder decoder = new BASE64Decoder();
		byte[] buffer = decoder.decodeBuffer(data);
		return buffer;
	}
	
	/**
	 * 校验数字签名
	 * @param data 加密数据
	 * @param publicKey
	 * @param sign 数字签名
	 * @return
	 * @throws Exception
	 */
	public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
		byte[] keyBytes = decryptBASE64(publicKey); 
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);  
		PublicKey pubKey = keyFactory.generatePublic(keySpec);
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);   
        signature.initVerify(pubKey); 
        signature.update(data);
        return signature.verify(decryptBASE64(sign)); //验证签名
	}
	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值