今天被问到关于https原理的问题,结果由于知识掌握不牢靠,停留于表面,很多细节都无法回答清楚,于是决定把https的原理弄个明白,废话不多说,我们先看看https的定义

(由于很久未写博客,排版有些凌乱,请谅解)

一:什么是https协议

在说HTTPS之前先说说什么是HTTP,HTTP就是我们平时浏览网页时候使用的一种协议。HTTP协议传输的数据都是未加密的,也就是明文的,因此使 用HTTP协议传输隐私信息非常不安全。为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。SSL目前的版本是3.0,被IETF(Internet Engineering Task Force)定义在RFC 6101中,之后IETF对SSL 3.0进行了升级,于是出现了TLS(Transport Layer Security) 1.0,定义在RFC 2246。实际上我们现在的HTTPS都是用的TLS协议,但是由于SSL出现的时间比较早,并且依旧被现在浏览器所支持,因此SSL依然是HTTPS的 代名词,但无论是TLS还是SSL都是上个世纪的事情,SSL最后一个版本是3.0,今后TLS将会继承SSL优良血统继续为我们进行加密服务。目前 TLS的版本是1.2,定义在RFC 5246中,暂时还没有被广泛的使用。对历史感兴趣的朋友可以参考http://en.wikipedia.org/wiki/Transport_Layer_Security,这里有对TLS/SSL详尽的叙述。

二:https的工作原理是什么

HTTPS在传输数据之前需要客户端(浏览器)与服务端(网站)之间进行一次握手,在握 手过程中将确立双方加密传输数据的密码信息,通常情况下会配合数字证书实现。

TLS/SSL协议不仅仅是一套加密传输的协议,更是一件经过艺术家精心设计的艺术品,TLS/SSL中使用 非对称加密,对称加密以及HASH算法。

这里我们先看看这上面提到的几种技术(如果你对这些技术已经非常了解,那么请跳过该段落,直接到段落三)

三 https握手的过程详细描述

1.浏览器将自己支持的一套加密规则发送给网站,如RSA加密算法,DES对称加密算法,SHA1摘要算法
2.网站从中选出一组加密算法与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息(证书中的私钥只能用于服务器端进行解密,在握手的整个过程中,都用到了证书中的公钥和浏览器发送给服务器的随机密码以及对称加密算法)

3.获得网站证书之后浏览器要做以下工作:
    a) 验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等),如果证书受信任,则浏览器栏里面会显示一个小锁头,否则会给出证书不受信的提示。
    b) 如果证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。
    c) 使用约定好的HASH算法计算握手消息(如SHA1),并使用生成的随机数对消息进行加密,最后将之前生成的被公钥加密的随机数密码,HASH摘要值一起发送给服务器

4.网站接收浏览器发来的数据之后要做以下的操作:
    a) 使用自己的私钥将信息解密并取出浏览器发送给服务器的随机密码,使用密码解密浏览器发来的握手消息,并验证HASH是否与浏览器发来的一致。
    b) 使用随机密码加密一段握手消息,发送给浏览器。
    5.浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密。

从上面的4个大的步骤可以看到,握手的整个过程使用到了数字证书、对称加密、HASH摘要算法,接下来我们用实际代码来实现整个过程

四 使用java代码模拟整个握手过程

一:准备工作

1、创建java证书,

C:\> keytool -genkey -alias wangyi -keypass wangyi -keyalg RSA -keysize 1024 -keystore https.keystore -storepass wangyi

HTTPS那些事 用java实现HTTPS工作原理-LMLPHP

2、将创建的证书保存到C盘(为了方便演示)

C:\>keytool -export -keystore https.keystore -alias wangyi -file https.crt -storepass wangyi

HTTPS那些事 用java实现HTTPS工作原理-LMLPHP

二:代码实现

代码包含6个类,分别为:

名称说明
CertifcateUtils证书操作类
DesCoderDes对称加密和解密工具类
HttpsMockBasehttps父类
HttpsMockClientclient类
HttpsMockServer服务器类
SocketUtilssocket工具类
  1. package httpsmock;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.FileInputStream;
  4. import java.io.InputStream;
  5. import java.security.KeyStore;
  6. import java.security.PrivateKey;
  7. import java.security.PublicKey;
  8. import java.security.cert.CertificateFactory;
  9. /**
  10. * Created by kingj on 2014/8/13.
  11. */
  12. public class CertifcateUtils {
  13. public static byte[] readCertifacates() throws Exception{
  14. CertificateFactory factory=CertificateFactory.getInstance("X.509");
  15. InputStream in=new FileInputStream("c:/https.crt");
  16. java.security.cert.Certificate cate=factory.generateCertificate(in);
  17. return cate.getEncoded();
  18. }
  19. public static byte[] readPrivateKey() throws  Exception{
  20. KeyStore store=KeyStore.getInstance("JKS");
  21. InputStream in=new FileInputStream("c:/https.keystore");
  22. store.load(in,"wangyi".toCharArray());
  23. PrivateKey pk=(PrivateKey)store.getKey("wangyi","wangyi".toCharArray());
  24. return pk.getEncoded();
  25. }
  26. public static PrivateKey readPrivateKeys() throws  Exception{
  27. KeyStore store=KeyStore.getInstance("JKS");
  28. InputStream in=new FileInputStream("c:/https.keystore");
  29. store.load(in,"wangyi".toCharArray());
  30. PrivateKey pk=(PrivateKey)store.getKey("wangyi","wangyi".toCharArray());
  31. return pk;
  32. }
  33. public static PublicKey readPublicKeys() throws  Exception{
  34. CertificateFactory factory=CertificateFactory.getInstance("X.509");
  35. InputStream in=new FileInputStream("c:/https.crt");
  36. java.security.cert.Certificate cate=factory.generateCertificate(in);
  37. return cate.getPublicKey();
  38. }
  39. public static  java.security.cert.Certificate createCertiface(byte b[]) throws Exception{
  40. CertificateFactory factory=CertificateFactory.getInstance("X.509");
  41. InputStream in=new ByteArrayInputStream(b);
  42. java.security.cert.Certificate cate=factory.generateCertificate(in);
  43. return cate;
  44. }
  45. public static String byte2hex(byte[] b) {
  46. String hs = "";
  47. String stmp = "";
  48. for (int n = 0; n < b.length; n++) {
  49. stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
  50. if (stmp.length() == 1) {
  51. hs = hs + "0" + stmp;
  52. } else {
  53. hs = hs + stmp;
  54. }
  55. }
  56. return hs.toUpperCase();
  57. }
  58. }
  1. package httpsmock;
  2. /**
  3. * Created by kingj on 2014/8/13.
  4. */
  5. import org.apache.commons.codec.binary.Hex;
  6. import java.security.Key;
  7. import java.security.SecureRandom;
  8. import javax.crypto.Cipher;
  9. import javax.crypto.KeyGenerator;
  10. import javax.crypto.SecretKey;
  11. import javax.crypto.SecretKeyFactory;
  12. import javax.crypto.spec.DESKeySpec;
  13. /**
  14. * DES Coder<br/>
  15. * secret key length:   56 bit, default:    56 bit<br/>
  16. * mode:    ECB/CBC/PCBC/CTR/CTS/CFB/CFB8 to CFB128/OFB/OBF8 to OFB128<br/>
  17. * padding: Nopadding/PKCS5Padding/ISO10126Padding/
  18. * @author Aub
  19. *
  20. */
  21. public class DesCoder {
  22. /**
  23. * 密钥算法
  24. */
  25. private static final String KEY_ALGORITHM = "DES";
  26. private static final String DEFAULT_CIPHER_ALGORITHM = "DES/ECB/PKCS5Padding";
  27. //  private static final String DEFAULT_CIPHER_ALGORITHM = "DES/ECB/ISO10126Padding";
  28. /**
  29. * 初始化密钥
  30. *
  31. * @return byte[] 密钥
  32. * @throws Exception
  33. */
  34. public static byte[] initSecretKey(SecureRandom random) throws Exception{
  35. //返回生成指定算法的秘密密钥的 KeyGenerator 对象
  36. KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);
  37. //初始化此密钥生成器,使其具有确定的密钥大小
  38. kg.init(random);
  39. //生成一个密钥
  40. SecretKey  secretKey = kg.generateKey();
  41. return secretKey.getEncoded();
  42. }
  43. /**
  44. * 转换密钥
  45. *
  46. * @param key   二进制密钥
  47. * @return Key  密钥
  48. * @throws Exception
  49. */
  50. public static Key toKey(byte[] key) throws Exception{
  51. //实例化DES密钥规则
  52. DESKeySpec dks = new DESKeySpec(key);
  53. //实例化密钥工厂
  54. SecretKeyFactory skf = SecretKeyFactory.getInstance(KEY_ALGORITHM);
  55. //生成密钥
  56. SecretKey  secretKey = skf.generateSecret(dks);
  57. return secretKey;
  58. }
  59. /**
  60. * 加密
  61. *
  62. * @param data  待加密数据
  63. * @param key   密钥
  64. * @return byte[]   加密数据
  65. * @throws Exception
  66. */
  67. public static byte[] encrypt(byte[] data,Key key) throws Exception{
  68. return encrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
  69. }
  70. /**
  71. * 加密
  72. *
  73. * @param data  待加密数据
  74. * @param key   二进制密钥
  75. * @return byte[]   加密数据
  76. * @throws Exception
  77. */
  78. public static byte[] encrypt(byte[] data,byte[] key) throws Exception{
  79. return encrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
  80. }
  81. /**
  82. * 加密
  83. *
  84. * @param data  待加密数据
  85. * @param key   二进制密钥
  86. * @param cipherAlgorithm   加密算法/工作模式/填充方式
  87. * @return byte[]   加密数据
  88. * @throws Exception
  89. */
  90. public static byte[] encrypt(byte[] data,byte[] key,String cipherAlgorithm) throws Exception{
  91. //还原密钥
  92. Key k = toKey(key);
  93. return encrypt(data, k, cipherAlgorithm);
  94. }
  95. /**
  96. * 加密
  97. *
  98. * @param data  待加密数据
  99. * @param key   密钥
  100. * @param cipherAlgorithm   加密算法/工作模式/填充方式
  101. * @return byte[]   加密数据
  102. * @throws Exception
  103. */
  104. public static byte[] encrypt(byte[] data,Key key,String cipherAlgorithm) throws Exception{
  105. //实例化
  106. Cipher cipher = Cipher.getInstance(cipherAlgorithm);
  107. //使用密钥初始化,设置为加密模式
  108. cipher.init(Cipher.ENCRYPT_MODE, key);
  109. //执行操作
  110. return cipher.doFinal(data);
  111. }
  112. /**
  113. * 解密
  114. *
  115. * @param data  待解密数据
  116. * @param key   二进制密钥
  117. * @return byte[]   解密数据
  118. * @throws Exception
  119. */
  120. public static byte[] decrypt(byte[] data,byte[] key) throws Exception{
  121. return decrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
  122. }
  123. /**
  124. * 解密
  125. *
  126. * @param data  待解密数据
  127. * @param key   密钥
  128. * @return byte[]   解密数据
  129. * @throws Exception
  130. */
  131. public static byte[] decrypt(byte[] data,Key key) throws Exception{
  132. return decrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
  133. }
  134. /**
  135. * 解密
  136. *
  137. * @param data  待解密数据
  138. * @param key   二进制密钥
  139. * @param cipherAlgorithm   加密算法/工作模式/填充方式
  140. * @return byte[]   解密数据
  141. * @throws Exception
  142. */
  143. public static byte[] decrypt(byte[] data,byte[] key,String cipherAlgorithm) throws Exception{
  144. //还原密钥
  145. Key k = toKey(key);
  146. return decrypt(data, k, cipherAlgorithm);
  147. }
  148. /**
  149. * 解密
  150. *
  151. * @param data  待解密数据
  152. * @param key   密钥
  153. * @param cipherAlgorithm   加密算法/工作模式/填充方式
  154. * @return byte[]   解密数据
  155. * @throws Exception
  156. */
  157. public static byte[] decrypt(byte[] data,Key key,String cipherAlgorithm) throws Exception{
  158. //实例化
  159. Cipher cipher = Cipher.getInstance(cipherAlgorithm);
  160. //使用密钥初始化,设置为解密模式
  161. cipher.init(Cipher.DECRYPT_MODE, key);
  162. //执行操作
  163. return cipher.doFinal(data);
  164. }
  165. private static String  showByteArray(byte[] data){
  166. if(null == data){
  167. return null;
  168. }
  169. StringBuilder sb = new StringBuilder("{");
  170. for(byte b:data){
  171. sb.append(b).append(",");
  172. }
  173. sb.deleteCharAt(sb.length()-1);
  174. sb.append("}");
  175. return sb.toString();
  176. }
  177. }
  1. package httpsmock;
  2. import com.sun.org.apache.bcel.internal.generic.NEW;
  3. import javax.crypto.*;
  4. import javax.crypto.spec.DESKeySpec;
  5. import java.security.*;
  6. import java.security.spec.InvalidKeySpecException;
  7. import java.util.Random;
  8. /**
  9. * Created by kingj on 2014/8/13.
  10. */
  11. public class HttpsMockBase {
  12. static PrivateKey privateKey;
  13. static PublicKey publicKey;
  14. public static boolean byteEquals(byte a[],byte[] b){
  15. boolean equals=true;
  16. if(a==null || b==null){
  17. equals=false;
  18. }
  19. if(a!=null && b!=null){
  20. if(a.length!=b.length){
  21. equals=false;
  22. }else{
  23. for(int i=0;i<a.length;i++){
  24. if(a[i]!=b[i]){
  25. equals=false;
  26. break;
  27. }
  28. }
  29. }
  30. }
  31. return equals;
  32. }
  33. public static byte[] decrypt(byte data[]) throws Exception{
  34. // 对数据解密
  35. Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
  36. cipher.init(Cipher.DECRYPT_MODE, privateKey);
  37. return cipher.doFinal(data);
  38. }
  39. public static byte[] decrypt(byte data[],SecureRandom seed) throws Exception{
  40. // 对数据解密
  41. Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
  42. cipher.init(Cipher.DECRYPT_MODE, privateKey,seed);
  43. return cipher.doFinal(data);
  44. }
  45. public static byte[] decryptByPublicKey(byte data[],SecureRandom seed) throws Exception{
  46. if(publicKey==null){
  47. publicKey=CertifcateUtils.readPublicKeys();
  48. }
  49. // 对数据解密
  50. Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
  51. if(seed==null){
  52. cipher.init(Cipher.DECRYPT_MODE, publicKey);
  53. }else{
  54. cipher.init(Cipher.DECRYPT_MODE, publicKey,seed);
  55. }
  56. return cipher.doFinal(data);
  57. }
  58. public static byte[] decryptByDes(byte data[],SecureRandom seed) throws Exception{
  59. if(publicKey==null){
  60. publicKey=CertifcateUtils.readPublicKeys();
  61. }
  62. // 对数据解密
  63. Cipher cipher = Cipher.getInstance("DES");
  64. if(seed==null){
  65. cipher.init(Cipher.DECRYPT_MODE, publicKey);
  66. }else{
  67. cipher.init(Cipher.DECRYPT_MODE, publicKey,seed);
  68. }
  69. return cipher.doFinal(data);
  70. }
  71. public static byte[] encryptByPublicKey(byte[] data, SecureRandom seed)
  72. throws Exception {
  73. if(publicKey==null){
  74. publicKey=CertifcateUtils.readPublicKeys();
  75. }
  76. // 对数据加密
  77. Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
  78. if(seed==null){
  79. cipher.init(Cipher.ENCRYPT_MODE, publicKey);
  80. }else{
  81. cipher.init(Cipher.ENCRYPT_MODE, publicKey,seed);
  82. }
  83. return cipher.doFinal(data);
  84. }
  85. public static String byte2hex(byte[] b) {
  86. String hs = "";
  87. String stmp = "";
  88. for (int n = 0; n < b.length; n++) {
  89. stmp = (Integer.toHexString(b[n] & 0XFF));
  90. if (stmp.length() == 1) {
  91. hs = hs + "0" + stmp;
  92. } else {
  93. hs = hs +"  " + stmp;
  94. }
  95. }
  96. return hs.toUpperCase();
  97. }
  98. public static byte[] cactHash(byte[] bytes) {
  99. byte[] _bytes = null;
  100. try {
  101. MessageDigest md = MessageDigest.getInstance("SHA1");
  102. md.update(bytes);
  103. _bytes = md.digest();
  104. } catch (NoSuchAlgorithmException ex) {
  105. ex.printStackTrace();
  106. }
  107. return _bytes;
  108. }
  109. static String random(){
  110. StringBuilder builder=new StringBuilder();
  111. Random random=new Random();
  112. int seedLength=10;
  113. for(int i=0;i<seedLength;i++){
  114. builder.append(digits[random.nextInt(seedLength)]);
  115. }
  116. return builder.toString();
  117. }
  118. static char[] digits={
  119. '0','1','2','3','4',
  120. '5','6','7','8','9',
  121. 'a','b','c','d','e',
  122. 'f','g','h','i','j'
  123. };
  124. }
  1. package httpsmock;
  2. import java.io.DataInputStream;
  3. import java.io.DataOutputStream;
  4. import java.net.Socket;
  5. import java.security.Key;
  6. import java.security.SecureRandom;
  7. /**
  8. * Created by kingj on 2014/8/13.
  9. */
  10. public class HttpsMockClient extends  HttpsMockBase {
  11. static DataInputStream in;
  12. static DataOutputStream out;
  13. static Key key;
  14. public static void main(String args[]) throws  Exception{
  15. int port=80;
  16. Socket s=new Socket("localhost",port);
  17. s.setReceiveBufferSize(102400);
  18. s.setKeepAlive(true);
  19. in=new DataInputStream(s.getInputStream());
  20. out=new DataOutputStream(s.getOutputStream());
  21. shakeHands();
  22. System.out.println("------------------------------------------------------------------");
  23. String name="duck";
  24. writeBytes(name.getBytes());
  25. int len=in.readInt();
  26. byte[] msg=readBytes(len);
  27. System.out.println("服务器反馈消息:"+byte2hex(msg));
  28. Thread.sleep(1000*100);
  29. }
  30. private static void shakeHands() throws Exception {
  31. //第一步 客户端发送自己支持的hash算法
  32. String supportHash="SHA1";
  33. int length=supportHash.getBytes().length;
  34. out.writeInt(length);
  35. SocketUtils.writeBytes(out, supportHash.getBytes(), length);
  36. //第二步 客户端验证服务器端证书是否合法
  37. int skip=in.readInt();
  38. byte[] certificate=SocketUtils.readBytes(in,skip);
  39. java.security.cert.Certificate cc= CertifcateUtils.createCertiface(certificate);
  40. publicKey=cc.getPublicKey();
  41. cc.verify(publicKey);
  42. System.out.println("客户端校验服务器端证书是否合法:" +true);
  43. //第三步  客户端校验服务器端发送过来的证书成功,生成随机数并用公钥加密
  44. System.out.println("客户端校验服务器端发送过来的证书成功,生成随机数并用公钥加密");
  45. SecureRandom seed=new SecureRandom();
  46. int seedLength=2;
  47. byte seedBytes[]=seed.generateSeed(seedLength);
  48. System.out.println("生成的随机数为 : " + byte2hex(seedBytes));
  49. System.out.println("将随机数用公钥加密后发送到服务器");
  50. byte[] encrptedSeed=encryptByPublicKey(seedBytes, null);
  51. SocketUtils.writeBytes(out,encrptedSeed,encrptedSeed.length);
  52. System.out.println("加密后的seed值为 :" + byte2hex(encrptedSeed));
  53. String message=random();
  54. System.out.println("客户端生成消息为:"+message);
  55. System.out.println("使用随机数并用公钥对消息加密");
  56. byte[] encrpt=encryptByPublicKey(message.getBytes(),seed);
  57. System.out.println("加密后消息位数为 : " +encrpt.length);
  58. SocketUtils.writeBytes(out,encrpt,encrpt.length);
  59. System.out.println("客户端使用SHA1计算消息摘要");
  60. byte hash[]=cactHash(message.getBytes());
  61. System.out.println("摘要信息为:"+byte2hex(hash));
  62. System.out.println("消息加密完成,摘要计算完成,发送服务器");
  63. SocketUtils.writeBytes(out,hash,hash.length);
  64. System.out.println("客户端向服务器发送消息完成,开始接受服务器端发送回来的消息和摘要");
  65. System.out.println("接受服务器端发送的消息");
  66. int serverMessageLength=in.readInt();
  67. byte[] serverMessage=SocketUtils.readBytes(in,serverMessageLength);
  68. System.out.println("服务器端的消息内容为 :" + byte2hex(serverMessage));
  69. System.out.println("开始用之前生成的随机密码和DES算法解密消息,密码为:"+byte2hex(seedBytes));
  70. byte[] desKey= DesCoder.initSecretKey(new SecureRandom(seedBytes));
  71. key=DesCoder.toKey(desKey);
  72. byte[] decrpytedServerMsg=DesCoder.decrypt(serverMessage, key);
  73. System.out.println("解密后的消息为:"+byte2hex(decrpytedServerMsg));
  74. int serverHashLength=in.readInt();
  75. byte[] serverHash=SocketUtils.readBytes(in,serverHashLength);
  76. System.out.println("开始接受服务器端的摘要消息:"+byte2hex(serverHash));
  77. byte[] serverHashValues=cactHash(decrpytedServerMsg);
  78. System.out.println("计算服务器端发送过来的消息的摘要 : " +byte2hex(serverHashValues));
  79. System.out.println("判断服务器端发送过来的hash摘要是否和计算出的摘要一致");
  80. boolean isHashEquals=byteEquals(serverHashValues,serverHash);
  81. if(isHashEquals){
  82. System.out.println("验证完成,握手成功");
  83. }else{
  84. System.out.println("验证失败,握手失败");
  85. }
  86. }
  87. public static byte[] readBytes(int length) throws  Exception{
  88. byte[] undecrpty=SocketUtils.readBytes(in,length);
  89. System.out.println("读取未解密消息:"+byte2hex(undecrpty));
  90. return DesCoder.decrypt(undecrpty,key);
  91. }
  92. public static void writeBytes(byte[] data) throws  Exception{
  93. byte[] encrpted=DesCoder.encrypt(data,key);
  94. System.out.println("写入加密后消息:"+byte2hex(encrpted));
  95. SocketUtils.writeBytes(out,encrpted,encrpted.length);
  96. }
  97. }
  1. package httpsmock;
  2. import javax.net.ServerSocketFactory;
  3. import java.io.DataInputStream;
  4. import java.io.DataOutputStream;
  5. import java.net.ServerSocket;
  6. import java.net.Socket;
  7. import java.security.Key;
  8. import java.security.SecureRandom;
  9. import java.util.concurrent.ExecutorService;
  10. import java.util.concurrent.Executors;
  11. /**
  12. * Created by kingj on 2014/8/13.
  13. */
  14. public class HttpsMockServer extends HttpsMockBase {
  15. static DataInputStream in;
  16. static DataOutputStream out;
  17. static String hash;
  18. static Key key;
  19. static ExecutorService executorService= Executors.newFixedThreadPool(20);
  20. public static void main(String args[]) throws Exception{
  21. int port=80;
  22. ServerSocket ss= ServerSocketFactory.getDefault().createServerSocket(port);
  23. ss.setReceiveBufferSize(102400);
  24. ss.setReuseAddress(false);
  25. while(true){
  26. try {
  27. final Socket s = ss.accept();
  28. doHttpsShakeHands(s);
  29. executorService.execute(new Runnable() {
  30. @Override
  31. public void run() {
  32. doSocketTransport(s);
  33. }
  34. });
  35. }catch (Exception e){
  36. e.printStackTrace();
  37. }
  38. }
  39. }
  40. private static void doSocketTransport(Socket s){
  41. try{
  42. System.out.println("--------------------------------------------------------");
  43. int length=in.readInt();
  44. byte[] clientMsg=readBytes(length);
  45. System.out.println("客户端指令内容为:" + byte2hex(clientMsg));
  46. writeBytes("服务器已经接受请求".getBytes());
  47. }catch (Exception ex){
  48. ex.printStackTrace();
  49. }
  50. }
  51. public static byte[] readBytes(int length) throws  Exception{
  52. byte[] undecrpty=SocketUtils.readBytes(in,length);
  53. System.out.println("读取未解密消息:"+byte2hex(undecrpty));
  54. return DesCoder.decrypt(undecrpty,key);
  55. }
  56. public static void writeBytes(byte[] data) throws  Exception{
  57. byte[] encrpted=DesCoder.encrypt(data,key);
  58. System.out.println("写入加密后消息:"+byte2hex(encrpted));
  59. SocketUtils.writeBytes(out,encrpted,encrpted.length);
  60. }
  61. private static void doHttpsShakeHands(Socket s) throws Exception {
  62. in=new DataInputStream(s.getInputStream());
  63. out=new DataOutputStream(s.getOutputStream());
  64. //第一步 获取客户端发送的支持的验证规则,包括hash算法,这里选用SHA1作为hash
  65. int length=in.readInt();
  66. in.skipBytes(4);
  67. byte[] clientSupportHash=SocketUtils.readBytes(in,length);
  68. String clientHash=new String(clientSupportHash);
  69. hash=clientHash;
  70. System.out.println("客户端发送了hash算法为:"+clientHash);
  71. //第二步,发送服务器证书到客户端
  72. byte[] certificateBytes=CertifcateUtils.readCertifacates();
  73. privateKey=CertifcateUtils.readPrivateKeys();
  74. System.out.println("发送证书给客户端,字节长度为:"+certificateBytes.length);
  75. System.out.println("证书内容为:" + byte2hex(certificateBytes));
  76. SocketUtils.writeBytes(out, certificateBytes, certificateBytes.length);
  77. System.out.println("获取客户端通过公钥加密后的随机数");
  78. int secureByteLength=in.readInt();
  79. byte[] secureBytes=SocketUtils.readBytes(in, secureByteLength);
  80. System.out.println("读取到的客户端的随机数为:"+byte2hex(secureBytes));
  81. byte secureSeed[]=decrypt(secureBytes);
  82. System.out.println("解密后的随机数密码为:" +byte2hex(secureSeed));
  83. //第三步 获取客户端加密字符串
  84. int skip=in.readInt();
  85. System.out.println("第三步 获取客户端加密消息,消息长度为 :" +skip);
  86. byte[] data=SocketUtils.readBytes(in,skip);
  87. System.out.println("客户端发送的加密消息为 : " +byte2hex(data));
  88. System.out.println("用私钥对消息解密,并计算SHA1的hash值");
  89. byte message[] =decrypt(data,new SecureRandom(secureBytes));
  90. byte serverHash[]=cactHash(message);
  91. System.out.println("获取客户端计算的SHA1摘要");
  92. int hashSkip=in.readInt();
  93. byte[] clientHashBytes=SocketUtils.readBytes(in,hashSkip);
  94. System.out.println("客户端SHA1摘要为 : " + byte2hex(clientHashBytes));
  95. System.out.println("开始比较客户端hash和服务器端从消息中计算的hash值是否一致");
  96. boolean isHashEquals=byteEquals(serverHash,clientHashBytes);
  97. System.out.println("是否一致结果为 : " + isHashEquals);
  98. System.out.println("第一次校验客户端发送过来的消息和摘译一致,服务器开始向客户端发送消息和摘要");
  99. System.out.println("生成密码用于加密服务器端消息,secureRandom : "+byte2hex(secureSeed));
  100. SecureRandom secureRandom=new SecureRandom(secureSeed);
  101. String randomMessage=random();
  102. System.out.println("服务器端生成的随机消息为 : "+randomMessage);
  103. System.out.println("用DES算法并使用客户端生成的随机密码对消息加密");
  104. byte[] desKey=DesCoder.initSecretKey(secureRandom);
  105. key=DesCoder.toKey(desKey);
  106. byte serverMessage[]=DesCoder.encrypt(randomMessage.getBytes(), key);
  107. SocketUtils.writeBytes(out,serverMessage,serverMessage.length);
  108. System.out.println("服务器端发送的机密后的消息为:"+byte2hex(serverMessage)+",加密密码为:"+byte2hex(secureSeed));
  109. System.out.println("服务器端开始计算hash摘要值");
  110. byte serverMessageHash[]=cactHash(randomMessage.getBytes());
  111. System.out.println("服务器端计算的hash摘要值为 :" +byte2hex(serverMessageHash));
  112. SocketUtils.writeBytes(out,serverMessageHash,serverMessageHash.length);
  113. System.out.println("握手成功,之后所有通信都将使用DES加密算法进行加密");
  114. }
  115. }
  1. package httpsmock;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.DataInputStream;
  4. import java.io.DataOutputStream;
  5. import java.io.IOException;
  6. import java.net.Socket;
  7. import java.util.Arrays;
  8. /**
  9. * Created by kingj on 2014/8/13.
  10. */
  11. public class SocketUtils {
  12. public static void close(Socket s){
  13. try {
  14. s.shutdownInput();
  15. s.shutdownOutput();
  16. } catch (IOException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. public static byte[] readBytes(DataInputStream in,int length) throws IOException {
  21. int r=0;
  22. byte[] data=new byte[length];
  23. while(r<length){
  24. r+=in.read(data,r,length-r);
  25. }
  26. return data;
  27. }
  28. public static void writeBytes(DataOutputStream out,byte[] bytes,int length) throws IOException{
  29. out.writeInt(length);
  30. out.write(bytes,0,length);
  31. out.flush();
  32. }
  33. }

通过运行上述代码,我们可以看看服务器端和客户端控制台打印的消息记录(https握手完成后,整个过程数据传输都需要客户端和服务端使用约定的DES算法对数据进行加密和解密)

1、服务端消息记录

客户端发送了hash算法为:SHA1
发送证书给客户端,字节长度为:618
证书内容为:  30  8202  66  30  8201  CF  A0030201020204  51  84  FA  AF  300D0609  2A  86  48  86  F70D01010B0500  30  66  310F  300D0603  550406  1306  77  61  6E  67  79  69  310F  300D0603  550408  1306  77  61  6E  67  79  69  310F  300D0603  550407  1306  77  61  6E  67  79  69  310F  300D0603  55040A  1306  77  61  6E  67  79  69  310F  300D0603  55040B  1306  77  61  6E  67  79  69  310F  300D0603  550403  1306  77  61  6E  67  79  69  30  1E  170D  31  34  30  38  31  33  30  35  32  30  35  34  5A  170D  31  34  31  31  31  31  30  35  32  30  35  34  5A  30  66  310F  300D0603  550406  1306  77  61  6E  67  79  69  310F  300D0603  550408  1306  77  61  6E  67  79  69  310F  300D0603  550407  1306  77  61  6E  67  79  69  310F  300D0603  55040A  1306  77  61  6E  67  79  69  310F  300D0603  55040B  1306  77  61  6E  67  79  69  310F  300D0603  550403  1306  77  61  6E  67  79  69  30  81  9F  300D0609  2A  86  48  86  F70D010101050003  81  8D00  30  81  8902  81  8100  89  20  2A  F6  BF  1E  F9  95  F8  E5  E2  C2  C6  14  22  DB  23  10  2F  44  E0  AD0B  FB  89  62  8C  A6  E2  14  52  E7  5D  FE  7B  CC  A4  D2  F4  F9  C5  8E  E0  75  CC  F3  71  E9  29  85  A9  DA  D2  BD  93  73  12  74  2B  4C  D2  74  1A  13  82  64  20  E0  8B  68  FF  9A  F0  6F0C  880F  91  A5  FE  42  44  DE  81  F0  47  C7  67  2001  C7  7E  8B  36  87  E8  1B  7E  6907  D0  39  77  DE  53  D4  F5  67  57  BD  15  8E  51  E5  44  10  CD  BE  81  EB  E3  86  E8  73  B5  1D  1F  FF0203010001  A3  21  30  1F  30  1D0603  55  1D0E04  1604  14  E2  81  F2  3E  81  92  8B  DE  7A  1D  93  A9  28  23  A7  5D  E7  65  63  EB  300D0609  2A  86  48  86  F70D01010B050003  81  810002  E6  BF00  FB  CE  3A  4A  AC  9E  5F  10  6C  4F  FE  44  93  A4  6D  89  BC  4F  CB  25  30  1F  B4  C7  67  E3  E6  A1  1D  66  4B  DA  E4  6D  D8  90  CC  D2  74  34  48  6C  9B  33  2E  C2  4E  9E  AA  470B  9B  4000  7A  59  67  3E  C2  75  1A  A0  7A  48  16  53  D6  C4  53  97080B  F4  23  49  2E06  60  DF  9D  B4  5B  76  B2  AC  35  CF  2E  3C  CA  E3  B6  25  7D  F7  BA  69  6F  15  CE  AF  B4  9D  83  94  2E  5E  37  6E  C5  C2  B9  94  54  DB06  5D  7F  B6  70  1C  91  E6  E3
获取客户端通过公钥加密后的随机数
读取到的客户端的随机数为:  86  16  A9  65  F6  EC  A3  57  D6  23  A2  43  8F  F4  52  F5  37  14  F9  5B  27  6F  75  A3  25  C9  9E  D4  DD  CC  68  BA03  A2  1B  E6  8D  74  61  3B  28  28  9F  1F  5A  AD  5F  32  4B  40  81  98  54  AC0F  840B  80  BF  53  80  50  1E  A7  24  16  10  2A  2B  6A  8709  86  7C  20  75  20  14  7E  38  F3  FA  76  6207  D1  E1  37  28  93  D9  C1  2F  D4  9B  6E  9A  5205  9A  6D  54  8B  DD  1D  8205  DF  BC  AE  BB  6C  24  F5  6E  BC  F2  DE  26  AB  B1  87  1F  DA  DE  3B  25  1E
解密后的随机数密码为:  5B  D4
第三步 获取客户端加密消息,消息长度为 :128
客户端发送的加密消息为 :   32  76  EB  3E  93  E7  F1  590E  67  EB  FA  29  24  5D  F4  A2  3E  78  BE  61  49  B1  4C  91  1A  450A  B7  D7  E0  71  A1  30  C0  12  F905  9C  CF  B9  C9  75  6B  C4  39  3C  EF  5F  1005  75  AD  50  9A09  6F  8A  7F  C0  F4  20  E0  BC  DF  74  90  F3  6A  46  5E  6C  47  FC  16  EC  4D  DD  10  F9  87  ED  E4  47  83  37  B8  6A  5B  5B  B2  17  9306  7707  72  8E  3008  73  59  89  F5  F7  E6  66  89  4F  F7  B6  2B  41  7B  3B  1B  29  63  D0  11  D4  52  60  4A  3B  74  CA  1E
用私钥对消息解密,并计算SHA1的hash值
获取客户端计算的SHA1摘要
客户端SHA1摘要为 : 01  56  CB  DF  D3  EF  5A  8F  BB  85  BE  15  FB  83  D9  10  1F  64  F6  D8
开始比较客户端hash和服务器端从消息中计算的hash值是否一致
是否一致结果为 : true
第一次校验客户端发送过来的消息和摘译一致,服务器开始向客户端发送消息和摘要
生成密码用于加密服务器端消息,secureRandom :   5B  D4  (使用客户端第一次传过来的密码)
服务器端生成的随机消息为 : 2355384499
用DES算法并使用客户端生成的随机密码对消息加密
服务器端发送的机密后的消息为:  34  DE  39  CE  7A  280D  4F  44  83  51  2D  C3  EB  4F  1B,加密密码为:  5B  D4   (使用客户端第一次传过来的密码)
服务器端开始计算hash摘要值
服务器端计算的hash摘要值为 :  DD  3D  66  B5  C8  B6  A2  36  5E  D1  55  9A  B6  F7  C0  39  3C  97  1402
握手成功,之后所有通信都将使用DES加密算法进行加密
--------------------------------------------------------
读取未解密消息:  9D  2D  C2  D7  5D  2F  3C  F5
客户端指令内容为:  64  75  63  6B
写入加密后消息:  52  91  2C  62  E3  B9  5E  80  CF  3D  39  B4  7D  55  B7  3A  97  46  34  98  5603  DA  FC  A9  E1  D1  61  8F  24  64  D8

2、客户端消息记录

客户端校验服务器端证书是否合法:true (校验证书)
客户端校验服务器端发送过来的证书成功,生成随机数并用公钥加密
生成的随机数为 :   5B  D4  (客户端生成了随机密码,用于整个握手过程中)
将随机数用公钥加密后发送到服务器
加密后的seed值为 :  86  16  A9  65  F6  EC  A3  57  D6  23  A2  43  8F  F4  52  F5  37  14  F9  5B  27  6F  75  A3  25  C9  9E  D4  DD  CC  68  BA03  A2  1B  E6  8D  74  61  3B  28  28  9F  1F  5A  AD  5F  32  4B  40  81  98  54  AC0F  840B  80  BF  53  80  50  1E  A7  24  16  10  2A  2B  6A  8709  86  7C  20  75  20  14  7E  38  F3  FA  76  6207  D1  E1  37  28  93  D9  C1  2F  D4  9B  6E  9A  5205  9A  6D  54  8B  DD  1D  8205  DF  BC  AE  BB  6C  24  F5  6E  BC  F2  DE  26  AB  B1  87  1F  DA  DE  3B  25  1E
客户端生成消息为:9080292229
使用随机数并用公钥对消息加密
加密后消息位数为 : 128
客户端使用SHA1计算消息摘要
摘要信息为:01  56  CB  DF  D3  EF  5A  8F  BB  85  BE  15  FB  83  D9  10  1F  64  F6  D8
消息加密完成,摘要计算完成,发送服务器
客户端向服务器发送消息完成,开始接受服务器端发送回来的消息和摘要
接受服务器端发送的消息
服务器端的消息内容为 :  34  DE  39  CE  7A  280D  4F  44  83  51  2D  C3  EB  4F  1B
开始用之前生成的随机密码和DES算法解密消息,密码为:  5B  D4
解密后的消息为:  32  33  35  35  33  38  34  34  39  39
开始接受服务器端的摘要消息:  DD  3D  66  B5  C8  B6  A2  36  5E  D1  55  9A  B6  F7  C0  39  3C  97  1402
计算服务器端发送过来的消息的摘要 :   DD  3D  66  B5  C8  B6  A2  36  5E  D1  55  9A  B6  F7  C0  39  3C  97  1402
判断服务器端发送过来的hash摘要是否和计算出的摘要一致
验证完成,握手成功
------------------------------------------------------------------
写入加密后消息:  9D  2D  C2  D7  5D  2F  3C  F5
读取未解密消息:  52  91  2C  62  E3  B9  5E  80  CF  3D  39  B4  7D  55  B7  3A  97  46  34  98  5603  DA  FC  A9  E1  D1  61  8F  24  64  D8
服务器反馈消息:  E6  9C  8D  E5  8A  A1  E5  99  A8  E5  B7  B2  E7  BB  8F  E6  8E  A5  E5  8F  97  E8  AF  B7  E6  B1  82

05-04 05:09