Java程式設計師從笨鳥到菜鳥(六十九)常用加密方法
阿新 • • 發佈:2018-12-28
前言
在這個資訊共享的時代,資訊保安無論是對於開發還是使用者來說都是重點關注的問題,例如在表單提交時,採用密文的方式來代替明文,可以相對有效避免重要資訊外洩,文中闡述了幾種比較常用的加密方法
加密方式
1、BASE64
嚴格來說是編碼格式,而非加密演算法;特點是加密解密是雙向的,可以求反解;主要是 BASE64Encoder、BASE64Decoder 兩個類;常見用於郵件、http 加密
測試程式碼實現:
package encoder; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; /** * create by
[email protected] on 2018/12/13 9:22 * BASE64 嚴格來說是編碼格式,而非加密演算法 * 加密解密是雙向的,可以求反解 * 主要是 BASE64Encoder、BASE64Decoder 兩個類 * 常見於郵件、http 加密 **/ public class BASE64 { /** * BASE64 解密 * @param key 祕鑰 * @throws Exception 異常 * */ private static byte[] decryptBASE64(String key) throws Exception { return (new BASE64Decoder()).decodeBuffer(key); } /** * BASE64 加密 * @param key 祕鑰 * @throws Exception 異常 * */ private static String encrypyBASE64(byte[] key) throws Exception { return (new BASE64Encoder()).encodeBuffer(key); } public static void main(String[] args) { String str = "12345678"; try { System.out.println("str====加密前====" + str); String encodeData = BASE64.encrypyBASE64(str.getBytes()); System.out.println("encodeData====加密資料=====" + encodeData); byte[] decodeData = BASE64.decryptBASE64(encodeData); System.out.println("decodeData====解密資料=====" + new String(decodeData)); } catch (Exception e) { e.printStackTrace(); } } }
執行結果:
str====加密前====12345678
encodeData====加密資料=====MTIzNDU2Nzg=
decodeData====解密資料=====12345678
Process finished with exit code 0
2、MD5
MD5(Message Digest Algorithm 5) 資訊摘要演算法,用於確保資訊傳輸完整一致,是計算機廣泛使用的雜湊演算法之一(又稱摘要演算法、雜湊演算法),將資料(如漢字)運算為另一固定長度值,常用語檔案校驗;通常是不直接使用上述 MD5 加密,通常將 MD5 產生的位元組陣列交給 BASE64 再加密一把,得到相應的字串
具有以下特點:
- 壓縮性:任意長度的資料,算出的 MD5 值長度是固定的
- 容易計算:從原資料計算出 MD5 值很容易
- 抗修改性:對於原資料進行任何更改,所得到的 MD5 值都有很大的區別
- 弱抗碰撞:已知原資料和其 MD5 值,想找到一個具有相同 MD5 值的資料非常困難
- 強抗碰撞:想找到兩個不同的資料,使他們具有相同的 MD5 值,非常困難
測試程式碼實現:
package encoder;
import java.math.BigInteger;
import java.security.MessageDigest;
/**
* create by [email protected] on 2018/12/13 10:02
* MD5(Message Digest Algorithm 5) 資訊摘要演算法 用於確保資訊傳輸完整一致
* 是計算機廣泛使用的雜湊演算法之一(又譯摘要演算法、雜湊演算法)
* 將資料(如漢字)運算為另一固定長度值,是雜湊演算法的基礎原理,常用語檔案校驗
* 通常不直接使用上述 MD5 加密,通常將 MD5 產生的位元組陣列交給 BASE64 再加密一把,得到相應的字串
* 具有以下特點:
* 1、壓縮性:任意長度的資料,算出的 MD5 值長度是固定的
* 2、容易計算:從原資料計算出 MD5 值很容易
* 3、抗修改性:對原資料進行任何更改,所得到的 MD5 值都有很大區別
* 4、弱抗碰撞:已知原資料和其 MD5 值,想找到一個具有相同 MD5 值得資料時非常困難的
* 5、強抗碰撞:想找到兩個不同的資料,使他們具有相同的 MD5 值,非常困難
**/
public class MD5 {
private static final String KEY_MD5 = "MD5";
private static String getResult(String inputStr) {
System.out.println("=====加密前的資料:" + inputStr);
BigInteger bigInteger = null;
try {
MessageDigest md = MessageDigest.getInstance(KEY_MD5);
byte[] inputData = inputStr.getBytes();
md.update(inputData);
bigInteger = new BigInteger(md.digest());
} catch (Exception e) {
e.printStackTrace();
}
return bigInteger.toString(16);
}
public static void main(String[] args) {
try {
String inputStr = "簡單加密8888888888";
System.out.println("MD5 加密後:" + getResult(inputStr));
} catch (Exception e) {
e.printStackTrace();
}
}
}
執行結果:
=====加密前的資料:簡單加密8888888888
MD5 加密後:-60398b41d73fd45b9f90ce6a612ada8c
Process finished with exit code 0
3、SHA
SHA(Secure Hash Algorithm 安全雜湊演算法)主要適用於數字簽名標準(Digest Signature Standard DSS)裡面定義的數字簽名演算法
演算法思想:接收一段明文,然後以一種不可逆的方式將它轉換成一段(通常更小)密文
較之 MD5 更為安全
SHA-1 與 MD5 比較;因為二者均由 MD4 匯出,SHA-1 和 MD5 彼此很相似,但還有以下幾點不同:
- 對強行攻擊的安全性:最顯著和最重要的區別是 SHA-1 摘要要比 MD5 摘要長 32 位
- 對密碼分析的安全性:由於 MD5 的設計,易受密碼分析攻擊,SHA-1 不易受攻擊
- 速度:在相同的硬體基礎上,SHA-1 的執行速度比 MD5 慢
測試程式碼實現:
package encoder;
import java.math.BigInteger;
import java.security.MessageDigest;
/**
* create by [email protected] on 2018/12/13 10:36
* SHA (Secure Hash Algorithm 安全雜湊演算法)主要適用於數字簽名標準(Digest Signature Standard DSS)裡面定義的數字簽名演算法
* (Digest Signature Algorithm DSA) 對於長度小於 2^64 位的訊息,SHA1 會產生一個 160 位的訊息摘要
* 演算法思想:接收一段明文,然後以一種不可逆的方式將它轉換成一段(通常更小)密文
* 較之 MD5 更為安全
* SHA-1 與 MD5 的比較
* 因為二者均由 MD4 匯出,SHA-1 和 MD5 彼此很相似,但還有以下幾點不同
* 1、對強行攻擊的安全性:最顯著和最重要的區別是 SHA-1 摘要要比 MD5 摘要長 32 位
* 2、對密碼分析的安全性:由於 MD5 的設計,易受密碼分析攻擊,SHA-1 不易受攻擊
* 3、速度:在相同的硬體基礎上,SHA-1 的執行速度比 MD5 慢
**/
public class SHA {
private static final String KEY_SHA = "SHA";
private static String getResult(String inputStr) {
BigInteger bigInteger = null;
System.out.println("=======加密前的資料:" + inputStr);
byte[] inputData = inputStr.getBytes();
try {
MessageDigest messageDigest = MessageDigest.getInstance(KEY_SHA);
messageDigest.update(inputData);
bigInteger = new BigInteger(messageDigest.digest());
} catch (Exception e) {
e.printStackTrace();
}
return bigInteger.toString(32);
}
public static void main(String[] args) {
try {
String inputStr = "簡單加密";
System.out.println("=======加密後的資料:" + getResult(inputStr));
} catch (Exception e) {
e.printStackTrace();
}
}
}
執行結果:
=======加密前的資料:簡單加密
=======加密後的資料:91k9vo7p400cjkgfhjh0ia9qthsjagfn
Process finished with exit code 0
4、AES
AES(Advanced Encryption Standard 高階加密標準)是現在對稱加密演算法中最流行的演算法之一
AES 可以使用 128、192 和 256 位金鑰,並且用 128 位分組加密和解密資料,相對來說安全
測試程式碼實現:
public class AES {
private static String src = "TestAES";
/**
* AES 加密字串
* @param content 需要被加密的字串
* @param password 加密需要的密碼
* @return 密文
* */
public static byte[] encrypt(String content, String password) {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); // 建立 AES 的 key 生產者
keyGenerator.init(128, new SecureRandom(password.getBytes())); // 利用使用者密碼作為隨機初始化出
// 128 位的 key 生產者,SecureRandom 是生成安全隨機序列,password.getBytes() 是種子,只要種子相同,序列就一樣
// 所以解密只要有 password 就行
SecretKey secretKey = keyGenerator.generateKey(); // 根據使用者密碼,生成一個金鑰
byte[] enCodeFormate = secretKey.getEncoded(); // 返回基本編碼格式的金鑰,如果此金鑰不支援編碼,則返回 null
SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormate, "AES"); // 轉換為 AES 專用金鑰
Cipher cipher = Cipher.getInstance("AES"); // 建立密碼器
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); // 初始化為加密模式的密碼器
byte[] result = cipher.doFinal(byteContent); // 加密
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* AES 解密 解密AES 加密的字串
* @param content AES 加密過的內容
* @param password 加密時的密碼
* @return 明文
* */
public static byte[] decrypt(byte[] content, String password) {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); // 建立 AES 的 key 生產者
keyGenerator.init(128, new SecureRandom(password.getBytes()));
SecretKey secretKey = keyGenerator.generateKey(); // 根據使用者密碼,生成一個金鑰
byte[] enCodeFormat = secretKey.getEncoded(); // 返回基本編碼格式的金鑰
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); // 轉為 AES 專用金鑰
Cipher cipher = Cipher.getInstance("AES"); // 建立密碼器
cipher.init(Cipher.DECRYPT_MODE, key); // 初始化為解密模式的密碼器
byte[] result = cipher.doFinal(content);
return result; // 明文
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String content = "美女,約嗎?";
String password = "123";
System.out.println("====加密前的內容:" + content);
// 加密
byte[] encrypt = encrypt(content, password);
System.out.println("====加密後的內容,轉換前:" + new String(encrypt));
System.out.println("====加密後的內容,轉換後:" + ParseSystemUtil.parseByte2HexStr(encrypt));
// 解密
byte[] decrypt = decrypt(encrypt, password);
System.out.println("====解密後的內容:" + new String(decrypt));
}
}
執行結果:
====加密前的內容:美女,約嗎?
====加密後的內容,轉換前:P�d�g�K�3�g�����,Ꝏ?U納�_
====加密後的內容,轉換後:50FE6401E867A34BD533FE67BB85EDABFED62CEA9D8E3F5516E7B48D01F21A5F
====解密後的內容:美女,約嗎?
Process finished with exit code 0
編碼轉換工具:
ParseSystemUtil 類實現:
package encoder;
/**
* create by [email protected] on 2018/12/13 13:53
* 位元組陣列和十六進位制之間的相互轉換
**/
public class ParseSystemUtil {
/**
* 將位元組陣列轉換成十六進位制
* @param buf 位元組陣列
* @return 十六進位制字串
* */
public static String parseByte2HexStr(byte[] buf) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
/**
* 將十六進位制轉換成位元組陣列
* @param hex 十六進位制的字串
* @return 位元組陣列
* */
public static byte[] parseHexStr2Byte(String hex) {
if (hex.length() < 1) {
return null;
}
byte[] result = new byte[hex.length() / 2];
for (int i = 0; i < hex.length() / 2; i++) {
int high = Integer.parseInt(hex.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(hex.substring(i * 2 + 1, i * 2 + 2), 16);
result[i] = (byte)(high * 16 + low);
}
return result;
}
// 字串轉字元陣列
public static char[] change(String s) {
char[] result = new char[s.length()/2]; // 定義陣列
for (int i = 0; i < result.length; i++)
result[i] = (char)(Integer.parseInt(s.substring(2*i, 2*i + 2),16 & 0xFF));
return result;
}
}