公钥加解密及SSL/TLS协议概述
来源:互联网 发布:淘宝怎样一键上传 编辑:程序博客网 时间:2024/06/06 04:57
公开密钥加密,也称为非对称加密,一种密码学算法类型,在这种密码学方法中,需要一对密钥,一个是私人密钥,另一个则是公开密钥。这两个密钥是数学相关。用某用户的密钥加密后所得的信息,只能用该用户的解密密钥才能解密。如果知道了其中一个,并不能计算出另外一个。因此如果公开了一对密钥中的一个,并不会危害到另外一个的秘密性质。
如果加密密钥是公开的,这用于给私钥所有者上传加密的数据,这被称作为公开密钥加密。
如果解密密钥是公开的,用私钥加密的信息,可以用公钥对其解密,用户客户验证持有私钥一方发布的数据或文件是完整准确的,接收者由此可知这条信息确实来自于拥有私钥的某人,这被称作数字签名,公钥的形式就是数字证书。
和其他加密过程一样,对RSA来说分配公钥的过程是非常重要的。分配公钥的过程必须能够抵挡中间人攻击。假设Eve交给Bob一个公钥,并使Bob相信这是Alice的公钥,并且它可以接下Alice和Bob之间的信息传递,那么它可以将它自己的公钥传给Bob,Bob以为这是Alice的公钥。Eve可以将所有Bob传递给Alice的消息截下来,将这个消息用它自己的密钥解密,读这个消息,然后将这个消息用Alice的公钥加密后传给Alice。理论上Alice和Bob都不会发现Eve在偷听他们的消息。一般用可靠的第三方机构签发证书来防止这样的攻击。
TLS/SSL协议的基本思路是采用公钥加密法,也就是说,客户端先向服务器端索要公钥,然后用公钥加密信息。服务器收到密文后,用自己的私钥解密。SSL/TLS协议的基本过程:
(1)客户端向服务器索要并验证公钥。
(2)双方协商生成“对话密钥”。
(3)双方采用“对话密钥”进行加密通信。
保证公钥不被篡改:将公钥放在数字证书中。只要证书是可信的,公钥就是可信的。
减少耗用时间:每一次对话(Session),客户端和服务器都生成一个“对话密钥(Session Key)”,用它来加密信息。由于“对话密钥”是对称加密,所以运算速度非常快,而服务器公钥只用于加密“对话密钥”本身,这样就减少了加密运算的消耗时间。
1.客户端发出请求(ClientHello)
首先,客户端向服务器发出加密同音的请求,在这一步客户端主要向服务器提供一下信息。
(1)支持的协议版本
(2)一个客户端生成的随机数,稍后用于生成“对话密钥”
(3)支持的加密方法
(4)支持的压缩算饭
2.服务器回应(ServerHello)
服务器收到客户端请求后,向客户端发出回应。服务器的回应:
(1)确认使用的加密通信协议版本
(2)一个服务器生成的随机数,稍后用于生成“对话密钥”
(3)确认使用的加密方法
(4)服务器证书
3.客户端回应
客户端收到服务器回应以后,首先验证服务器证书。
如果证书没有问题,客户端就会从证书中取出服务器的公钥。然后,向服务器发送下面三项信息。
(1)一个随机数。该随机数用服务器公钥加密,防止被窃听。
(2)编码改变通知,表示随后的信息都将从双方商定的加密方法和密钥发送
(3)客户端握手通知,表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来提供服务器校验。
上面第一项的随机数,是整个握手阶段出现的第三个随机数,又称“pre-master key”。有了它以后,客户端和服务器就同时有了三个随机数,接着双方就用事先商定的加密方法,各自生成本次会话所用的用一把“会话密钥”。
三个随机数:不管是客户端还是服务器,都需要随机数,这样生成的密钥才不会每次都一样。由于SSL协议中证书是静态的,因此十分有必要引入一种随机因素来保证协商出来的密钥的随机性。
对于RSA密钥交换算法来说,pre-master-key本身就是一个随机数,再加上hello消息中的随机,三个随机数通过一个密钥导出器最终导出一个对称密钥。
pre-master的存在在于SSL协议不信任每个主机都能产生完全随机的随机数,如果随机数不随机,那么pre-master-secret就有可能被猜出来,那么仅适用pre master secret作为密钥就不合适了,因此必须引入新的随机元素,那么客户端和服务器加上pre master secret三个随机数一同生成的密钥就不容易被猜出了,一个伪随机可能完全不随机,可是三个伪随机就十分接近随机了,每增加一个自由度,随机性增加的可不是一。
4.服务器的最后回应
服务器收到客户端的第三个随机数pre-master key之后,计算生成本次会话所用的“会话密钥”。然后,向客户端最后发送下面信息。
(1)编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。
(2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来提供客户端校验。
如果加密密钥是公开的,这用于给私钥所有者上传加密的数据,这被称作为公开密钥加密。
如果解密密钥是公开的,用私钥加密的信息,可以用公钥对其解密,用户客户验证持有私钥一方发布的数据或文件是完整准确的,接收者由此可知这条信息确实来自于拥有私钥的某人,这被称作数字签名,公钥的形式就是数字证书。
与对称密钥加密相比,公钥加密无需共享通用密钥,解密的私钥不发往任何用户。即使公钥在网上被截获,如果没有与其匹配的私钥,也无法解密,所截获的公钥是没有任何用处的。
但公钥加密只能加密少量的数据,因此,一个完整的密码体系,往往通过公钥加密来创建私有密钥,然后用私有密钥通过对称密码学加密大量数据。公钥加密的另一个功能 —— 数字签名 ,则是对称密钥加密无法实现的。
Java实现公钥(RSA)加密
RSA公钥与私钥的产生:
假设Alice想要通过一个不可靠的媒体接收Bob的一条私人信息。她可以用以下的方式来产生一个公钥和一个私钥:
1.随意选择两个大的指数p和q,p不等于q,就是那N=p*q。
根据欧拉函数,求得r=(p-1)(q-1)
3.选择一个小于r的整数e,求得e关于模r的模反元素,命名为d.(模反元素存在,当且仅当e与r互质)
4.将p和q的记录销毁。
(N,e)是公钥,(N,d)是私钥,Alice将他的公钥(N,e)传给Bob,而将她的私钥(N,d)藏起来。
import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;import java.security.InvalidKeyException;import java.security.KeyFactory;import java.security.KeyPair;import java.security.KeyPairGenerator;import java.security.NoSuchAlgorithmException;import java.security.SecureRandom;import java.security.interfaces.RSAPrivateKey;import java.security.interfaces.RSAPublicKey;import java.security.spec.InvalidKeySpecException;import java.security.spec.PKCS8EncodedKeySpec;import java.security.spec.X509EncodedKeySpec;import javax.crypto.BadPaddingException;import javax.crypto.Cipher;import javax.crypto.IllegalBlockSizeException;import javax.crypto.NoSuchPaddingException;import com.sun.org.apache.xml.internal.security.utils.Base64; public class RSAEncrypt { /** * 字节数据转字符串专用集合 */ private static final char[] HEX_CHAR = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * 随机生成密钥对 */ public static void genKeyPair(String filePath) { // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象 KeyPairGenerator keyPairGen = null; try { keyPairGen = KeyPairGenerator.getInstance("RSA"); } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 初始化密钥对生成器,密钥大小为96-1024位 keyPairGen.initialize(1024,new SecureRandom()); // 生成一个密钥对,保存在keyPair中 KeyPair keyPair = keyPairGen.generateKeyPair(); // 得到私钥 RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 得到公钥 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); try { // 得到公钥字符串 String publicKeyString = Base64.encode(publicKey.getEncoded()); // 得到私钥字符串 String privateKeyString = Base64.encode(privateKey.getEncoded()); // 将密钥对写入到文件 FileWriter pubfw = new FileWriter(filePath + "/publicKey.keystore"); FileWriter prifw = new FileWriter(filePath + "/privateKey.keystore"); BufferedWriter pubbw = new BufferedWriter(pubfw); BufferedWriter pribw = new BufferedWriter(prifw); pubbw.write(publicKeyString); pribw.write(privateKeyString); pubbw.flush(); pubbw.close(); pubfw.close(); pribw.flush(); pribw.close(); prifw.close(); } catch (Exception e) { e.printStackTrace(); } } /** * 从文件中输入流中加载公钥 * * @param in * 公钥输入流 * @throws Exception * 加载公钥时产生的异常 */ public static String loadPublicKeyByFile(String path) throws Exception { try { BufferedReader br = new BufferedReader(new FileReader(path + "/publicKey.keystore")); String readLine = null; StringBuilder sb = new StringBuilder(); while ((readLine = br.readLine()) != null) { sb.append(readLine); } br.close(); return sb.toString(); } catch (IOException e) { throw new Exception("公钥数据流读取错误"); } catch (NullPointerException e) { throw new Exception("公钥输入流为空"); } } /** * 从字符串中加载公钥 * * @param publicKeyStr * 公钥数据字符串 * @throws Exception * 加载公钥时产生的异常 */ public static RSAPublicKey loadPublicKeyByStr(String publicKeyStr) throws Exception { try { byte[] buffer = Base64.decode(publicKeyStr); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer); return (RSAPublicKey) keyFactory.generatePublic(keySpec); } catch (NoSuchAlgorithmException e) { throw new Exception("无此算法"); } catch (InvalidKeySpecException e) { throw new Exception("公钥非法"); } catch (NullPointerException e) { throw new Exception("公钥数据为空"); } } /** * 从文件中加载私钥 * * @param keyFileName * 私钥文件名 * @return 是否成功 * @throws Exception */ public static String loadPrivateKeyByFile(String path) throws Exception { try { BufferedReader br = new BufferedReader(new FileReader(path + "/privateKey.keystore")); String readLine = null; StringBuilder sb = new StringBuilder(); while ((readLine = br.readLine()) != null) { sb.append(readLine); } br.close(); return sb.toString(); } catch (IOException e) { throw new Exception("私钥数据读取错误"); } catch (NullPointerException e) { throw new Exception("私钥输入流为空"); } } public static RSAPrivateKey loadPrivateKeyByStr(String privateKeyStr) throws Exception { try { byte[] buffer = Base64.decode(privateKeyStr); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); } catch (NoSuchAlgorithmException e) { throw new Exception("无此算法"); } catch (InvalidKeySpecException e) { throw new Exception("私钥非法"); } catch (NullPointerException e) { throw new Exception("私钥数据为空"); } } /** * 公钥加密过程 * * @param publicKey * 公钥 * @param plainTextData * 明文数据 * @return * @throws Exception * 加密过程中的异常信息 */ public static byte[] encrypt(RSAPublicKey publicKey, byte[] plainTextData) throws Exception { if (publicKey == null) { throw new Exception("加密公钥为空, 请设置"); } Cipher cipher = null; try { // 使用默认RSA cipher = Cipher.getInstance("RSA"); // cipher= Cipher.getInstance("RSA", new BouncyCastleProvider()); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] output = cipher.doFinal(plainTextData); return output; } catch (NoSuchAlgorithmException e) { throw new Exception("无此加密算法"); } catch (NoSuchPaddingException e) { e.printStackTrace(); return null; } catch (InvalidKeyException e) { throw new Exception("加密公钥非法,请检查"); } catch (IllegalBlockSizeException e) { throw new Exception("明文长度非法"); } catch (BadPaddingException e) { throw new Exception("明文数据已损坏"); } } /** * 私钥加密过程 * * @param privateKey * 私钥 * @param plainTextData * 明文数据 * @return * @throws Exception * 加密过程中的异常信息 */ public static byte[] encrypt(RSAPrivateKey privateKey, byte[] plainTextData) throws Exception { if (privateKey == null) { throw new Exception("加密私钥为空, 请设置"); } Cipher cipher = null; try { // 使用默认RSA cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, privateKey); byte[] output = cipher.doFinal(plainTextData); return output; } catch (NoSuchAlgorithmException e) { throw new Exception("无此加密算法"); } catch (NoSuchPaddingException e) { e.printStackTrace(); return null; } catch (InvalidKeyException e) { throw new Exception("加密私钥非法,请检查"); } catch (IllegalBlockSizeException e) { throw new Exception("明文长度非法"); } catch (BadPaddingException e) { throw new Exception("明文数据已损坏"); } } /** * 私钥解密过程 * * @param privateKey * 私钥 * @param cipherData * 密文数据 * @return 明文 * @throws Exception * 解密过程中的异常信息 */ public static byte[] decrypt(RSAPrivateKey privateKey, byte[] cipherData) throws Exception { if (privateKey == null) { throw new Exception("解密私钥为空, 请设置"); } Cipher cipher = null; try { // 使用默认RSA cipher = Cipher.getInstance("RSA"); // cipher= Cipher.getInstance("RSA", new BouncyCastleProvider()); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] output = cipher.doFinal(cipherData); return output; } catch (NoSuchAlgorithmException e) { throw new Exception("无此解密算法"); } catch (NoSuchPaddingException e) { e.printStackTrace(); return null; } catch (InvalidKeyException e) { throw new Exception("解密私钥非法,请检查"); } catch (IllegalBlockSizeException e) { throw new Exception("密文长度非法"); } catch (BadPaddingException e) { throw new Exception("密文数据已损坏"); } } /** * 公钥解密过程 * * @param publicKey * 公钥 * @param cipherData * 密文数据 * @return 明文 * @throws Exception * 解密过程中的异常信息 */ public static byte[] decrypt(RSAPublicKey publicKey, byte[] cipherData) throws Exception { if (publicKey == null) { throw new Exception("解密公钥为空, 请设置"); } Cipher cipher = null; try { // 使用默认RSA cipher = Cipher.getInstance("RSA"); // cipher= Cipher.getInstance("RSA", new BouncyCastleProvider()); cipher.init(Cipher.DECRYPT_MODE, publicKey); byte[] output = cipher.doFinal(cipherData); return output; } catch (NoSuchAlgorithmException e) { throw new Exception("无此解密算法"); } catch (NoSuchPaddingException e) { e.printStackTrace(); return null; } catch (InvalidKeyException e) { throw new Exception("解密公钥非法,请检查"); } catch (IllegalBlockSizeException e) { throw new Exception("密文长度非法"); } catch (BadPaddingException e) { throw new Exception("密文数据已损坏"); } } /** * 字节数据转十六进制字符串 * * @param data * 输入数据 * @return 十六进制内容 */ public static String byteArrayToString(byte[] data) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < data.length; i++) { // 取出字节的高四位 作为索引得到相应的十六进制标识符 注意无符号右移 stringBuilder.append(HEX_CHAR[(data[i] & 0xf0) >>> 4]); // 取出字节的低四位 作为索引得到相应的十六进制标识符 stringBuilder.append(HEX_CHAR[(data[i] & 0x0f)]); if (i < data.length - 1) { stringBuilder.append(' '); } } return stringBuilder.toString(); } }测试类
import com.sun.org.apache.xml.internal.security.utils.Base64; public class Main {public static void main(String[] args) throws Exception{String filepath="F:/";//RSAEncrypt.genKeyPair(filepath);String plainText="公钥加密私钥解密";//公钥加密过程 byte[] cipherData=RSAEncrypt.encrypt(RSAEncrypt.loadPublicKeyByStr(RSAEncrypt.loadPublicKeyByFile(filepath)),plainText.getBytes()); String cipher=Base64.encode(cipherData); //私钥解密过程 byte[] res=RSAEncrypt.decrypt(RSAEncrypt.loadPrivateKeyByStr(RSAEncrypt.loadPrivateKeyByFile(filepath)), Base64.decode(cipher)); String restr=new String(res); System.out.println("原文:"+plainText); System.out.println("加密:"+cipher); System.out.println("解密:"+restr); System.out.println(); } }密钥分配
和其他加密过程一样,对RSA来说分配公钥的过程是非常重要的。分配公钥的过程必须能够抵挡中间人攻击。假设Eve交给Bob一个公钥,并使Bob相信这是Alice的公钥,并且它可以接下Alice和Bob之间的信息传递,那么它可以将它自己的公钥传给Bob,Bob以为这是Alice的公钥。Eve可以将所有Bob传递给Alice的消息截下来,将这个消息用它自己的密钥解密,读这个消息,然后将这个消息用Alice的公钥加密后传给Alice。理论上Alice和Bob都不会发现Eve在偷听他们的消息。一般用可靠的第三方机构签发证书来防止这样的攻击。
TLS/SSL协议的基本思路是采用公钥加密法,也就是说,客户端先向服务器端索要公钥,然后用公钥加密信息。服务器收到密文后,用自己的私钥解密。SSL/TLS协议的基本过程:
(1)客户端向服务器索要并验证公钥。
(2)双方协商生成“对话密钥”。
(3)双方采用“对话密钥”进行加密通信。
保证公钥不被篡改:将公钥放在数字证书中。只要证书是可信的,公钥就是可信的。
减少耗用时间:每一次对话(Session),客户端和服务器都生成一个“对话密钥(Session Key)”,用它来加密信息。由于“对话密钥”是对称加密,所以运算速度非常快,而服务器公钥只用于加密“对话密钥”本身,这样就减少了加密运算的消耗时间。
1.客户端发出请求(ClientHello)
首先,客户端向服务器发出加密同音的请求,在这一步客户端主要向服务器提供一下信息。
(1)支持的协议版本
(2)一个客户端生成的随机数,稍后用于生成“对话密钥”
(3)支持的加密方法
(4)支持的压缩算饭
2.服务器回应(ServerHello)
服务器收到客户端请求后,向客户端发出回应。服务器的回应:
(1)确认使用的加密通信协议版本
(2)一个服务器生成的随机数,稍后用于生成“对话密钥”
(3)确认使用的加密方法
(4)服务器证书
3.客户端回应
客户端收到服务器回应以后,首先验证服务器证书。
如果证书没有问题,客户端就会从证书中取出服务器的公钥。然后,向服务器发送下面三项信息。
(1)一个随机数。该随机数用服务器公钥加密,防止被窃听。
(2)编码改变通知,表示随后的信息都将从双方商定的加密方法和密钥发送
(3)客户端握手通知,表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来提供服务器校验。
上面第一项的随机数,是整个握手阶段出现的第三个随机数,又称“pre-master key”。有了它以后,客户端和服务器就同时有了三个随机数,接着双方就用事先商定的加密方法,各自生成本次会话所用的用一把“会话密钥”。
三个随机数:不管是客户端还是服务器,都需要随机数,这样生成的密钥才不会每次都一样。由于SSL协议中证书是静态的,因此十分有必要引入一种随机因素来保证协商出来的密钥的随机性。
对于RSA密钥交换算法来说,pre-master-key本身就是一个随机数,再加上hello消息中的随机,三个随机数通过一个密钥导出器最终导出一个对称密钥。
pre-master的存在在于SSL协议不信任每个主机都能产生完全随机的随机数,如果随机数不随机,那么pre-master-secret就有可能被猜出来,那么仅适用pre master secret作为密钥就不合适了,因此必须引入新的随机元素,那么客户端和服务器加上pre master secret三个随机数一同生成的密钥就不容易被猜出了,一个伪随机可能完全不随机,可是三个伪随机就十分接近随机了,每增加一个自由度,随机性增加的可不是一。
4.服务器的最后回应
服务器收到客户端的第三个随机数pre-master key之后,计算生成本次会话所用的“会话密钥”。然后,向客户端最后发送下面信息。
(1)编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。
(2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来提供客户端校验。
阅读全文
0 0
- 公钥加解密及SSL/TLS协议概述
- IOS9使用的SSL/TLS协议运行机制及概述
- SSL/TLS协议安全系列:SSL/TLS概述
- SSL/TLS协议簇加解密流程
- SSL/TLS协议簇加解密流程
- SSL/TLS协议簇加解密流程
- SSL/TLS协议簇加解密流程
- SSL/TLS协议簇加解密流程
- SSL&TLS协议簇加解密流程
- SSL/TLS协议簇加解密流程
- SSL/TLS协议运行机制的概述
- SSL/TLS协议运行机制的概述
- SSL/TLS协议运行机制的概述
- SSL/TLS协议运行机制的概述
- SSL/TLS协议运行机制的概述
- SSL/TLS协议运行机制的概述
- SSL/TLS协议运行机制的概述
- SSL/TLS协议运行机制的概述
- python问题:IndentationError:expected an indented block错误解决
- HandlerThread
- Android线程优先级设置方法技巧
- 如何选择主机操作系统?
- 配置Tomcat使用https协议
- 公钥加解密及SSL/TLS协议概述
- linux 输出带完整路径的文件
- 注册域名的时候一定要注意的事项
- 【WLAN从入门到精通-基础篇】第9期——直接转发和隧道转发
- java基础提高篇--异常
- RecyclerView的基本使用
- spring 注入static 字段 @value
- RecyclerView的下拉刷新和加载更多 动画
- MyBatisSystemException There is no getter for property named 'id' in 'class java.lang.String'