面試官:說(shuō)一下你常用的加密算法
加密算法我們整體可以分為:可逆加密和不可逆加密,可逆加密又可以分為:對(duì)稱(chēng)加密和非對(duì)稱(chēng)加密。
一、不可逆加密
常見(jiàn)的不可逆加密算法有MD5,HMAC,SHA1、SHA-224、SHA-256、SHA-384,和SHA-512,其中SHA-224、SHA-256、SHA-384,和SHA-512我們可以統(tǒng)稱(chēng)為SHA2加密算法,SHA加密算法的安全性要比MD5更高,而SHA2加密算法比SHA1的要高。其中SHA后面的數(shù)字表示的是加密后的字符串長(zhǎng)度,SHA1默認(rèn)會(huì)產(chǎn)生一個(gè)160位的信息摘要。
不可逆加密算法最大的特點(diǎn)就是密鑰,但是HMAC是需要密鑰的【手動(dòng)狗頭】。
由于這些加密都是不可逆的,因此比較常用的場(chǎng)景就是用戶(hù)密碼加密,其驗(yàn)證過(guò)程就是通過(guò)比較兩個(gè)加密后的字符串是否一樣來(lái)確認(rèn)身份的。網(wǎng)上也有很多自稱(chēng)是可以破解MD5密碼的網(wǎng)站,其原理也是一樣,就是有一個(gè)巨大的資源庫(kù),存放了許多字符串及對(duì)應(yīng)的MD5加密后的字符串,通過(guò)你輸入的MD5加密串來(lái)進(jìn)行比較,如果過(guò)你的密碼復(fù)雜度比較低,還是有很大機(jī)率驗(yàn)證出來(lái)的。
1.1 MD5
MD5信息摘要算法(英語(yǔ):MD5 Message-Digest Algorithm),一種被廣泛使用的密碼散列函數(shù),可以產(chǎn)生出一個(gè)128位(16字節(jié))的散列值(hash value),用于確保信息傳輸完整一致。
MD5算法有以下特點(diǎn):
1、壓縮性:無(wú)論數(shù)據(jù)長(zhǎng)度是多少,計(jì)算出來(lái)的MD5值長(zhǎng)度相同
2、容易計(jì)算性:由原數(shù)據(jù)容易計(jì)算出MD5值
3、抗修改性:即便修改一個(gè)字節(jié),計(jì)算出來(lái)的MD5值也會(huì)巨大差異
4、抗碰撞性:知道數(shù)據(jù)和MD5值,很小概率找到相同MD5值相同的原數(shù)據(jù)。
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系列
安全散列算法(英語(yǔ):Secure Hash Algorithm,縮寫(xiě)為SHA)是一個(gè)密碼散列函數(shù)家族,是FIPS所認(rèn)證的安全散列算法。能計(jì)算出一個(gè)數(shù)字消息所對(duì)應(yīng)到的,長(zhǎng)度固定的字符串(又稱(chēng)消息摘要)的算法。且若輸入的消息不同,它們對(duì)應(yīng)到不同字符串的機(jī)率很高。
2005年8月17日的CRYPTO會(huì)議尾聲中王小云、姚期智、姚儲(chǔ)楓再度發(fā)表更有效率的SHA-1攻擊法,能在2的63次方個(gè)計(jì)算復(fù)雜度內(nèi)找到碰撞。
也就是說(shuō)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是密鑰相關(guān)的哈希運(yùn)算消息認(rèn)證碼(Hash-based Message Authentication ?Code)的縮寫(xiě),由H.Krawezyk,M.Bellare,R.Canetti于1996年提出的一種基于Hash函數(shù)和密鑰進(jìn)行消息認(rèn)證的方法,并于1997年作為RFC2104被公布,并在IPSec和其他網(wǎng)絡(luò)協(xié)議(如SSL)中得以廣泛應(yīng)用,現(xiàn)在已經(jīng)成為事實(shí)上的Internet安全標(biāo)準(zhǔn)。它可以與任何迭代散列函數(shù)捆綁使用。
HMAC算法更像是一種加密算法,它引入了密鑰,其安全性已經(jīng)不完全依賴(lài)于所使用的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這幾種算法。
二、對(duì)稱(chēng)加密算法
對(duì)稱(chēng)加密算法是應(yīng)用比較早的算法,在數(shù)據(jù)加密和解密的時(shí)用的都是同一個(gè)密鑰,這就造成了密鑰管理困難的問(wèn)題。常見(jiàn)的對(duì)稱(chēng)加密算法有DES、3DES、AES128、AES192、AES256 (默認(rèn)安裝的 JDK 尚不支持 AES256,需要安裝對(duì)應(yīng)的 jce 補(bǔ)丁進(jìn)行升級(jí) jce1.7,jce1.8)。其中AES后面的數(shù)字代表的是密鑰長(zhǎng)度。對(duì)稱(chēng)加密算法的安全性相對(duì)較低,比較適用的場(chǎng)景就是內(nèi)網(wǎng)環(huán)境中的加解密。
2.1 DES
DES是對(duì)稱(chēng)加密算法領(lǐng)域中的典型算法,其密鑰默認(rèn)長(zhǎng)度為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?????????//創(chuàng)建一個(gè)密匙工廠,然后用它把DESKeySpec轉(zhuǎn)換成?
7?????????SecretKeyFactory?secretKeyFactory?=?SecretKeyFactory.getInstance("DES");?
8?????????SecretKey?secretKey?=?secretKeyFactory.generateSecret(desKeySpec);?
9?????????//Cipher對(duì)象實(shí)際完成加密操作?
10?????????Cipher?cipher?=?Cipher.getInstance("DES");?
11?????????//用密匙初始化Cipher對(duì)象?
12?????????cipher.init(Cipher.ENCRYPT_MODE,?secretKey,?random);?
13?????????//正式執(zhí)行加密操作?
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算法要求有一個(gè)可信任的隨機(jī)數(shù)源?
22????SecureRandom?random?=?new?SecureRandom();?
23????//?創(chuàng)建一個(gè)DESKeySpec對(duì)象?
24????DESKeySpec?desKeySpec?=?new?DESKeySpec(password.getBytes());?
25????//?創(chuàng)建一個(gè)密匙工廠?
26????SecretKeyFactory?keyFactory?=?SecretKeyFactory.getInstance("DES");?
27????//?將DESKeySpec對(duì)象轉(zhuǎn)換成SecretKey對(duì)象?
28????SecretKey?secretKey?=?keyFactory.generateSecret(desKeySpec);?
29????//?Cipher對(duì)象實(shí)際完成解密操作?
30????Cipher?cipher?=?Cipher.getInstance("DES");?
31????//?用密匙初始化Cipher對(duì)象?
32????cipher.init(Cipher.DECRYPT_MODE,?secretKey,?random);?
33????//?真正開(kāi)始解密操作?
34????return?new?String(cipher.doFinal(Base64.decodeBase64(src)));?
35}
2.2 3DES
3DES(即Triple DES)是DES向AES過(guò)渡的加密算法,它使用3條56位的密鑰對(duì)數(shù)據(jù)進(jìn)行三次加密。是DES的一個(gè)更安全的變形。它以DES為基本模塊,通過(guò)組合分組方法設(shè)計(jì)出分組加密算法。比起最初的DES,3DES更為安全。密鑰長(zhǎng)度默認(rèn)為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對(duì)象解密
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 高級(jí)數(shù)據(jù)加密標(biāo)準(zhǔn),能夠有效抵御已知的針對(duì)DES算法的所有攻擊,默認(rèn)密鑰長(zhǎng)度為128位,還可以供選擇192位,256位。這里順便提一句這個(gè)位指的是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);//?創(chuàng)建密碼器
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}
推薦使用對(duì)稱(chēng)加密算法有:AES128、AES192、AES256。
三、非對(duì)稱(chēng)加密算法
非對(duì)稱(chēng)加密算法有兩個(gè)密鑰,這兩個(gè)密鑰完全不同但又完全匹配。只有使用匹配的一對(duì)公鑰和私鑰,才能完成對(duì)明文的加密和解密過(guò)程。常見(jiàn)的非對(duì)稱(chēng)加密有RSA、SM2等。
3.1 RSA
RSA密鑰至少為500位長(zhǎng),一般推薦使用1024位。
1//非對(duì)稱(chēng)密鑰算法
2public?static?final?String?KEY_ALGORITHM?=?"RSA";
3
4/**
5??*?密鑰長(zhǎng)度,DH算法的默認(rèn)密鑰長(zhǎng)度是1024
6??*?密鑰長(zhǎng)度必須是64的倍數(shù),在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??*?初始化密鑰對(duì)
15??*
16??*?@return?Map?甲方密鑰的Map
17??*/
18public?static?Map?initKey()?throws?Exception? {
19????//實(shí)例化密鑰生成器
20????KeyPairGenerator?keyPairGenerator?=?KeyPairGenerator.getInstance(KEY_ALGORITHM);
21????//初始化密鑰生成器
22????keyPairGenerator.initialize(KEY_SIZE);
23????//生成密鑰對(duì)
24????KeyPair?keyPair?=?keyPairGenerator.generateKeyPair();
25????//甲方公鑰
26????RSAPublicKey?publicKey?=?(RSAPublicKey)?keyPair.getPublic();
27????//甲方私鑰
28????RSAPrivateKey?privateKey?=?(RSAPrivateKey)?keyPair.getPrivate();
29????//將密鑰存儲(chǔ)在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?待加密數(shù)據(jù)
39??*?@param?key??密鑰
40??*?@return?byte[]?加密數(shù)據(jù)
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????//數(shù)據(jù)加密
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?待加密數(shù)據(jù)
59??*?@param?key??密鑰
60??*?@return?byte[]?加密數(shù)據(jù)
61??*/
62public?static?byte[]?encryptByPublicKey(byte[]?data,?byte[]?key)?throws?Exception?{
63
64????//實(shí)例化密鑰工廠
65????KeyFactory?keyFactory?=?KeyFactory.getInstance(KEY_ALGORITHM);
66????//初始化公鑰
67????//密鑰材料轉(zhuǎn)換
68????X509EncodedKeySpec?x509KeySpec?=?new?X509EncodedKeySpec(key);
69????//產(chǎn)生公鑰
70????PublicKey?pubKey?=?keyFactory.generatePublic(x509KeySpec);
71????//數(shù)據(jù)加密
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?待解密數(shù)據(jù)
81??*?@param?key??密鑰
82??*?@return?byte[]?解密數(shù)據(jù)
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????//數(shù)據(jù)解密
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?待解密數(shù)據(jù)
100??*?@param?key??密鑰
101??*?@return?byte[]?解密數(shù)據(jù)
102??*/
103public?static?byte[]?decryptByPublicKey(byte[]?data,?byte[]?key)?throws?Exception?{
104
105????//實(shí)例化密鑰工廠
106????KeyFactory?keyFactory?=?KeyFactory.getInstance(KEY_ALGORITHM);
107????//初始化公鑰
108????//密鑰材料轉(zhuǎn)換
109????X509EncodedKeySpec?x509KeySpec?=?new?X509EncodedKeySpec(key);
110????//產(chǎn)生公鑰
111????PublicKey?pubKey?=?keyFactory.generatePublic(x509KeySpec);
112????//數(shù)據(jù)解密
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}
四、加密鹽
加密鹽也是比較常聽(tīng)到的一個(gè)概念,鹽就是一個(gè)隨機(jī)字符串用來(lái)和我們的加密串拼接后進(jìn)行加密。加鹽主要是為了提供加密字符串的安全性。假如有一個(gè)加鹽后的加密串,黑客通過(guò)一定手段這個(gè)加密串,他拿到的明文,并不是我們加密前的字符串,而是加密前的字符串和鹽組合的字符串,這樣相對(duì)來(lái)說(shuō)又增加了字符串的安全性。
文中的一些算法來(lái)源于網(wǎng)絡(luò),可直接復(fù)制使用。
比較推薦的幾個(gè)加密算法有:
不可逆加密:
SHA256、SHA384、SHA512以及HMAC-SHA256、HMAC-SHA384、HMAC-SHA512對(duì)稱(chēng)加密算法:
AES、3DES非對(duì)稱(chēng)加密算法:
RSA
< END >
