什么是SSL/TLS
SSL(secure sockets layer,安全套接层)安全传输技术。TCP是传输层的协议,但是它是明文传输的,是不安全的。SSL的诞生给TCP加了一层保险,为TCP通信提供安全及数据完整性保护。TLS只是SSL的升级版,它们的作用是一样的。TLS(TransportLayer Security,传输层安全协议)由两层组成:TLS记录(TLS Record)和TLS握手(TLSHandshake)。TLS协议是更新、更安全的SSL协议版本。
SSL/TLS协议的分层结构
SSL/TLS协议包括握手协议(Handshake Protocol)、密码变化协议(SSL Change Cipher SpecProtocol)、警告协议(Alert Protocol)、记录协议(Record Protocol)。
(1)握手协议:SSL/TLS协议非常重要的组成部分,用来协商通信过程中使用的加密套件(加密算法、密钥交换算法和MAC算法等)、在服务端和客户端之间安全地交换密钥、实现服务端和客户端的身份验证。
(2)密码变化协议:客户端和服务端通过密码变化协议通知对端,随后的报文都将使用新协商的加密套件和密钥进行保护和传输。
(3)警告协议:用来向对端发送告警信息,消息中包含告警的严重级别和描述。
(4)应用数据协议:负责将SSL/TLS承载的应用数据传达给通信对端。
(5)记录协议:主要负责对上层的数据(SSL/TLS握手协议、SSL/TLS密码变化协议、SSL/TLS警告协议和应用数据协议)进行分块计算、添加MAC值、加密等处理,并把处理后的记录块传输给对端。
SSL/TLS协议的分层结构如图所示:
SSL/TLS协议主要分为两层(上层的是握手协议、密码变化协议、警告协议和应用数据协议,下层的是记录协议),主要负责使用对称密码对消息进行加密。其中,握手协议(Handshake Protocol)是SSL/TSL通信中最复杂的子协议,也是安全通信所涉及的第一个子协议。
加密算法原理
为了理解SSL/TLS原理,大家需要掌握一些加密算法的基础知识。当然,这不是为了让大家成为密码学专家,所以只需对基础的加密算法有一些了解即可。基础的加密算法主要有哈希(Hash,或称为散列)、对称加密(Symmetric Cryptography)、非对称加密(Asymmetric Cryptography)、数字签名(Digital Signature)。
哈希单向加密算法原理
哈希算法(或称为散列算法)比较简单,就是为待加密的任意大小的信息(如字符串)生成一个固定大小(比如通过MD5加密之后是32个字符)的字符串摘要。常用的哈希算法有MD5、SHA1、SHA-512等。哈希是不可逆的加密技术,一些数据一旦通过哈希转换为其他形式,源数据将永远无法恢复。
在哪些场景下使用哈希加密呢?一般来说,在用户注册的时候,服务端保存用户密码的时候会将明文密码的哈希密码存储在数据库中,而不是直接存储用户的明文密码。当用户下次登录时,会对用户的登入密码(明文)使用相同的哈希算法进行处理,并将哈希结果与来自数据库的哈希密码进行匹配,如果是相同的,那么用户将登录成功,否则用户将登录失败。
哈希加密也称为单向哈希加密,是通过对不同输入长度的信息进行哈希计算得到固定长度的输出,是单向、不可逆的。所以,即使保存用户密码的数据库被攻击,也不会造成用户的密码泄漏。
最常见的哈希算法为MD5(Message-Digest Algorithm 5,信息-摘要算法5),也是计算机广泛使用的哈希算法之一。主流编程语言普遍都提供MD5实现,MD5的前身有MD2、MD3和MD4。曾经,MD5一度被广泛应用于安全领域。随着MD5的弱点不断被发现,以及计算机能力的不断提升,该算法不再适合当前的安全环境。目前,MD5计算广泛应用于错误检查。例如,在一些文件下载中,软件通过计算MD5和检验下载所得文件的完整性。MD5将输入的不定长度信息经过程序流程生成四个32位(Bit)数据,最后联合起来输出一个固定长度128位的摘要,基本处理流程包括求余、取余、调整长度、与链接变量进行循环运算等,最终得出结果。除了MD5,Java还提供了SHA1、SHA256、SHA512等哈希摘要函数的实现。除了在算法上有些差异之外,这些哈希函数的主要不同在于摘要长度,MD5生成的摘要是128位,SHA1生成的摘要是160位,SHA256生成的摘要是256位,SHA512生成的摘要是512位。
SHA-1与MD5的最大区别在于其摘要比MD5摘要长32位(相当于长4字节,转换十六进制后比MD5多8个字符)。对SHA-1强行攻击的强度比对MD5攻击的强度要大。但是SHA-1哈希过程的循环步骤比MD5多,且需要的缓存大,因此SHA-1的运行速度比MD5慢。
以下代码使用Java提供的MD5、SHA1、SHA256、SHA512等哈希摘要函数生成哈希摘要(哈希加密结果)并进行验证的案例:
package test;
import java.security.MessageDigest;
public class HashCrypto {
/**
* 哈希单向加密测试用例
*/
public static String encrypt(String plain) {
StringBuffer md5Str = new StringBuffer(32);
try {
/**
* MD5
*/
//MessageDigest md = MessageDigest.getInstance("MD5");
/**
* SHA-1
*/
//MessageDigest md = MessageDigest.getInstance("SHA-1");
/**
* SHA-256
*/
//MessageDigest md = MessageDigest.getInstance("SHA-256");
/**
* SHA-512
*/
MessageDigest md = MessageDigest.getInstance("SHA-512");
String charset = "UTF-8";
byte[] array = md.digest(plain.getBytes(charset));
for (byte b : array) {
//转成十六进制字符串
String hexString = Integer.toHexString((0x000000FF & b) | 0xFFFFFF00);
log.debug("hexString:{}, 第6位之后: {}", hexString, hexString.substring(6));
md5Str.append(hexString.substring(6));
}
} catch (Exception ex) {
ex.printStackTrace();
}
return md5Str.toString();
}
public static void main(String[] args) {
//原始的明文字符串,也是需要加密的对象
String plain = "123456";
//使用哈希函数加密
String cryptoMessage = HashCrypto.encrypt(plain);
log.info("cryptoMessage:{}", cryptoMessage);
//验证
String cryptoMessage2 = HashCrypto.encrypt(plain);
log.info("验证 {},\n是否一致:{}", cryptoMessage2,
cryptoMessage.equals(cryptoMessage2));
//验证2
String plainOther = "654321";
String cryptoMessage3 = HashCrypto.encrypt(plainOther);
log.info("验证 {},\n是否一致:{}", cryptoMessage3,
cryptoMessage.equals(cryptoMessage3));
}
}
对称加密算法原理
对称加密(Symmetric Cryptography)指的是客户端自己封装一种加密算法,将给服务端发送的数据进行加密,并且将数据加密的方式(密钥)发送给密文,服务端收到密钥和数据,用密钥进行解密。
对称加密的典型处理流程大致如图所示:
对称加密:使用同一个密钥加密和解密,优点是速度快;但是它要求共享密钥,缺点是密钥管理不方便、容易泄露。
常见的对称加密算法有DES、AES等。DES加密算法出自IBM的数学研究,被美国政府正式采用之后开始广泛流传,但是近些年来使用越来越少,因为DES使用56位密钥,以现代计算能力24小时内即可被破解。虽然如此,但是在对安全要求不高的应用中,还是可以使用DES加密算法。
下面是一段使用Java语言编写的进行DES加密的演示代码:
package com.crazymakercircle.secure.crypto;
//省略import
public class DESCrypto
{
/**
* 对称加密
*/
public static byte[] encrypt(byte[] data, String password) {
try{
SecureRandom random = new SecureRandom();
//使用密码,创建一个密钥描述符
DESKeySpec desKey = new DESKeySpec(password.getBytes());
//创建一个密钥工厂,然后用它把 DESKeySpec 密钥描述符实例转换成密钥
SecretKeyFactory keyFactory =
SecretKeyFactory.getInstance("DES");
//通过密钥工程生成密钥
SecretKey secretKey = keyFactory.generateSecret(desKey);
//Cipher对象实际完成加密操作
Cipher cipher = Cipher.getInstance("DES");
//用密钥初始化Cipher对象
cipher.init(Cipher.ENCRYPT_MODE, secretKey, random);
//为数据执行加密操作
return cipher.doFinal(data);
}catch(Throwable e){
e.printStackTrace();
}
return null;
}
/**
* 对称解密
*/
public static byte[] decrypt(byte[] cryptData,
String password) …{
//DES算法要求有一个可信任的随机数源
SecureRandom random = new SecureRandom();
//创建一个 DESKeySpec 密钥描述符对象
DESKeySpec desKey = new DESKeySpec(password.getBytes());
//创建一个密钥工厂
SecretKeyFactory keyFactory =
SecretKeyFactory.getInstance("DES");
//将 DESKeySpec 对象转换成 SecretKey 对象
SecretKey secretKey = keyFactory.generateSecret(desKey);
//Cipher对象实际完成解密操作
Cipher cipher = Cipher.getInstance("DES");
//用密钥初始化Cipher对象
cipher.init(Cipher.DECRYPT_MODE, secretKey, random);
//真正开始解密操作
return cipher.doFinal(cryptData);
}
public static void main(String args[]) {
//待加密内容
String str = "123456";
//密码长度要是8的倍数
String password = "12345678";
byte[] result = DESCrypto.encrypt(str.getBytes(),password);
log.info("str:{} 加密后:{}",str,new String(result));
//直接将如上内容解密
try {
byte[] decryResult = DESCrypto.decrypt(result, password);
log.info("解密后:{}",new String(decryResult));
} catch (Exception e1) {
e1.printStackTrace();
}
}
}