面试官:说一下你常用的加密算法
加密算法我们整体可以分为:可逆加密和不可逆加密,可逆加密又可以分为:对称加密和非对称加密。
一、不可逆加密
常见的不可逆加密算法有MD5
,HMAC
,SHA1
、SHA-224
、SHA-256
、SHA-384
,和SHA-512
,其中SHA-224
、SHA-256
、SHA-384
,和SHA-512
我们可以统称为SHA2
加密算法,SHA
加密算法的安全性要比MD5
更高,而SHA2
加密算法比SHA1
的要高。其中SHA
后面的数字表示的是加密后的字符串长度,SHA1
默认会产生一个160
位的信息摘要。
不可逆加密算法最大的特点就是密钥,但是HMAC
是需要密钥的【手动狗头】。
由于这些加密都是不可逆的,因此比较常用的场景就是用户密码加密,其验证过程就是通过比较两个加密后的字符串是否一样来确认身份的。网上也有很多自称是可以破解MD5
密码的网站,其原理也是一样,就是有一个巨大的资源库,存放了许多字符串及对应的MD5
加密后的字符串,通过你输入的MD5
加密串来进行比较,如果过你的密码复杂度比较低,还是有很大机率验证出来的。
1.1 MD5
MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。
MD5
算法有以下特点:
1、压缩性:无论数据长度是多少,计算出来的MD5
值长度相同
2、容易计算性:由原数据容易计算出MD5
值
3、抗修改性:即便修改一个字节,计算出来的MD5
值也会巨大差异
4、抗碰撞性:知道数据和MD5
值,很小概率找到相同MD5
值相同的原数据。
1public static String md5(String text) {
2 MessageDigest messageDigest = null;
3 try {
4 messageDigest = MessageDigest.getInstance("MD5");
5 } catch (NoSuchAlgorithmException e) {
6 e.printStackTrace();
7 }
8 byte[] bytes = messageDigest.digest(text.getBytes());
9 return Hex.encodeHexString(bytes);
10}
1.2 SHA系列
安全散列算法(英语:Secure Hash Algorithm,缩写为SHA)是一个密码散列函数家族,是FIPS所认证的安全散列算法。能计算出一个数字消息所对应到的,长度固定的字符串(又称消息摘要)的算法。且若输入的消息不同,它们对应到不同字符串的机率很高。
2005年8月17日的CRYPTO会议尾声中王小云、姚期智、姚储枫再度发表更有效率的SHA-1攻击法,能在2的63次方个计算复杂度内找到碰撞。
也就是说SHA-1
加密算法有碰撞的可能性,虽然很小。
1public static String sha256(String text) {
2 MessageDigest messageDigest = null;
3 try {
4 messageDigest = MessageDigest.getInstance("SHA-256");
5 } catch (NoSuchAlgorithmException e) {
6 e.printStackTrace();
7 }
8 byte[] bytes = messageDigest.digest(text.getBytes());
9 return Hex.encodeHexString(bytes);
10}
1.3 HMAC系列
HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code)的缩写,由H.Krawezyk,M.Bellare,R.Canetti于1996年提出的一种基于Hash函数和密钥进行消息认证的方法,并于1997年作为RFC2104被公布,并在IPSec和其他网络协议(如SSL)中得以广泛应用,现在已经成为事实上的Internet安全标准。它可以与任何迭代散列函数捆绑使用。
HMAC算法更像是一种加密算法,它引入了密钥,其安全性已经不完全依赖于所使用的Hash算法
1public static String hmacSha256(String text, SecretKeySpec sk) {
2 Mac mac = null;
3 try {
4 mac = Mac.getInstance("HmacSHA256");
5 } catch (NoSuchAlgorithmException e) {
6 e.printStackTrace();
7 }
8 try {
9 mac.init(sk);
10 } catch (InvalidKeyException e) {
11 e.printStackTrace();
12 }
13 byte[] rawHmac = mac.doFinal(text.getBytes());
14 return new String(Base64.encodeBase64(rawHmac));
15}
如果要使用不可逆加密,推荐使用SHA256、SHA384、SHA512以及HMAC-SHA256、HMAC-SHA384、HMAC-SHA512这几种算法。
二、对称加密算法
对称加密算法是应用比较早的算法,在数据加密和解密的时用的都是同一个密钥,这就造成了密钥管理困难的问题。常见的对称加密算法有DES
、3DES
、AES128
、AES192
、AES256
(默认安装的 JDK
尚不支持 AES256
,需要安装对应的 jce
补丁进行升级 jce1.7
,jce1.8
)。其中AES
后面的数字代表的是密钥长度。对称加密算法的安全性相对较低,比较适用的场景就是内网环境中的加解密。
2.1 DES
DES
是对称加密算法领域中的典型算法,其密钥默认长度为56
位。
1// 加密
2public static String encrypt(byte[] dataSource, String password){
3 try {
4 SecureRandom random = new SecureRandom();
5 DESKeySpec desKeySpec = new DESKeySpec(password.getBytes());
6 //创建一个密匙工厂,然后用它把DESKeySpec转换成
7 SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
8 SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);
9 //Cipher对象实际完成加密操作
10 Cipher cipher = Cipher.getInstance("DES");
11 //用密匙初始化Cipher对象
12 cipher.init(Cipher.ENCRYPT_MODE, secretKey, random);
13 //正式执行加密操作
14 return Base64.encodeBase64String(cipher.doFinal(dataSource));
15 } catch (Throwable e) {
16 e.printStackTrace();
17 } return null;
18 }
19// 解密
20public static String decrypt(String src, String password) throws Exception{
21 // DES算法要求有一个可信任的随机数源
22 SecureRandom random = new SecureRandom();
23 // 创建一个DESKeySpec对象
24 DESKeySpec desKeySpec = new DESKeySpec(password.getBytes());
25 // 创建一个密匙工厂
26 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
27 // 将DESKeySpec对象转换成SecretKey对象
28 SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
29 // Cipher对象实际完成解密操作
30 Cipher cipher = Cipher.getInstance("DES");
31 // 用密匙初始化Cipher对象
32 cipher.init(Cipher.DECRYPT_MODE, secretKey, random);
33 // 真正开始解密操作
34 return new String(cipher.doFinal(Base64.decodeBase64(src)));
35}
2.2 3DES
3DES
(即Triple DES)是DES
向AES
过渡的加密算法,它使用3条56位的密钥对数据进行三次加密。是DES
的一个更安全的变形。它以DES
为基本模块,通过组合分组方法设计出分组加密算法。比起最初的DES
,3DES
更为安全。密钥长度默认为168
位,还可以选择128
位。
1public static String encryptThreeDESECB(String src, String key) {
2 try{
3 DESedeKeySpec dks = new DESedeKeySpec(key.getBytes("UTF-8"));
4 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
5 SecretKey securekey = keyFactory.generateSecret(dks);
6
7 Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
8 cipher.init(Cipher.ENCRYPT_MODE, securekey);
9 byte[] b = cipher.doFinal(src.getBytes("UTF-8"));
10
11 String ss = new String(Base64.encodeBase64(b));
12 ss = ss.replaceAll("\\+", "-");
13 ss = ss.replaceAll("/", "_");
14 return ss;
15 } catch(Exception ex){
16 ex.printStackTrace();
17 return src;
18 }
19}
20
21public static String decryptThreeDESECB(String src, String key) {
22 try{
23 src = src.replaceAll("-", "+");
24 src = src.replaceAll("_", "/");
25 byte[] bytesrc = Base64.decodeBase64(src.getBytes("UTF-8"));
26 // --解密的key
27 DESedeKeySpec dks = new DESedeKeySpec(key.getBytes("UTF-8"));
28 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
29 SecretKey securekey = keyFactory.generateSecret(dks);
30
31 // --Chipher对象解密
32 Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
33 cipher.init(Cipher.DECRYPT_MODE, securekey);
34 byte[] retByte = cipher.doFinal(bytesrc);
35
36 return new String(retByte, "UTF-8");
37 } catch(Exception ex){
38 ex.printStackTrace();
39 return src;
40 }
41}
2.3 AES
AES
高级数据加密标准,能够有效抵御已知的针对DES
算法的所有攻击,默认密钥长度为128
位,还可以供选择192
位,256
位。这里顺便提一句这个位指的是bit。
1private static final String defaultCharset = "UTF-8";
2private static final String KEY_AES = "AES";
3private static final String KEY_MD5 = "MD5";
4private static MessageDigest md5Digest;
5static {
6 try {
7 md5Digest = MessageDigest.getInstance(KEY_MD5);
8 } catch (NoSuchAlgorithmException e) {
9
10 }
11}
12/**
13 * 加密
14 */
15public static String encrypt(String data, String key) {
16 return doAES(data, key, Cipher.ENCRYPT_MODE);
17}
18/**
19 * 解密
20 */
21public static String decrypt(String data, String key) {
22 return doAES(data, key, Cipher.DECRYPT_MODE);
23}
24
25
26/**
27 * 加解密
28 */
29private static String doAES(String data, String key, int mode) {
30 try {
31 boolean encrypt = mode == Cipher.ENCRYPT_MODE;
32 byte[] content;
33 if (encrypt) {
34 content = data.getBytes(defaultCharset);
35 } else {
36 content = Base64.decodeBase64(data.getBytes());
37 }
38 SecretKeySpec keySpec = new SecretKeySpec(md5Digest.digest(key.getBytes(defaultCharset))
39 , KEY_AES);
40 Cipher cipher = Cipher.getInstance(KEY_AES);// 创建密码器
41 cipher.init(mode, keySpec);// 初始化
42 byte[] result = cipher.doFinal(content);
43 if (encrypt) {
44 return new String(Base64.encodeBase64(result));
45 } else {
46 return new String(result, defaultCharset);
47 }
48 } catch (Exception e) {
49 }
50 return null;
51}
推荐使用对称加密算法有:AES128
、AES192
、AES256
。
三、非对称加密算法
非对称加密算法有两个密钥,这两个密钥完全不同但又完全匹配。只有使用匹配的一对公钥和私钥,才能完成对明文的加密和解密过程。常见的非对称加密有RSA
、SM2
等。
3.1 RSA
RSA
密钥至少为500位长,一般推荐使用1024位。
1//非对称密钥算法
2public static final String KEY_ALGORITHM = "RSA";
3
4/**
5 * 密钥长度,DH算法的默认密钥长度是1024
6 * 密钥长度必须是64的倍数,在512到65536位之间
7 */
8private static final int KEY_SIZE = 1024;
9//公钥
10private static final String PUBLIC_KEY = "RSAPublicKey";
11//私钥
12private static final String PRIVATE_KEY = "RSAPrivateKey";
13/**
14 * 初始化密钥对
15 *
16 * @return Map 甲方密钥的Map
17 */
18public static Map initKey() throws Exception {
19 //实例化密钥生成器
20 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
21 //初始化密钥生成器
22 keyPairGenerator.initialize(KEY_SIZE);
23 //生成密钥对
24 KeyPair keyPair = keyPairGenerator.generateKeyPair();
25 //甲方公钥
26 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
27 //甲方私钥
28 RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
29 //将密钥存储在map中
30 Map keyMap = new HashMap();
31 keyMap.put(PUBLIC_KEY, publicKey);
32 keyMap.put(PRIVATE_KEY, privateKey);
33 return keyMap;
34}
35/**
36 * 私钥加密
37 *
38 * @param data 待加密数据
39 * @param key 密钥
40 * @return byte[] 加密数据
41 */
42public static byte[] encryptByPrivateKey(byte[] data, byte[] key) throws Exception {
43
44 //取得私钥
45 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
46 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
47 //生成私钥
48 PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
49 //数据加密
50 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
51 cipher.init(Cipher.ENCRYPT_MODE, privateKey);
52 return cipher.doFinal(data);
53}
54
55/**
56 * 公钥加密
57 *
58 * @param data 待加密数据
59 * @param key 密钥
60 * @return byte[] 加密数据
61 */
62public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws Exception {
63
64 //实例化密钥工厂
65 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
66 //初始化公钥
67 //密钥材料转换
68 X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
69 //产生公钥
70 PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
71 //数据加密
72 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
73 cipher.init(Cipher.ENCRYPT_MODE, pubKey);
74 return cipher.doFinal(data);
75}
76
77/**
78 * 私钥解密
79 *
80 * @param data 待解密数据
81 * @param key 密钥
82 * @return byte[] 解密数据
83 */
84public static byte[] decryptByPrivateKey(byte[] data, byte[] key) throws Exception {
85 //取得私钥
86 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
87 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
88 //生成私钥
89 PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
90 //数据解密
91 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
92 cipher.init(Cipher.DECRYPT_MODE, privateKey);
93 return cipher.doFinal(data);
94}
95
96/**
97 * 公钥解密
98 *
99 * @param data 待解密数据
100 * @param key 密钥
101 * @return byte[] 解密数据
102 */
103public static byte[] decryptByPublicKey(byte[] data, byte[] key) throws Exception {
104
105 //实例化密钥工厂
106 KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
107 //初始化公钥
108 //密钥材料转换
109 X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
110 //产生公钥
111 PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
112 //数据解密
113 Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
114 cipher.init(Cipher.DECRYPT_MODE, pubKey);
115 return cipher.doFinal(data);
116}
117
118/**
119 * 取得私钥
120 *
121 * @param keyMap 密钥map
122 * @return byte[] 私钥
123 */
124public static byte[] getPrivateKey(Map keyMap) {
125 Key key = (Key) keyMap.get(PRIVATE_KEY);
126 return key.getEncoded();
127}
128
129/**
130 * 取得公钥
131 *
132 * @param keyMap 密钥map
133 * @return byte[] 公钥
134 */
135public static byte[] getPublicKey(Map keyMap) throws Exception {
136 Key key = (Key) keyMap.get(PUBLIC_KEY);
137 return key.getEncoded();
138}
四、加密盐
加密盐也是比较常听到的一个概念,盐就是一个随机字符串用来和我们的加密串拼接后进行加密。加盐主要是为了提供加密字符串的安全性。假如有一个加盐后的加密串,黑客通过一定手段这个加密串,他拿到的明文,并不是我们加密前的字符串,而是加密前的字符串和盐组合的字符串,这样相对来说又增加了字符串的安全性。
文中的一些算法来源于网络,可直接复制使用。
比较推荐的几个加密算法有:
不可逆加密:
SHA256
、SHA384
、SHA512
以及HMAC-SHA256
、HMAC-SHA384
、HMAC-SHA512
对称加密算法:
AES
、3DES
非对称加密算法:
RSA
< END >