加密演算法我們整體可以分為:可逆加密和不可逆加密,可逆加密又可以分為:對稱加密和非對稱加密。
一、不可逆加密
常見的不可逆加密演算法有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
值相同的原資料。
public static String md5(String text) {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
byte[] bytes = messageDigest.digest(text.getBytes());
return Hex.encodeHexString(bytes);
}
1.2 SHA系列
安全雜湊演算法(英語:Secure Hash Algorithm,縮寫為SHA)是一個密碼雜湊函式家族,是FIPS所認證的安全雜湊演算法。能計算出一個數字訊息所對應到的,長度固定的字串(又稱訊息摘要)的演算法。且若輸入的訊息不同,它們對應到不同字串的機率很高。
2005年8月17日的CRYPTO會議尾聲中王小云、姚期智、姚儲楓再度發表更有效率的SHA-1攻擊法,能在2的63次方個計算複雜度內找到碰撞。
也就是說SHA-1
加密演算法有碰撞的可能性,雖然很小。
public static String sha256(String text) {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
byte[] bytes = messageDigest.digest(text.getBytes());
return Hex.encodeHexString(bytes);
}
1.3 HMAC系列
HMAC是金鑰相關的雜湊運算訊息認證碼(Hash-based Message Authentication Code)的縮寫,由H.Krawezyk,M.Bellare,R.Canetti於1996年提出的一種基於Hash函式和金鑰進行訊息認證的方法,並於1997年作為RFC2104被公佈,並在IPSec和其他網路協議(如SSL)中得以廣泛應用,現在已經成為事實上的Internet安全標準。它可以與任何迭代雜湊函式捆綁使用。
HMAC演算法更像是一種加密演算法,它引入了金鑰,其安全性已經不完全依賴於所使用的Hash演算法
public static String hmacSha256(String text, SecretKeySpec sk) {
Mac mac = null;
try {
mac = Mac.getInstance("HmacSHA256");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
try {
mac.init(sk);
} catch (InvalidKeyException e) {
e.printStackTrace();
}
byte[] rawHmac = mac.doFinal(text.getBytes());
return new String(Base64.encodeBase64(rawHmac));
}
如果要使用不可逆加密,推薦使用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
位。
// 加密
public static String encrypt(byte[] dataSource, String password){
try {
SecureRandom random = new SecureRandom();
DESKeySpec desKeySpec = new DESKeySpec(password.getBytes());
//建立一個密匙工廠,然後用它把DESKeySpec轉換成
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);
//Cipher物件實際完成加密操作
Cipher cipher = Cipher.getInstance("DES");
//用密匙初始化Cipher物件
cipher.init(Cipher.ENCRYPT_MODE, secretKey, random);
//正式執行加密操作
return Base64.encodeBase64String(cipher.doFinal(dataSource));
} catch (Throwable e) {
e.printStackTrace();
} return null;
}
// 解密
public static String decrypt(String src, String password) throws Exception{
// DES演算法要求有一個可信任的隨機數源
SecureRandom random = new SecureRandom();
// 建立一個DESKeySpec物件
DESKeySpec desKeySpec = new DESKeySpec(password.getBytes());
// 建立一個密匙工廠
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
// 將DESKeySpec物件轉換成SecretKey物件
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
// Cipher物件實際完成解密操作
Cipher cipher = Cipher.getInstance("DES");
// 用密匙初始化Cipher物件
cipher.init(Cipher.DECRYPT_MODE, secretKey, random);
// 真正開始解密操作
return new String(cipher.doFinal(Base64.decodeBase64(src)));
}
2.2 3DES
3DES
(即Triple DES)是DES
向AES
過渡的加密演算法,它使用3條56位的金鑰對資料進行三次加密。是DES
的一個更安全的變形。它以DES
為基本模組,通過組合分組方法設計出分組加密演算法。比起最初的DES
,3DES
更為安全。金鑰長度預設為168
位,還可以選擇128
位。
public static String encryptThreeDESECB(String src, String key) {
try{
DESedeKeySpec dks = new DESedeKeySpec(key.getBytes("UTF-8"));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
SecretKey securekey = keyFactory.generateSecret(dks);
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, securekey);
byte[] b = cipher.doFinal(src.getBytes("UTF-8"));
String ss = new String(Base64.encodeBase64(b));
ss = ss.replaceAll("\\+", "-");
ss = ss.replaceAll("/", "_");
return ss;
} catch(Exception ex){
ex.printStackTrace();
return src;
}
}
public static String decryptThreeDESECB(String src, String key) {
try{
src = src.replaceAll("-", "+");
src = src.replaceAll("_", "/");
byte[] bytesrc = Base64.decodeBase64(src.getBytes("UTF-8"));
// --解密的key
DESedeKeySpec dks = new DESedeKeySpec(key.getBytes("UTF-8"));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
SecretKey securekey = keyFactory.generateSecret(dks);
// --Chipher物件解密
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, securekey);
byte[] retByte = cipher.doFinal(bytesrc);
return new String(retByte, "UTF-8");
} catch(Exception ex){
ex.printStackTrace();
return src;
}
}
2.3 AES
AES
高階資料加密標準,能夠有效抵禦已知的針對DES
演算法的所有攻擊,預設金鑰長度為128
位,還可以供選擇192
位,256
位。這裡順便提一句這個位指的是bit。
private static final String defaultCharset = "UTF-8";
private static final String KEY_AES = "AES";
private static final String KEY_MD5 = "MD5";
private static MessageDigest md5Digest;
static {
try {
md5Digest = MessageDigest.getInstance(KEY_MD5);
} catch (NoSuchAlgorithmException e) {
}
}
/**
* 加密
*/
public static String encrypt(String data, String key) {
return doAES(data, key, Cipher.ENCRYPT_MODE);
}
/**
* 解密
*/
public static String decrypt(String data, String key) {
return doAES(data, key, Cipher.DECRYPT_MODE);
}
/**
* 加解密
*/
private static String doAES(String data, String key, int mode) {
try {
boolean encrypt = mode == Cipher.ENCRYPT_MODE;
byte[] content;
if (encrypt) {
content = data.getBytes(defaultCharset);
} else {
content = Base64.decodeBase64(data.getBytes());
}
SecretKeySpec keySpec = new SecretKeySpec(md5Digest.digest(key.getBytes(defaultCharset))
, KEY_AES);
Cipher cipher = Cipher.getInstance(KEY_AES);// 建立密碼器
cipher.init(mode, keySpec);// 初始化
byte[] result = cipher.doFinal(content);
if (encrypt) {
return new String(Base64.encodeBase64(result));
} else {
return new String(result, defaultCharset);
}
} catch (Exception e) {
}
return null;
}
推薦使用對稱加密演算法有:AES128
、AES192
、AES256
。
三、非對稱加密演算法
非對稱加密演算法有兩個金鑰,這兩個金鑰完全不同但又完全匹配。只有使用匹配的一對公鑰和私鑰,才能完成對明文的加密和解密過程。常見的非對稱加密有RSA
、SM2
等。
3.1 RSA
RSA
金鑰至少為500位長,一般推薦使用1024位。
//非對稱金鑰演算法
public static final String KEY_ALGORITHM = "RSA";
/**
* 金鑰長度,DH演算法的預設金鑰長度是1024
* 金鑰長度必須是64的倍數,在512到65536位之間
*/
private static final int KEY_SIZE = 1024;
//公鑰
private static final String PUBLIC_KEY = "RSAPublicKey";
//私鑰
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* 初始化金鑰對
*
* @return Map 甲方金鑰的Map
*/
public static Map<String, Object> initKey() throws Exception {
//例項化金鑰生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
//初始化金鑰生成器
keyPairGenerator.initialize(KEY_SIZE);
//生成金鑰對
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//甲方公鑰
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
//甲方私鑰
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
//將金鑰儲存在map中
Map<String, Object> keyMap = new HashMap<String, Object>();
keyMap.put(PUBLIC_KEY, publicKey);
keyMap.put(PRIVATE_KEY, privateKey);
return keyMap;
}
/**
* 私鑰加密
*
* @param data 待加密資料
* @param key 金鑰
* @return byte[] 加密資料
*/
public static byte[] encryptByPrivateKey(byte[] data, byte[] key) throws Exception {
//取得私鑰
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//生成私鑰
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
//資料加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 公鑰加密
*
* @param data 待加密資料
* @param key 金鑰
* @return byte[] 加密資料
*/
public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws Exception {
//例項化金鑰工廠
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//初始化公鑰
//金鑰材料轉換
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
//產生公鑰
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
//資料加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
return cipher.doFinal(data);
}
/**
* 私鑰解密
*
* @param data 待解密資料
* @param key 金鑰
* @return byte[] 解密資料
*/
public static byte[] decryptByPrivateKey(byte[] data, byte[] key) throws Exception {
//取得私鑰
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//生成私鑰
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
//資料解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
/**
* 公鑰解密
*
* @param data 待解密資料
* @param key 金鑰
* @return byte[] 解密資料
*/
public static byte[] decryptByPublicKey(byte[] data, byte[] key) throws Exception {
//例項化金鑰工廠
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//初始化公鑰
//金鑰材料轉換
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
//產生公鑰
PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
//資料解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, pubKey);
return cipher.doFinal(data);
}
/**
* 取得私鑰
*
* @param keyMap 金鑰map
* @return byte[] 私鑰
*/
public static byte[] getPrivateKey(Map<String, Object> keyMap) {
Key key = (Key) keyMap.get(PRIVATE_KEY);
return key.getEncoded();
}
/**
* 取得公鑰
*
* @param keyMap 金鑰map
* @return byte[] 公鑰
*/
public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
Key key = (Key) keyMap.get(PUBLIC_KEY);
return key.getEncoded();
}
四、加密鹽
加密鹽也是比較常聽到的一個概念,鹽就是一個隨機字串用來和我們的加密串拼接後進行加密。加鹽主要是為了提供加密字串的安全性。假如有一個加鹽後的加密串,黑客通過一定手段這個加密串,他拿到的明文,並不是我們加密前的字串,而是加密前的字串和鹽組合的字串,這樣相對來說又增加了字串的安全性。
文中的一些演算法來源於網路,可直接複製使用。
比較推薦的幾個加密演算法有:
- 不可逆加密:
SHA256
、SHA384
、SHA512
以及HMAC-SHA256
、HMAC-SHA384
、HMAC-SHA512
- 對稱加密演算法:
AES
、3DES
- 非對稱加密演算法:
RSA