对称密码技术:发件人和收件人使用其共同拥有相同的单个密钥,这种密钥既用于加密,也用于解密,叫做机密密钥(也称为对称密钥或会话密钥)。
能够提供信息机密性(没有密钥信息不能被解密)、完整性(被改变的信息不能被解密)的服务。
对称式密码学又称:单钥密码学、秘密密钥密码学、会话密钥密码学、私钥密码学、共享秘钥密码学。
DES(数据加密标准):分组式加密,算法源于Lucifer,作为NIST对称式加密标准;64位(有效位56位、校验8位),分组算法
3DES:128位,分组算法IDEA(国际数据加密算法):128位,比DES快,分组算法
AES(高级加密标准):DES升级版,算法出自 Rinjindael
优点:用户只需记忆一个密钥,就可用于加密、解密;与非对称加密方法相比,加密解密的计算量小,速度快,简单易用,适合于对海量数据进行加密处理。
缺点:如果密钥交换不安全,密钥的安全性就会丧失。特别是在电子商务环境下,当客户是未知的、不可信的实体时,如何使客户安全地获得密钥就成为一大难题。
如果用户较多情况下的密钥管理问题。如果密钥多个用户被共享,不能提供抗抵赖性。
对称加密一般适用于后台与后台之间的通讯,不暴露外网地址,无法让第三方抓包的场景。在移动端,不能使用对称加密,防止黑客把APK(移动端APP)反编译得到秘钥。
DES 加解密工具类:
package com.study.util;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.security.SecureRandom;
/**
* @author biandan
* @description
* @signature 让天下没有难写的代码
* @create 2021-06-03 下午 5:52
*/
public class DESUtil {
public static void main(String[] args) {
// 待加密内容
String str = "biandan";
// 密码,长度要是8的倍数
String password = "87654321";
byte[] result = DESUtil.encrypt(str.getBytes(), password);
System.out.println("加密后:" + new String(result));
// 直接将如上内容解密
try {
byte[] decryResult = DESUtil.decrypt(result, password);
System.out.println("解密后:" + new String(decryResult));
} catch (Exception e1) {
e1.printStackTrace();
}
}
/**
* 加密
* @param datasource 数据源
* @param password 密码
* @return
*/
public static byte[] encrypt(byte[] datasource, String password) {
try {
SecureRandom random = new SecureRandom();
DESKeySpec desKey = new DESKeySpec(password.getBytes());
// 创建一个密匙工厂,然后用它把DESKeySpec转换成
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey securekey = keyFactory.generateSecret(desKey);
// Cipher对象实际完成加密操作
Cipher cipher = Cipher.getInstance("DES");
// 用密匙初始化Cipher对象,ENCRYPT_MODE用于将 Cipher 初始化为加密模式的常量
cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
// 现在,获取数据并加密
// 正式执行加密操作
return cipher.doFinal(datasource); // 按单部分操作加密或解密数据,或者结束一个多部分操作
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
/**
* 解密
* @param src
* @param password
* @return
* @throws Exception
*/
public static byte[] decrypt(byte[] src, String password) throws Exception {
// DES算法要求有一个可信任的随机数源
SecureRandom random = new SecureRandom();
// 创建一个DESKeySpec对象
DESKeySpec desKey = new DESKeySpec(password.getBytes());
// 创建一个密匙工厂
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");// 返回实现指定转换的
// Cipher 对象
// 将DESKeySpec对象转换成SecretKey对象
SecretKey securekey = keyFactory.generateSecret(desKey);
// Cipher对象实际完成解密操作
Cipher cipher = Cipher.getInstance("DES");
// 用密匙初始化Cipher对象
cipher.init(Cipher.DECRYPT_MODE, securekey, random);
// 真正开始解密操作
return cipher.doFinal(src);
}
}
测试:
使用一对(2个)密钥:一个用于加密信息,另一个则用于解密信息。
两个密钥之间存在着相互依存关系:即用其中任一个密钥加密的信息只能用另一个密钥进行解密。(公钥加密,私钥解密。或者,私钥加密,公钥解密。一般是公钥加密私钥解密)
密钥依据性质划分,将其中的一个向外界公开,称为公钥;另一个则自己保留,称为私钥。公钥(Public key)常用于数据加密(用对方公钥加密)或签名验证(用对方公钥解密),私钥(Private key)常用于数据解密(发送方用接收方公钥加密)或数字签名(用自己私钥加密)。
特性:机密性、完整性、抗抵赖性
场景1:甲给乙传递数据。
乙:生成两把密钥(公钥和私钥),乙把公钥给甲,私钥自己保留。
甲:获取乙的公钥,然后用它对信息加密,把数据传递给乙。
乙:得到甲发送的加密后信息,用自己的私钥解密,得到明文数据。
场景2:乙给甲传递数据
乙:生成两把密钥(公钥和私钥),乙把公钥给甲,自己保留私钥。
乙:用自己的私钥加密数据,传递给甲。
甲:获取乙方私钥加密数据,用公钥解密。
优点:难破解
缺点: 加密速度慢
RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)
非对称加密示例:
package com.study.util;
import org.apache.tomcat.util.codec.binary.Base64;
import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
/**
* @author biandan
* @description RSA 非对称加密工具类
* @signature 让天下没有难写的代码
* @create 2021-06-03 下午 10:12
*/
public class RSAUtil {
public static String publicKey; // 公钥
public static String privateKey; // 私钥
public static String RSA = "RSA";//加密算法
//生成公钥和私钥,它们是成对出现的
public static void generateKey() {
// 1.初始化秘钥
KeyPairGenerator keyPairGenerator;
try {
keyPairGenerator = KeyPairGenerator.getInstance(RSA);
SecureRandom sr = new SecureRandom(); // 随机数生成器
keyPairGenerator.initialize(512, sr); // 设置最少512位长的秘钥
KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 开始创建
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
// 进行转码
publicKey = Base64.encodeBase64String(rsaPublicKey.getEncoded());
// 进行转码
privateKey = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 私钥匙加密或解密
*
* @param content
* @param privateKeyStr
* @param mode 1=加密,2=解密
* @return
*/
public static String encryptByPrivateKey(String content, String privateKeyStr, int mode) {
// 私钥要用PKCS8进行处理
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyStr));
String result = null;
try {
KeyFactory keyFactory = KeyFactory.getInstance(RSA);
// 还原Key对象
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance(RSA);
cipher.init(mode, privateKey);
byte[] bytes;
int maxLength;//最大加密、解密字节数,超出最大字节数需要分组加密、解密
if(mode == Cipher.DECRYPT_MODE){//解密
bytes = Base64.decodeBase64(content);
maxLength = 64;
}else{
bytes = content.getBytes("UTF-8");
maxLength = 32;
}
int dataLength = bytes.length;
System.out.println("私钥获取的数据长度="+dataLength);
int offSet = 0;//偏移值
byte[] resultBytes = {};
byte[] tempByte;
while (dataLength - offSet > 0) {
if (dataLength - offSet > maxLength) {
tempByte = cipher.doFinal(bytes, offSet, maxLength);
offSet += maxLength;
} else {
tempByte = cipher.doFinal(bytes, offSet, dataLength - offSet);
offSet = dataLength;
}
resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + tempByte.length);
System.arraycopy(tempByte, 0, resultBytes, resultBytes.length - tempByte.length, tempByte.length);
}
if (mode == Cipher.ENCRYPT_MODE) {// 加密
result = Base64.encodeBase64String(resultBytes);
}else{
result = new String(resultBytes, "UTF-8");//解密
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 公钥匙加密或解密
*
* @param content
* @param publicKeyStr
* @param mode 1=加密,2=解密
* @return
*/
public static String encryptByPublicKey(String content, String publicKeyStr, int mode) {
// 公钥要用X509进行处理
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyStr));
String result = null;
try {
KeyFactory keyFactory = KeyFactory.getInstance(RSA);
// 还原Key对象
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance(RSA);
cipher.init(mode, publicKey);
byte[] bytes;
int maxLength;//最大加密、解密字节数,超出最大字节数需要分组加密、解密
if(mode == Cipher.DECRYPT_MODE){//解密
bytes = Base64.decodeBase64(content);
maxLength = 64;
}else{
bytes = content.getBytes("UTF-8");
maxLength = 32;
}
int dataLength = bytes.length;
System.out.println("公钥获取的数据长度="+dataLength);
int offSet = 0;//偏移值
byte[] resultBytes = {};
byte[] tempByte;
while (dataLength - offSet > 0) {
if (dataLength - offSet > maxLength) {
tempByte = cipher.doFinal(bytes, offSet, maxLength);
offSet += maxLength;
} else {
tempByte = cipher.doFinal(bytes, offSet, dataLength - offSet);
offSet = dataLength;
}
resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + tempByte.length);
System.arraycopy(tempByte, 0, resultBytes, resultBytes.length - tempByte.length, tempByte.length);
}
if (mode == Cipher.ENCRYPT_MODE) {// 加密
result = Base64.encodeBase64String(resultBytes);
}else{
result = new String(resultBytes, "UTF-8");//解密
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
//测试
public static void main(String[] args) throws Exception{
/**
* TODO 注意: 私钥加密必须公钥解密。公钥加密必须私钥解密。
*/
System.out.println("-------------生成一对秘钥,公钥给接收方,私钥给发送方自己保存 -------------");
RSAUtil.generateKey();
System.out.println("公钥:" + RSAUtil.publicKey);
System.out.println("私钥:" + RSAUtil.privateKey);
System.out.println("------------- 例子1:私钥加密,公钥解密 -------------");
String secret = "战书:明晚十点城外凉亭 Java 要和 PHP 一决高下!";
// 私钥加密
String encryptText = RSAUtil.encryptByPrivateKey(secret, RSAUtil.privateKey, Cipher.ENCRYPT_MODE);
System.out.println("私钥加密的密文:" + encryptText);
// 公钥解密
String result = RSAUtil.encryptByPublicKey(encryptText, RSAUtil.publicKey, Cipher.DECRYPT_MODE);
System.out.println("公钥解密的明文:" + result);
System.out.println();
System.out.println("------------- 例子2:公钥加密,私钥解密 -------------");
// 公钥加密
String movieMsg = "后台开发:周日下午一起去看电影《战狼Ⅱ》,有空不? 前端妹纸:可以呀!";
String cipherText = RSAUtil.encryptByPublicKey(movieMsg, RSAUtil.publicKey, Cipher.ENCRYPT_MODE);
System.out.println("公钥加密的密文:" + cipherText);
// 私钥解密
String text = RSAUtil.encryptByPrivateKey(cipherText, RSAUtil.privateKey, Cipher.DECRYPT_MODE);
System.out.print("私钥解密的明文:" + text);
}
}
注意:加解密需要判断数据长度,否则报错:javax.crypto.IllegalBlockSizeException: Data must not be longer than 53 bytes
运行结果:
OK,信息加密技术讲解到这。