這是我參加全國信息安全大賽的設計的加密系統中的一個加密算法,雖然比賽的結果不是很是理想可是,我仍是學到了不少東西,如今和你們分享一下,比賽收穫的東西。
基於口令加密
PBE(Password Based Encryption,基於口令加密)算法是一種基於口令的加密算法,其特色在於口令是由用戶本身掌握的,採用隨機數雜湊多重加密等方法保證數據的安全性。
PBE算法沒有密鑰的概念,密鑰在其它對稱加密算法中是通過算法計算得出來的,PBE算法則是使用口令替代了密鑰。
密鑰的長短直接影響了算法的安全性,但不方便記憶。即使是咱們將密鑰通過Base64編碼轉換爲一個可見字符,長密鑰同樣不容易記憶。所以,在這種狀況下密鑰是須要存儲的,可是口令則否則。好比通常人每天開關電腦,進入操做系統的惟一途徑就是輸入口令。口令是咱們便於記憶的一種憑證,基於這一點,PBE算法使用口令替代了密鑰。
PBE算法並無真正構建新的加密/解密算法,而是對咱們已經知道的對稱加密算法(如DES算法)作了包裝。使用PBE算法對數據作加密/解密操做的時候,實際上是使用了DES或者是AES等其它對稱加密算法作了相應的操做。
既然PBE算法使用咱們較爲經常使用的對稱加密算法,那就沒法迴避密鑰的問題。口令並不能替代密鑰,密鑰是通過加密算法計算得來的,可是口令自己不可能很長看,單純的口令很容易經過窮舉攻擊方式破譯,這就引入了「鹽」。鹽能阻止字典攻擊或預先計算的攻擊,它自己是一個隨機信息,相同的隨機信息極不可能使用兩次。將鹽附加在口令上,經過消息摘要算法通過迭代計算得到構建密鑰/初始化向量的基本材料,使得破譯的難度加大。
基於PBE算法的消息傳遞模型以下圖3-13所示:甲乙雙方做爲消息傳遞雙方(甲方爲發送方,也就是甲方是本系統的服務器,乙方做爲接收方,也就是本系統客戶端的使用者),假定甲乙雙方在消息傳遞前已經商定加密算法迭代的次數,與完成一次消息傳遞須要通過以下步驟:
1) 由消息傳遞雙方約定口令,這裏由甲方構建口令。
2) 由口令構建者發佈口令,即本系統的服務器將口令發送給系統的客戶端使用者
3) 由口令構建者構建本次消息傳遞使用的鹽,這裏由甲方(本系統)構建鹽
4) 由消息發送方使用口令、鹽對數據加密,這裏由甲方對數據加密
5) 由消息發送者將鹽、加密數據放鬆給消息接收者,這裏由甲方將鹽、加密數據發送給乙方
6) 由消息接收方使用鹽、口令對加密數據解密,這裏由乙方完成數據解密
圖3-13 基於PBE算法的消息通信模型
基於PBE算法的消息傳遞模型理解起來並非十分複雜。對於上述單項消息傳遞而言,若是乙方想要恢復甲方消息,甲方並不須要重複步驟一、2,僅僅想要由乙方執行三、四、5,由甲方執行步驟6便可。
同時,甲乙雙方也能夠在消息傳遞過程當中傳遞迭代次數。
「鹽」自己就是一種能夠由消息傳遞雙方按必定規律約定的消息,好比時間。也能夠是某個不可變物理硬件的編號,好比U盤的自身惟一標識,而本系統則採用「手機的惟一標識」,WIM 是一個防篡改硬件,在安全層和應用層執行安全功能,保存處理用戶的ID 和權限等功能。
甲乙雙方能夠經過約定消息傳遞的時間,這裏是由本系統直接決定時間,並將其做爲基本消息,根據預約的算法(如MD5算法) 對其處理,最終獲取真正的「鹽」,這樣一來,「鹽」就無需傳遞,提升了安全性。
假設一個場景:由這樣的一個系統,用戶使用者須要經過手機訪問一個系統,同同時須要輸入口令方能登陸系統,那麼手機自己就是「鹽」的提供者!即便手機丟失,加密的信息也未必能被竊取!即「鹽」與口令就像兩把不可分離的鑰匙。
PBE實現方案:
Java 6 和Bouncy Castle都提供了PBE系列算法的相關實現,差異在於對各類消息摘要算法和對稱加密算法的組合。經常使用的消息摘要算法包括MD5和SHA算法,經常使用的對稱加密算法包括DES、RC2等。PBE系列算法就死將這些算法進行合理組合,其密鑰長度均以PBE具體的算法中的對稱加密算法爲準。
有關PBE算法的Java6 和Bouncy Castle實現細節以下表3-11。
表3-11 PBE算法
整體看來口令和鹽兩邊都須要知道。消息傳遞過程仍是須要指定雙方的統一算法進行。而這些算法其實仍是用的那些常見的對稱加密算法
3、java6和bouncycastle支持的算法列表
算法 密鑰長度 密鑰長度默認值 工做模式 填充方式 備註
PBEWithMD5AndDES 56 56 CBC PKCS5Padding java6實現
PBEWithMD5AndTripeDES 1十二、168 168 CBC PKCS6Padding java7實現
PBEWithSHA1AndDESede 1十二、168 168 CBC PKCS7Padding java8實現
PBEWithSHA1AndRC2_40 40至1024 128 CBC PKCS8Padding java9實現
PBEWithMD5AndDES 64 64 CBC PKCS5Padding/PKCS7Padding/ISO10126Padding/ZeroBytePadding BouncyCastle實現
PBEWithMD5AndRC2 128 128 CBC PKCS5Padding/PKCS7Padding/ISO10127Padding/ZeroBytePadding BouncyCastle實現
PBEWithSHA1AndDES 64 64 CBC PKCS5Padding/PKCS7Padding/ISO10128Padding/ZeroBytePadding BouncyCastle實現
PBEWithSHA1AndRC2 128 128 CBC PKCS5Padding/PKCS7Padding/ISO10129Padding/ZeroBytePadding BouncyCastle實現
PBEWithSHAAndIDEA-CBC 128 128 CBC PKCS5Padding/PKCS7Padding/ISO10130Padding/ZeroBytePadding BouncyCastle實現
PBEWithSHAAnd2-KeyTripleDES-CBC 128 128 CBC PKCS5Padding/PKCS7Padding/ISO10131Padding/ZeroBytePadding BouncyCastle實現
PBEWithSHAAnd3-KeyTripleDES-CBC 192 192 CBC PKCS5Padding/PKCS7Padding/ISO10132Padding/ZeroBytePadding BouncyCastle實現
PBEWithSHAAnd128BitRC2-CBC 128 128 CBC PKCS5Padding/PKCS7Padding/ISO10133Padding/ZeroBytePadding BouncyCastle實現
PBEWithSHAAnd40BitRC2-CBC 40 40 CBC PKCS5Padding/PKCS7Padding/ISO10134Padding/ZeroBytePadding BouncyCastle實現
PBEWithSHAAnd128BitRC4 128 128 CBC PKCS5Padding/PKCS7Padding/ISO10135Padding/ZeroBytePadding BouncyCastle實現
PBEWithSHAAnd40BitRC4 40 40 CBC PKCS5Padding/PKCS7Padding/ISO10136Padding/ZeroBytePadding BouncyCastle實現
PBEWithSHAAndTwofish-CBC 256 256 CBC PKCS5Padding/PKCS7Padding/ISO10137Padding/ZeroBytePadding BouncyCastle實現
PBE實現代碼:
PBECoder.javajava
package com.wecode.database.secure; import java.security.Key; import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; /** * PBE安全編碼組件 * * @author WeCode ( ydonghao2 ) * @version 1.0 */ public class PBECoder { public static final String ALGORUTHM = "PBEWITHMD5andDES"; /** * 迭代次數 */ public static final int ITERATION_COUN = 100; /** * "鹽"初始化<br> * 鹽長度必須爲8字節 * @return byte[] 鹽 * @throws Exception */ public static byte[] initSalt() throws Exception { //實例化安全隨機數 SecureRandom random = new SecureRandom(); //產出鹽 return random.generateSeed(8); } /** * 轉換密鑰 * @param password 密碼 * @return Key密鑰 * @throws Exception * */ private static Key toKey(String password) throws Exception { //密鑰材料轉換 PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray()); //實例化 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORUTHM); //生成密鑰 SecretKey secretkey = keyFactory.generateSecret(keySpec); return secretkey; } /** * 加密 * @param data 數據 * @param password 密鑰 * @param salt 鹽 * @return byte[] 加密數據 * @throws Exception */ public static byte[] encrypt(byte[] data, String password, byte[] salt) throws Exception{ //轉換密鑰 Key key = toKey(password); //實例化PBE參考數據 PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, ITERATION_COUN); //實例化 Cipher cipher = Cipher.getInstance(ALGORUTHM); //初始化 cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec); //執行操做 return cipher.doFinal(data); } /** * 解密 * @param data 數據 * @param password 密碼 * @param salt 鹽 * @return byte[] 解密數據 * @throws Exception */ public static byte[] decrypt(byte[] data, String password, byte[] salt) throws Exception{ //轉換密鑰 Key key = toKey(password); //實例化PBE參數材料 PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, ITERATION_COUN); //實例化 Cipher cipher = Cipher.getInstance(ALGORUTHM); //初始化 cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec); //執行操做 return cipher.doFinal(data); } }
PBE算法是實現過程當中須要關注的環節,包括「鹽」的初始化,密鑰材料的轉化和加密/解密的時間。
在初始化「鹽」時,必須使用隨機的方式構造「鹽」,最終要獲得一個8字節的字節數組。鑑於安全性的要求,這裏的隨機數生成器只能使用SecureRandom類,以下所示:算法
//實例化安全性隨機數 SecureRandomrandom = new SecureRandom(); //產出「鹽」 byte[]b = random.generateSeed(8);
字節數組b[]就是咱們要的「鹽」。
密鑰材料轉換部分不一樣於其餘對稱加密算法,這裏使用的是PBEKeySpec類,以下所示:apache
//密鑰材料轉換 PBEKeySpeckeySpec = new PBEKeySpec(password.toChatArray());
其餘對稱加密算法的密鑰材料實現類的構造方法要求輸入字節數組形式的變量,而PBEKeySpec類構造方法則要求輸入字符數組變量。
爲何不是字符串(String)而是字符數組(char[])呢?這是由於字符串是可序列化的封裝類,可在程序調用時被序列化到文件中,而字符數組只能之內存變量的形式保留在內存中。
在加密/解密實現時,須要注意使用參數材料PBEParameterSpec類,同時注意迭代次數。構建PBE參數材料後就能夠轉交給Cipher類完成加密/解密操做,以下所示:數組
//實例化PBE參數材料 PBEParameterSpecparamSpec = new PBEParameterSpec(salt, ITERATION_COUNT); //實例化 Cipher= cipher = Cipher.getInstance(ALGORITHM); //初始化 cipher.init(Cipher.DECRYPT_MODE,key, paramSpec);
- KEY_ALGORITHM 指明加密算法,這裏是PBEWITHMD5andDES加密算法。
- ITERATION_COUN迭代的次數。
+initSalt()PBE加密方式的初始化鹽返回的是byte[] 鹽,鹽長度必須爲8字節。
+ toKey(Stringpassword) 轉換密鑰,password是密碼,返回Key類型密鑰。
+encrypt(byte[]data, String password, byte[] salt) 加密方法。Data是數據,password是密鑰,salt是鹽,返回byte[]類型,是加密之後的數據。
+decrypt(byte[]data, String password, byte[] salt)解密方法,data是數據,password的密碼,salt是鹽,返回byte[]類型,是解之後的數據。
PBE算法實現和AES算法有不少相似的地方,類似的地方下面代碼不具體介紹。
PBE的主要代碼以下:
本系統在這裏採用的是PBEWITHMD5andDES算法。安全
public static final String ALGORUTHM ="PBEWITHMD5andDES";
迭代次數服務器
public static final int ITERATION_COUN = 100;
"鹽"初始化
鹽長度必須爲8字節
返回:
byte[]鹽
拋出:
java.lang.Exceptiondom
public static byte[] initSalt() throws Exception { //實例化安全隨機數 SecureRandom random = new SecureRandom(); //產出鹽 return random.generateSeed(8); }
轉換密鑰
參數:
password-密碼
返回:
Key密鑰
拋出:
java.lang.Exception
PBE算法定義並繼承了SecretKey接口。測試
private static Key toKey(String password) throws Exception { //密鑰材料轉換 PBEKeySpec keySpec = newPBEKeySpec(password.toCharArray()); //實例化 SecretKeyFactory keyFactory =SecretKeyFactory.getInstance(ALGORUTHM); //生成密鑰 SecretKey secretkey =keyFactory.generateSecret(keySpec); return secretkey; }
加密方法
參數:
data- 數據
password- 密鑰
salt- 鹽
返回:
byte[]加密數據
拋出:
java.lang.Exception編碼
public static byte[] encrypt(byte[] data, String password, byte[] salt) throws Exception{ //轉換密鑰 Key key = toKey(password); //實例化PBE參考數據 PBEParameterSpec parameterSpec = new PBEParameterSpec(salt,ITERATION_COUN); //實例化 Cipher cipher = Cipher.getInstance(ALGORUTHM); //初始化 cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec); //執行操做 return cipher.doFinal(data); }
解密方法
參數:
data- 數據
password- 密碼
salt- 鹽
返回:
byte[]解密數據
拋出:
java.lang.Exception加密
public static byte[] decrypt(byte[] data, String password,byte[] salt) throws Exception{ //轉換密鑰 Key key = toKey(password); //實例化PBE參數材料 PBEParameterSpec parameterSpec = newPBEParameterSpec(salt, ITERATION_COUN); //實例化 Cipher cipher = Cipher.getInstance(ALGORUTHM); //初始化 cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec); //執行操做 return cipher.doFinal(data); }
PBE算法實例驗證PBECoder.java:
PBECoderTest.java
package com.wecode.database.secure; import org.apache.commons.codec.binary.Base64; import org.junit.Test; /** * PBE 檢驗 * * @author WeCode ydonghao2 * @version 1.0 */ public class PBECoderTest { /** * 測試 * * @throws Exception */ /** * 測試的時候人工能夠引入junit.jar包 * @throws Exception */ //@Test public static void main(String[] args) throws Exception { String inputStr = "PBE"; System.err.println("原文:\t" + inputStr); byte[] input = inputStr.getBytes(); String pwd = "ydonghao"; System.err.println("密碼:\t" + pwd); // 初始化鹽 byte[] salt = PBECoder.initSalt(); System.err.println("鹽:\t" + Base64.encodeBase64String(salt)); // 加密 byte[] data = PBECoder.encrypt(input, pwd, salt); System.err.println("加密後\t" + Base64.encodeBase64String(data)); // 解密 byte[] output = PBECoder.decrypt(data, pwd, salt); String outputStr = new String(output); System.err.println("解密後\t" + outputStr); } }
這個加密算法的實現須要一些環境的搭建,參考這裏:http://blog.csdn.net/ydonghao2/article/details/11046635