文章内容
实际工作中遇到的加密解密算法和算法之间的区别以及如何选择,包括:BASE64、DES、3DES、AES、RSA、MD5、SHA。
区别与选择:
- 加密算法可逆,用来保证数据安全,散列算法不可逆,用来验证身份。
- 对称加密算法,速度快,适合给大量数据加密,交互双方使用同一个密钥,存在被抓包破解的风险。
- 非对称加密算法使用公钥加密,私钥解密,私钥签名,公钥验签,安全性较高,但速度较慢。非对称加密使用两个密钥,服务端和客户端密匙不一样,私钥放在服务端,安全性高。
- 在实际的操作过程中,通常采用的方式是:采用非对称加密算法管理对称算法的密钥,然后用对称加密算法加密数据,这样就集成了两类加密算法的优点,既实现了加密速度快的优点,又实现了安全方便管理密钥的优点。
一、编码加解密
1、Base64
严格意义来说Base64并不是一种加密/解密算法,而是一种编码的方式。Base64不生成密钥,通过Base64编码后的密文就可以直接“翻译”为明文。在网络上传输数据时,往往要经过多个路由设备,由于不同的设备对字符的处理方式有一些不同,这样那些不可见字符就有可能被处理错误,这是不利于传输的。所以就先把数据先做一个Base64编码,统统变成可见字符,这样出错的可能性就大降低,Base64转换后的字符串理论上将要比原来的长1/3。
import java.util.Base64;
/**
* Base64使用例子
* 加密和解密
*/public class Base64Demo {
public static void main(String[] args) {
// 加密文字
String data = "EasyJava";
// 加密
byte[] encode = Base64.getEncoder().encode(data.getBytes());
String enCodeResult = new String(encode);
System.out.println("加密结果:" + enCodeResult);
// 解密
byte[] decode = Base64.getDecoder().decode(enCodeResult);
String decodeResult = new String(decode);
System.out.println("解密结果:" + decodeResult);
}
}
执行结果:
加密结果:RWFzeUphdmE=
解密结果:EasyJava
二、对称加解密
1、DES
DES是一种基于56位密钥的对称算法,1976年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),随后在国际上广泛流传开来。现在DES已经不是一种安全的加密算法,已被公开破解,现在DES已经被高级加密标准(AES)所代替。新项目不建议选择DES,这种加密方式只存在历史项目中。
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.security.Key;
/**
* DES使用例子
* 加密和解密
*/public class DESDemo {
/**
* 密钥
*/ private static String SECRET_KEY = "12345678";
private static final char[] HEXES = {
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'a', 'b',
'c', 'd', 'e', 'f'
};
/**
* 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();
}
public static void main(String[] args) throws Exception {
// 加密字符串
String src = "EasyJava";
//KEY转换
DESKeySpec desKeySpec = new DESKeySpec(SECRET_KEY.getBytes("utf-8"));
SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
Key convertSecretKey = factory.generateSecret(desKeySpec);
//加密
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, convertSecretKey);
byte[] result = cipher.doFinal(src.getBytes());
System.out.println("加密结果: " + bytes2Hex(result));
//解密
cipher.init(Cipher.DECRYPT_MODE, convertSecretKey);
result = cipher.doFinal(result);
System.out.println("解密结果: " + new String(result));
}
}
执行结果:
加密结果: dcf185f57bb08208feb959b7d4642fcb
解密结果: EasyJava
2、3DES
相当于是对每个数据块应用三次DES加密算法。由于计算机运算能力的增强,原版DES密码的密钥长度变得容易被暴力破解,3DES即是设计用来提供一种相对简单的方法,即通过增加DES的密钥长度来避免类似的攻击,而不是设计一种全新密码算法。
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
/**
* DES3使用例子
* 加密和解密
*/public class DES3Demo {
/**
* 密钥(24位)
*/ private static String SECRET_KEY = "123456781234567812345678";
private static final String KEY_ALGORITHM = "DESede";
private static final String CIPHER_ALGORITHM = "DESede/CBC/PKCS5Padding";
/**
* 3DES 加密
*
* @param key 密钥(24位)
* @param iv 偏移量
* @param data 需要加密的字符串
* @return 返回加密的字符串
*/ public static String encrypt(String key, String iv, String data) {
try {
DESedeKeySpec spec = new DESedeKeySpec(key.getBytes(StandardCharsets.UTF_8));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
Key desKey = keyFactory.generateSecret(spec);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
IvParameterSpec ips = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.ENCRYPT_MODE, desKey, ips);
byte[] bOut = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.encodeBase64String(bOut);
} catch (Exception e) {
e.printStackTrace();
System.out.println("3DES 解密错误");
throw new RuntimeException("3DES 解密错误");
}
}
/**
* 3DES 解密
*
* @param key 密钥(24位)
* @param iv 偏移量
* @param data 需要解密的密文
* @return 返回加密的字符串
*/ public static String decrypt(String key, String iv, String data) {
try {
DESedeKeySpec spec = new DESedeKeySpec(key.getBytes(StandardCharsets.UTF_8));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
Key desKey = keyFactory.generateSecret(spec);
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
IvParameterSpec ips = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.DECRYPT_MODE, desKey, ips);
byte[] bOut = cipher.doFinal(Base64.decodeBase64(data.getBytes(StandardCharsets.UTF_8)));
return new String(bOut, StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
System.out.println("3DES 解密错误");
throw new RuntimeException("3DES 解密错误");
}
}
public static void main(String[] args) {
String data = "EasyJava";
String des3EncodeCBC = encrypt(SECRET_KEY, "53152654", data);
System.out.println("加密结果:" + des3EncodeCBC);
String des3DecodeCBC = decrypt(SECRET_KEY, "53152654", des3EncodeCBC);
System.out.println("解密结果:" + des3DecodeCBC);
}
}
执行结果:
加密结果:5gBjg/fcZ45YrGKm8nxHvQ==
解密结果:EasyJava
3、AES
AES是目前对称加密算法中最流行的算法之一,是DES的替代者,未被破解,性能更优。
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.Base64;
/**
* AES使用例子
* 加密和解密
*/public class AESDemo {
/**
* 密钥(24)
*/ private static String SECRET_KEY = "xxxxxxxxxxxxxxxxxxxxxxxx";
public static void main(String[] args) throws Exception {
// 加密字符串
String src = "EasyJava";
// Key转换
Key key = new SecretKeySpec(SECRET_KEY.getBytes("utf-8"), "AES");
// 加密
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptBytes = cipher.doFinal(src.getBytes("utf-8"));
System.out.println("加密结果:" + Base64.getEncoder().encodeToString(encryptBytes));
// 解密
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decodeBytes = cipher.doFinal(encryptBytes);
System.out.println("解密结果:" + new String(decodeBytes));
}
}
执行结果:
加密结果:ESuCYW2piOpf4Expba6IuA==
解密结果:EasyJava
三、非对称加解密
1、RSA
广泛接受且实现,多用于数据加密和数字签名领域,支持公钥加密私钥解密,私钥加密公钥解密方式。
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.Base64;
/**
* RSA使用例子
* 私钥加密,公钥解密
* 公钥加密,私钥解密
*/public class RSADemo {
private static void encode1(String src, RSAPublicKey rsaPublicKey, RSAPrivateKey rsaPrivateKey) throws Exception {
// 加密
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] encodeResult = cipher.doFinal(src.getBytes("utf-8"));
System.out.println("【私钥加密、公钥解密】加密结果:" + Base64.getEncoder().encodeToString(encodeResult));
// 解密
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded());
keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] decodeResult = cipher.doFinal(encodeResult);
System.out.println("【私钥加密、公钥解密】解密结果:" + new String(decodeResult, "utf-8"));
}
private static void encode2(String src, RSAPublicKey rsaPublicKey, RSAPrivateKey rsaPrivateKey) throws Exception {
// 加密
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encodeResult = cipher.doFinal(src.getBytes("utf-8"));
System.out.println("【公钥加密、私钥解密】加密结果:" + Base64.getEncoder().encodeToString(encodeResult));
// 解密
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decodeResult = cipher.doFinal(encodeResult);
System.out.println("【公钥加密、私钥解密】解密结果:" + new String(decodeResult, "utf-8"));
}
public static void main(String[] args) throws Exception {
// 加密字符串
String src = "EasyJava";
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(512);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 公钥 & 私钥
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
System.out.println("公钥:" + Base64.getEncoder().encodeToString(rsaPublicKey.getEncoded()));
System.out.println("私钥:" + Base64.getEncoder().encodeToString(rsaPrivateKey.getEncoded()));
// 【私钥加密、公钥解密】
encode1(src, rsaPublicKey, rsaPrivateKey);
// 【公钥加密、私钥解密】
encode2(src, rsaPublicKey, rsaPrivateKey);
}
}
执行结果:
公钥:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ68MgEMZD7X+5e9kYwLZfcXeCzlXlB3me/v214mON3ONbDjX++a3b7EqW+rVf49H9AmiY5bWuj335X/18/qyV8CAwEAAQ==
私钥:MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEAnrwyAQxkPtf7l72RjAtl9xd4LOVeUHeZ7+/bXiY43c41sONf75rdvsSpb6tV/j0f0CaJjlta6Pfflf/Xz+rJXwIDAQABAkAnJACwgX22gUtofzYEcksSQpc/es9myBcNMvfEn2gmSO8wYDxTnyctYGC0slOjFXDSuTp5WoK/bRcAOWnDI/UBAiEA0uQJeMGpl5c8Qmbxs5NWJvXOntJE/S/Ka/1UgX/MJXUCIQDAsDf9rUDxzjF+hqY9Mv79STO3PYd5MZN/C3uHCGzVAwIgDf6d9kp7s5iQoiNstKr5U5qKPJXdiCOsvh/QhMtzQ6UCIFkiKwRh3KT+aM7KoqO0r1ejQRUGlWBumngua5nvP8jZAiBcdQ1czDuqBPQrua0DylFeP/3way2pKrQFg4xmNcM8Uw==
【私钥加密、公钥解密】加密结果:KjQIb91tWcau+FAwPF6AX6jw3QzYhXyaQQPG6roNWZ5wT1nMhanX2G1O2ZNPqVS2s3Ar4Cl1ROKYF6JmlEeYBw==
【私钥加密、公钥解密】解密结果:EasyJava
【公钥加密、私钥解密】加密结果:gw3PUe2zrCsZ+SyIgGBPX2ZnzQVnTZal7pvP4TZRGUzQho2IK91YjdbV8ec9Tp9HUHKVHMf96aNu2tRc7ZDMBg==
【公钥加密、私钥解密】解密结果:EasyJava
2、MD5
MD5严格意义上不属于加密算法,属于信息摘要算法。
import java.security.MessageDigest;
/**
* MD5使用例子
* 生成消息摘要
*/public class MD5Demo {
private static final char[] HEXES = {
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'a', 'b',
'c', 'd', 'e', 'f'
};
/**
* 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();
}
public static void main(String[] args) throws Exception {
// 加密字符
String src = "EasyJava";
// 获取摘要
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] md5Result = messageDigest.digest(src.getBytes("utf-8"));
System.out.println("摘要信息:" + bytes2Hex(md5Result));
}
}
执行结果:
摘要信息:0d50d2ea5aece0b7bc10643a3f72159c
3、SHA
SHA家族的五个算法,分别是SHA-1、SHA-224、SHA-256、SHA-384,和 SHA-512,后四者有时并称为SHA-2。SHA-1在许多安全协定中广为使用。 MD5与SHA1都是Hash算法,MD5输出是128位的(转换为16进制则为32个字符),SHA1输出是160位的(转换为16进制则为40个字符),MD5比SHA1快,SHA1比MD5强度高。
import java.security.MessageDigest;
/**
* SHA使用例子
* 生成消息摘要
*/public class SHADemo {
private static final char[] HEXES = {
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'a', 'b',
'c', 'd', 'e', 'f'
};
/**
* 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();
}
public static void main(String[] args) throws Exception {
// 加密字符
String src = "EasyJava";
// 获取摘要
MessageDigest messageDigest = MessageDigest.getInstance("SHA");
messageDigest.update(src.getBytes("utf-8"));
byte[] md5Result = messageDigest.digest();
System.out.println("摘要信息:" + bytes2Hex(md5Result));
}
}
执行结果:
摘要信息:a59bcc9c6c15610a8fc0dc75a1551903f5583a90