加密与解密
一、 对称加密
采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。
常见对称加密算法
- DES : Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。
- AES : Advanced Encryption Standard, 高级加密标准 .在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的 DES,已经被多方分析且广为全世界所使用。
特点
- 加密速度快, 可以加密大文件
- 密文可逆, 一旦密钥文件泄漏, 就会导致数据暴露
- 加密后编码表找不到对应字符, 出现乱码
- 一般结合 Base64 使用
2. DES 加密
import com.sun.org.apache.xml.internal.security.utils.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
/**
* @author duniqb <duniqb@qq.com>
* @version v1.0.0
* @date 2020/5/28 22:37
* @since 1.8
*/
public class DesAesDemo {
public static void main(String[] args) throws Exception {
encryptDes("读你千遍", "12345678", "DES", "DES");
decryptDes("xtL1MINKt046sqEjk0xNIw==", "12345678", "DES", "DES");
}
/**
* 加密
*
* @param input 原文
* @param key 定义 key,必须 8 字节以上
* @param transformation 算法
* @param algorithm 加密类型
*/
private static void encryptDes(String input, String key, String transformation, String algorithm) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
// 加密规则
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
// 初始化
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] bytes = cipher.doFinal(input.getBytes());
// Base64 转码
String encode = Base64.encode(bytes);
System.out.println("加密:" + encode);
}
/**
* 解密
*
* @param encryptDES 密文
* @param key 定义 key
* @param transformation 算法
* @param algorithm 加密类型
*/
private static void decryptDes(String encryptDES, String key, String transformation, String algorithm) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
// 解密规则
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
// 初始化
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] bytes = cipher.doFinal(Base64.decode(encryptDES));
System.out.println("解密:" + new String(bytes));
}
}
输出:
加密:xtL1MINKt046sqEjk0xNIw==
解密:读你千遍
3. AES 加密
AES 加密解密和 DES 加密解密代码一样,只需要修改加密算法就行
public class AesDemo {
public static void main(String[] args) throws Exception {
encryptDes("读你千遍", "1234567812345678", "AES", "AES");
decryptDes("epIPp6nMNmkJPt01Lmtlrg==", "1234567812345678", "AES", "AES");
}
/**
* 加密
*
* @param input 原文
* @param key 定义 key,必须 16 字节以上
* @param transformation 算法
* @param algorithm 加密类型
*/
private static void encryptDes(String input, String key, String transformation, String algorithm) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
// 加密规则
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
// 初始化
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] bytes = cipher.doFinal(input.getBytes());
// Base64 转码
String encode = Base64.encode(bytes);
System.out.println("加密:" + encode);
}
/**
* 解密
*
* @param encryptDES 密文
* @param key 定义 key
* @param transformation 算法
* @param algorithm 加密类型
*/
private static void decryptDes(String encryptDES, String key, String transformation, String algorithm) throws Exception {
Cipher cipher = Cipher.getInstance(transformation);
// 解密规则
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
// 初始化
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] bytes = cipher.doFinal(Base64.decode(encryptDES));
System.out.println("解密:" + new String(bytes));
}
}
输出
加密:epIPp6nMNmkJPt01Lmtlrg==
解密:读你千遍
4. Base64
-
Base64 是网络上最常见的用于传输 8Bit字节码的可读性编码算法之一
-
可读性编码算法不是为了保护数据的安全性,而是为了可读性。可读性编码不改变信息内容,只改变信息内容的表现形式
-
所谓 Base64,即是说在编码过程中使用了64种字符:大写A到Z、小写a到z、数字0到9、“+”和“/”
-
Base58是Bitcoin(比特币)中使用的一种编码方式,主要用于产生Bitcoin的钱包地址
相比Base64,Base58不使用数字"0",字母大写"O",字母大写"I",和字母小写"i",以及"+"和"/"符号
Base64 算法原理
Base64 是 3个字节为一组,一个字节 8 位,一共就是24位 ,然后,把3个字节转成4组,每组6位,
3 * 8 = 4 * 6 = 24 ,每组6位,缺少的2位,会在高位进行补0 ,这样做的好处在于 ,base取的是后面6位,去掉高2位 ,那么base64的取值就可以控制在 0-63 位了,所以就叫base64,111 111 = 32 + 16 + 8 + 4 + 2 + 1 =
Base64 构成原则
- 小写 a - z = 26个字母
- 大写 A - Z = 26个字母
- 数字 0 - 9 = 10 个数字
- / = 2个符号
Base64 有个 = 号,但是在映射表里面没有发现 = 号 ,等号非常特殊,因为base64是三个字节一组 ,如果位数不够的时候,会使用等号来补齐
二、消息摘要
消息摘要(Message Digest)又称为数字摘要(Digital Digest),它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生。使用数字摘要生成的值是不可以篡改的,为了保证文件或者值的安全
特点
无论输入的消息有多长,计算出来的消息摘要的长度总是固定的。例如应用MD5算法摘要的消息有128个比特位,用SHA-1算法摘要的消息最终有160比特位的输出
只要输入的消息不同,对其进行摘要以后产生的摘要消息也必不相同;但相同的输入必会产生相同的输出
消息摘要是单向、不可逆的
常见算法 :
- MD5
- SHA1
- SHA256
- SHA512
public static void main(String[] args) throws Exception {
// 原文
String input = "aa";
// 算法
String algorithm = "MD5";
// 消息摘要对象
MessageDigest digest = MessageDigest.getInstance(algorithm);
byte[] digest1 = digest.digest(input.getBytes());
System.out.println(Base64.encode(digest1));
}
三、非对称加密
1. 简介
-
非对称加密算法又称
现代加密算法
。 -
非对称加密是计算机通信安全的基石,保证了加密数据
不会被破解
。 -
与对称加密算法不同,非对称加密算法需要两个密钥:
公开密钥(publickey)
和私有密(privatekey)
-
公开密钥和私有密钥是
一对
-
如果用
公开密钥
对数据进行加密
,只有用对应的私有密钥
才能解密
。 -
如果用
私有密钥
对数据进行加密
,只有用对应的公开密钥
才能解密
。 -
因为加密和解密使用的是两个
不同
的密钥,所以这种算法叫作非对称加密算法
。 -
示例
- 首先生成密钥对, 公钥为(5,14), 私钥为(11,14)
- 现在A希望将原文2发送给B
- A使用公钥加密数据. 2的5次方mod 14 = 4 , 将密文4发送给B
- B使用私钥解密数据. 4的11次方mod14 = 2, 得到原文2
-
特点
- 加密和解密使用不同的密钥
- 如果使用私钥加密, 只能使用公钥解密
- 如果使用公钥加密, 只能使用私钥解密
- 处理数据的速度较慢, 因为安全级别高
-
常见算法
- RSA
- ECC
2. 生成私钥和公钥并用私钥加密原文
public class RsaDemo {
public static void main(String[] args) throws Exception {
String input = "读你千遍";
// 算法
String algorithm = "RSA";
// 生成密钥对
KeyPair keyPair = getPrivatePublicKey(algorithm);
// 使用私钥对原文加密
byte[] bytes = privateKeyEncrypt(input, algorithm, keyPair.getPrivate());
// 使用私钥对原文解密,使用私钥加密必须用公钥解密
privateKeyDecrypt(bytes, algorithm, keyPair.getPublic());
}
/**
* 使用私钥对原文加密
*
* @param input
* @param algorithm
* @param privateKey
*/
private static byte[] privateKeyEncrypt(String input, String algorithm, PrivateKey privateKey) throws Exception {
// 创建加密对象
Cipher cipher = Cipher.getInstance(algorithm);
// 加密初始化:加密模式,想使用私钥进行加密
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
// 使用私钥对原文加密
byte[] bytes = cipher.doFinal(input.getBytes());
System.out.println("加密:\n" + Base64.encode(bytes));
return bytes;
}
/**
* 使用私钥对原文解密
*
* @param input
* @param algorithm
* @param publicKey 使用私钥加密必须用公钥解密
*/
private static void privateKeyDecrypt(byte[] input, String algorithm, PublicKey publicKey) throws Exception {
// 创建加密对象
Cipher cipher = Cipher.getInstance(algorithm);
// 解密初始化:解密模式,想使用公钥进行解密
cipher.init(Cipher.DECRYPT_MODE, publicKey);
// 使用私钥对原文加密
byte[] bytes = cipher.doFinal(input);
System.out.println("解密:\n" + new String(bytes));
}
/**
* 生成密钥对
*
* @param algorithm
* @return
*/
private static KeyPair getPrivatePublicKey(String algorithm) throws Exception {
// 创建密钥对
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
byte[] privateKeyEncoded = privateKey.getEncoded();
byte[] publicKeyEncoded = publicKey.getEncoded();
System.out.println("私钥:\n" + Base64.encode(privateKeyEncoded));
System.out.println("公钥:\n" + Base64.encode(publicKeyEncoded));
return keyPair;
}
}
输出:
私钥:
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCrNYmzvDU5dCEe7eMHBffz25EP
jxQSei7f6E72sSeA+jNI/ATM0H1+vV+mqkg/j+8KZw56y/xMq7lDSpyM7qzP7p93C0dMw1oCVcA3
ZJC6NUJK2DvlbMTXwM7cK0NU2jUSAHYggJZUQBtE/QPKGSacSqj4oV9yz/AZuqfSd3AowGhijN14
rd6AVwASaDXRseXWUf0MfkflfcMi9YIOZs/0jYh0P1XsusiiAY55ymK7p/2hp6f8A5G1JHKMelp0
Gv0Gu0+yvBvoFMJYmHsMM37/64o4pkk4uV9YJR739jAYxYknkzszhHycZmJe32f6l7yrZL8hHnmx
PVbWerH/YPU1AgMBAAECggEAWWbVhyNqb5vyh6IVAN/fAFnRP9yo0JU40PcP9MXBKRp9mUjwgLsG
LR5aSj91Bqv0WOxDpV/LWYX6cecOJJGlb9bdMl6TVsRTxhBdFQ/Qe0vAWDDd5gmMKFWoL1PwDuj6
+XGAPayn00q+cqHkTq4feVGLNnmdy+hFKkpLxB1WVhB4QNpo1VfmIxq/2A16J7kjO+bSscyXHXZD
9sY3atzjJNSi5Ucv9snZGjCbIy6dH3NSfH/gb0WEJ2UsHUpktP34PlhetTaTF5EGqoGSKHx8j5no
OZinTJl81mdojcdn9qwhj61AXBi73pKvATR5KSwwtrgtqoHrlX1bm26zS2lWAQKBgQDVskleAqhj
KkxRC4i6ddz5gpc+KZmzZarpw463Q/6WUu/9v8CmG2xnkNBaQBc4ghMARJU3n4qhQC0XaxWO2T3d
zKk6ijARRhNFhCD4lvMRu1eMnOk9L20TlLTJeZisU1AZ9QyHfHr366YMAGBrk4Tn067wHrKdjlS3
/u2LxKnj9QKBgQDNGhQtnElZcQ4OzVKTirmoJDKQny8juMzec6Cs4+YMWJd8Dwn+31dTpZBgd5Dm
0xR1hI0/darWxtqDu0PlU+XlGbhWfy1o+glH11KYc1EdmWOnDAPlcTJ+APUINeD+wFOLr0+ZkVtb
IjKNyTe8//8GEqpUn0q8OopMGdtYg01EQQKBgQCwSv7/0Y19Eb4X9j38JZQBO1zuaX9lYnBXnX81
baCv/wgB/Jk/78vEvNFGQy1k7toiFHJNiw7g7ILGmQ8V496IBnlt0+bHuVnYtaJFsrIgZL9bP2+j
jGaeCsYCmI/9SlYAtAp8E5OWyA8aQ41H8lFot1fQvGJ6XJtDl8n8zmehqQKBgQChxR8DNpDX/gbT
f/jQrItw5lQHo7Z/m2PjVRn3I62wMTQSQp5kq+3p6N1uaQAeP4CUjF8IHZwI2UFCw7Jyr/DU+ate
deLwOL6XLmtUGShyiyiBekWcc+MaZVDfD/gNls6Oj1DeVDrVL6zmDj5rURLpzFESe61rt1SbX42V
S8LmwQKBgD+F43ltHB2ODd53XYwesYV7NWxIybTQGwCTJWHvcE4sZhjAHtVdAH1h0U5ZylaYvBAX
jp3mXoenl4SKI8m9eXQfzTTScRaOrwQIgL1VRcgFi79ky9azwh0sDlcYxP1Oilvh/qymuOqFDn9X
XriRSWa2/xsazMiov31cnGybHV7j
公钥:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqzWJs7w1OXQhHu3jBwX389uRD48UEnou
3+hO9rEngPozSPwEzNB9fr1fpqpIP4/vCmcOesv8TKu5Q0qcjO6sz+6fdwtHTMNaAlXAN2SQujVC
Stg75WzE18DO3CtDVNo1EgB2IICWVEAbRP0DyhkmnEqo+KFfcs/wGbqn0ndwKMBoYozdeK3egFcA
Emg10bHl1lH9DH5H5X3DIvWCDmbP9I2IdD9V7LrIogGOecpiu6f9oaen/AORtSRyjHpadBr9BrtP
srwb6BTCWJh7DDN+/+uKOKZJOLlfWCUe9/YwGMWJJ5M7M4R8nGZiXt9n+pe8q2S/IR55sT1W1nqx
/2D1NQIDAQAB
加密:
J+KehAYvqcyTUxK2Gse+6ADx30hugLVL+KN3QTNlroFAgh9Au0tGeVchmi84+yNjNTB35GWk7gGl
lfcKKdieJy8TO/J9XCmX/en8/n8rEbxTXN8p9IFfvqjNPhRt2fuPzLzp9F9kTqKMtuxWajSN2WWA
sBT8xsPmJQZ0Ri8QX6jTG+j/IgpbCdpvpBW8nXOjvamdLZMlY4JvWnEtRhJ0053pVok9D0dEQSm5
izX3aIx1gOVoAyIU3iVjbJUA5+76czhHj1oqxTvJBy9OZ8HnKBNkvQwWEzfKROGNwliM+zIcYewN
tWIFAYxry2B0rn70s7twzbUvIageEy7QkbigdQ==
解密:
读你千遍
三、数字签名
数字签名(又称公钥数字签名)是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。它是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术来实现的,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。数字签名是非对称密钥加密技术与数字摘要技术的应用。
1. 基本原理
张三有俩好哥们A、B。由于工作原因,张三和 AB 写邮件的时候为了安全都需要加密。于是张三想到了数字签名:
- 加密采用非对称加密,张三有三把钥匙,两把公钥,送给朋友。一把私钥留给自己。
- A或者B写邮件给张三:A 先用公钥对邮件加密,然后张三收到邮件之后使用私钥解密。
- 张三写邮件给 A 或者 B:
- 张三写完邮件,先用 hash 函数生成邮件的摘要,附着在文章上面,这就完成了数字签名,然后张三再使用私钥加密。就可以把邮件发出去了。
- A 或者是 B 收到邮件之后,先把数字签名取下来,然后使用自己的公钥解密即可。这时候取下来的数字签名中的摘要若和张三的一致,那就认为是张三发来的,再对信件本身使用Hash函数,将得到的结果,与上一步得到的摘要进行对比。如果两者一致,就证明这封信未被修改过。
2. 数字证书
对签名进行验证时,需要用到公钥。如果公钥是伪造的,那我们无法验证数字签名了,也就根本不可能从数字签名确定对方的合法性了。这就需要证书。