废话不多说,本文将介绍常见的几种安全算法:数字摘要,对称加密,非对称加密,数字签名,数字证书。
1.数字摘要
数字摘要(消息摘要)是将一个消息或者文本内容使用函数或算法转换成固定长度的值。
如 函数y= f(x) x即为消息或文本, y为数字摘要。当消息和摘要(y1)在网络传递时,如果消息在网络传递过程中被恶意的篡改了,接收者通过对消息使用相同的算法或函数重新计算得到新数字摘要(y2),新数字摘要(y2)与原数字摘要(y1)进行比较,就可以判断消息是否被篡改。因此数字摘要可以验证消息的完整性。
hash碰撞:如果两个消息时相同的,计算出的值(数字摘要)一定时相同的,如果两个不同的消息,计算出的值不一定相同,这种情况称之为hash碰撞。因此一个hash函数的好坏取决于碰撞的概率,如果说攻击者能够利用hash碰撞轻易的伪造出不同的消息hash值(数字摘要)时相同的。那么这样的hash函数时危险的。可以认为,摘要的长度越长算法越安全。由于数字摘要并不包含原文的完整信息,因此,要从摘要信息逆向得出摘要的明文,原则上是不肯能完成的。
数字摘要的特点:
(1)无论输入的消息有多长,计算出的摘要长度是固定的。MD5算法计算出的摘要长度是128位,SHA-1算法计算出的摘要160位。
(2)一般输入的消息不同,则对应摘要不同,这种情况是hash碰撞。但消息相同,对应摘要必然相同。
(3)摘要并不包含原文的完整信息,因此逆向得不到原文信息。如果你想通过穷举的方式,采用暴力手段,尝试每一个信息计算出摘要,然后于原摘要进行对比,是可以回复原文的,但以目前的计算水平来看,需要消耗很长时间,因此被认为是不可能实现的。
接下来一起看一下获取数字摘要相关的算法。
MD5 算法:
MD5(Message Digest Algorithm 5) 信息摘要算法5,是数字摘要算法的一种实现摘要长度为128位。MD5由MD2,MD3,MD4改进而来,主要增强了算法复杂度和不可逆性。该算法具有普遍,稳定,快速的特点,因此业内广泛使用。目前主流编程语言都有MD5实现,下面是java中MD5的使用:
public static byte[] encode(String content) throws Exception{
//获取MD5算法
MessageDigest md = MessageDigest.getInstance("MD5");
//通过MD5中digest方法获取数字摘要
byte[] bytes = md.digest(content.getBytes("utf-8"));
return bytes;
}
//原文 content:hello,world
//生成的摘要(16进制编码后):22bd33d4c72d1986ccb4227ff7fle7265
SHA算法:
SHA(Secure Hash Algorithm) 安全散列算法 其修订版本 SHA-1 是基于MD4算法的,是目前公认的安全散列算法之一,其生成摘要的长度为160位。由于摘要 长度比MD5长,运算过程更加复杂,因此在相同的硬件上,SHA-1的效率相对MD5较慢,但安全性更高。下面是Java中SHA-1的使用:
public static byte[] encode(String content) throws Exception{
//获取SHA-1
MessageDigest md = MessageDigest.getInstance("SHA-1");
//获取摘要
byte[] bytes = md.digest(content.getBytes("utf-8"));
return bytes;
}
//原文 content: hello,world
//数字摘要(16进制编码后):deb945d3e6fe72dbla290bcfcf53057clcaafdel
16进制编码:
16进制由0-9和a-f表示,其中a-f对应10进制中的10-15
java 实现16进制编码:
/**
* byte数组 转换成 16进制小写字符串
*/
public static String bytes2Hex(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
return null;
}
StringBuilder hex = new StringBuilder();
for (byte b : bytes) {
hex.append(HEXES[(b >> 4) & 0x0F]);
hex.append(HEXES[b & 0x0F]);
}
return hex.toString();
}
java 实现16进制解码:
/**
* 16进制字符串 转换为对应的 byte数组
*/
public static byte[] hex2Bytes(String hex) {
if (hex == null || hex.length() == 0) {
return null;
}
char[] hexChars = hex.toCharArray();
byte[] bytes = new byte[hexChars.length / 2]; // 如果 hex 中的字符不是偶数个, 则忽略最后一个
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) Integer.parseInt("" + hexChars[i * 2] + hexChars[i * 2 + 1], 16);
}
return bytes;
}
Base64编码:
很多人认为Base64是安全加密算法,并且将其当作加密算法使用,实际并非如此,因为任何人得到Base编码后的内容,通过相同的方法就可以得到原文信息。因此Base64只能算作编码算法。下面是java中base64的使用:
//编码
private static String encode(byte[] bytes) throws Exception{
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(bytes);
}
//解码
private static byte[] decode(String content) throws Exception{
BASE64Dncoder decoder = new BASE64Dncoder();
return dncoder.decode(content);
}
2.对称加密:
数据发送方将密文(明文通过密钥加密的)和加密密钥一起经过特殊加密算法处理后,生成复杂的加密密文进行发送,数据接收方是收到密文后,若想读取数据,则需要使用加密使用的密钥及相同算法的逆算法对加密的密文进行解密,才能使其回复可读明文,在对称加密算法中,使用的密钥只有一个,发送和接收方都使用这个密钥进行加密和解密,这就要求发送方和接受方事先必须知道加密的密钥。
对称加密算法特点:算法公开,计算量小,加密速度快,加密效率高。其安全性依赖于密钥,因此保护密钥不被泄露至关重要。
常见的对称加密:AES,DES等。
AES:
AES(Advanced Encryption Standard)高级加密标准,用来替代DES算法,是对称加密算法中最流行的算法之一。主要特点:强安全性,高性能,高效率,易用灵活等特点。设计有3个密钥长度(128,192,256位)比DES更安全。
java中AES算法的使用:
/**
* 生成base64编码的密钥字符串
*/
public static String genKeyAes() throws Exception{
//获取AEA生成器
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
//设置密钥位数
keyGen.init(128);
//获取密钥
SecretKey key = kenGen.generateKey();
//将密钥转成base64字符串
String base64Str = byte2Base64(key.getEncode());
return base64Str ;
}
/**
* 将base64编码格式的密钥字符串 转成 SecretKey对象
*/
public static SecretKey loadKeyAES(String base64Str) throws Exception{
//获取密钥
byte[] bytes = base642byte(base64Str);
//将密钥转成 SecretKey对象
SecretKey key = new SecretKeySpec(bytes,"AES");
return key;
}
加密与解密:
/**
* 对称加密
*/
public static String encryptAES(String source,SecretKey key) throws Exception{
//实例化对象cipher
Cipher cipher = Cipher.getInstance("AES");
//初始化加密模式
cipher.init(Cipher.ENCRYPT_MODE,key);
//得到加密字节数组
byte[] bytes = cipher.dofinal(source.getBytes());
//将加密字节数组 转成base64编码格式字符串
String miwen = byte2Base64(bytes);
return miwen ;
}
/**
* 对称解密
*/
public static String decryptAES(String source,SecretKey key) throws Exception{
//实例化对象cipher
Cipher cipher = Cipher.getInstance("AES");
//初始化加密模式
cipher.init(Cipher.DECRYPT_MODE,key);
//得到原文字节数组
byte[] bytes = cipher.dofinal(source.getBytes());
//将原文字节数组 转成base64编码格式字符串
String yuanwen = byte2Base64(bytes);
return yuanwen;
}
DES基于java的用法和AES一样 只是将上诉代码中的"AES"换成 DES即可 这里就不多赘述了。
3.非对称加密:
非对称加密:需要两把密钥,一把公钥,一把私钥。公钥加密对应私钥解密,私钥加密对应公钥解密。
非对称加密算法实现信息交换的基本过程:
非对称加密的特点:非对称加密的公钥是公开的,因此不需要在网络上传送密钥,大大提高了安全性,但非对称加密比对称加密复杂,加密速度相对较慢。
因此广泛使用非对称与对称加密结合使用的方法,优缺点互补,达到时间与安全的平衡。对称加密速度快,用来加密较长的文件(明文),然后用非对称加密来给文件密钥加密。
当前使用最广泛的非对称加密算法为RSA。
基于java的RSA算法的使用:
公钥和私钥字符串:
/**
*获取keyPair对象
*/
public static keyPair getKeyPair() throws Exception{
//获取RSA生成器
KeyPairGenerator k = KeyPairGenerator.getInstance("RSA");
//初始化密钥长度
k.initialize(512);
//获取keyPair对象
KeyPair keyPair = k.generateKeyPair();
return keyPair;
}
/**
*获取公钥字符串:通过keyPair对象
*/
public static String getPublicKey(KeyPair keyPair){
//公钥对象
PublicKey publicKey = keyPair.getPublic();
//公钥字节数组
byte[] bytes = publicKey.getEncode();
//公钥转成base64格式编码字符串
return byte2Base64(bytes);
}
/**
*获取私钥字符串:通过keyPair对象
*/
public static String getPrivateKey(KeyPair keyPair){
//私钥对象
PrivateKey privateKey = keyPair.getPublic();
//私钥字节数组
byte[] bytes = privateKey .getEncode();
//私钥转成base64格式编码字符串
return byte2Base64(bytes);
}
通过公钥和私钥字符串获取公钥对象(PublicKey)和私钥对象(PrivateKey):
/**
*获取PublicKey 对象
*/
public static PublicKey string2PublicKey(String pubStr) throws Exception{
//将base64编码的字符串 转成 字节数组公钥
byte[] keyBytes = base642byte(pubStr);
//将字节数组公钥转成 X509EncodeKeySpec 对象
X509EncodeKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
//获取KeyFactory
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
//获取PublicKey 对象
PublicKey publicKey = keyFactory .generateFactory(keySpec);
return publicKey;
}
/**
*获取PrivateKey 对象
*/
public static PrivateKey string2PrivateKey (String priStr) throws Exception{
//将base64编码的字符串 转成 字节数组公钥
byte[] keyBytes = base642byte(priStr);
//将字节数组公钥转成 X509EncodeKeySpec 对象
PKCS8EncodeKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
//获取KeyFactory
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
//获取PrivateKey 对象
PrivateKey privateKey = keyFactory .generateFactory(keySpec);
return privateKey ;
}
根据PublicKey与PrivateKey 进行加密和解密,这里使用公钥加密,私钥解密:
/**
*公钥加密
*/
public static String publicEncrypt(String content,PublicKey publicKey) throws Exception{
//获取Cipher对象
Cipher cipher = Cipher.getInstance("RSA");
//初始化加密模式
cipher.init(Cipher.ENCRYPT_MODE,publicKey);
//获取公钥加密的密文字节数组
byte[] bytes = cipher.dofinal(content.getBytes());
//转成bas64编码格式的字符串
return byte2Base64(bytes);
}
/**
*私钥解密
*/
public static String privateDecrypt(String content,PrivateKey privateKey) throws Exception{
//获取Cipher对象
Cipher cipher = Cipher.getInstance("RSA");
//初始化加密模式
cipher.init(Cipher.DECRYPT_MODE,privateKey);
//获取公钥加密的密文字节数组
byte[] bytes = cipher.dofinal(content.getBytes());
//转成bas64编码格式的字符串
return byte2Base64(bytes);
}
4.数字签名
数字签名是对非对称加密与数字摘要的综合使用,指的是将通信内容的摘要信息使用发送者的私钥进行加密。
接收方通过发送者的公钥解密得到摘要信息,然后使用发送者相同的摘要算法对原文进行摘要,将两者摘要信息进行对比校验,判断原文是否被串改。
校验是否被串改过程:
介绍完上面的各种加密算法后,来个支付包加密流程,是各种加密算法的综合运用:
流程讲解:
主要看加密包(发送的数据包)部分:分为信息,签名,会话密钥。
信息:将原文通过会话密钥(对称加密的密钥)加密生成密文 (步骤1)
签名:将原文通过MD5进行数字摘要,在对摘要使用信息发送方A的私钥加密 生成签名 (步骤2)
会话密钥:其实就是对称加密的密钥,可以是AES。使用接收方B的公钥加密会话密钥 (步骤3)
将加密包发送给信息接收方B:
使用接收方B的私钥解密 得到会话密钥 (步骤4)
使用会话密钥对信息进行解密 得到原文 (步骤5)
将原文使用相同的摘要算法MD5 得到摘要A (步骤6)
使用发送方A的公钥解密数字签名 得到 摘要B 步骤6)
校验摘要A与摘要B是否相等,不相等,说明原文被篡改。
本文主要参考书籍: