單向散列加密
散列是信息的提煉,通常其長度要比信息小得多,且爲一個固定長度。加密性強的散列一定是不可逆的,這就意味着通過散列結果,無法推出任何部分的原始信息。任何輸入信息的變化,哪怕僅一位,都將導致散列結果的明顯變化,這稱之爲雪崩效應。散列還應該是防衝突的,即找不出具有相同散列結果的兩條信息。具有這些特性的散列結果就可以用於驗證信息是否被修改。
單向散列函數一般用於產生消息摘要,密鑰加密等,常見的有:
1、MD5(Message Digest Algorithm 5):是RSA數據安全公司開發的一種單向散列算法,非可逆,相同的明文產生相同的密文。
2、SHA(Secure Hash Algorithm):可以對任意長度的數據運算生成一個160位的數值;
SHA-1與MD5的比較
因爲二者均由MD4導出,SHA-1和MD5彼此很相似。相應的,他們的強度和其他特性也是相似,但還有以下幾點不同:
1、對強行供給的安全性:最顯著和最重要的區別是SHA-1摘要比MD5摘要長32 位。使用強行技術,產生任何一個報文使其摘要等於給定報摘要的難度對MD5是2128數量級的操作,而對SHA-1則是2160數量級的操作。這樣,SHA-1對強行攻擊有更大的強度。
2、對密碼分析的安全性:由於MD5的設計,易受密碼分析的攻擊,SHA-1顯得不易受這樣的攻擊。
3、速度:在相同的硬件上,SHA-1的運行速度比MD5慢。
1、特徵:雪崩效應、定長輸出和不可逆。
2、作用是:確保數據的完整性。
3、加密算法:md5(標準密鑰長度128位)、sha1(標準密鑰長度160位)、md4、CRC-32
4、加密工具:md5sum、sha1sum、openssl dgst。
5、計算某個文件的hash值,例如:md5sum/shalsum FileName,openssl dgst –md5/-sha
在線MD5解密與加密
MD5加鹽實現方式
一般使用的加鹽:
md5(Password+UserName),即將用戶名和密碼字符串相加再MD5,這樣的MD5摘要基本上不可反查。
但有時候用戶名可能會發生變化,發生變化後密碼即不可用了(驗證密碼實際上就是再次計算摘要的過程)。
因此我們做了一個非常簡單的加鹽算法,每次保存密碼到數據庫時,都生成一個隨機16位數字,將這16位數字和密碼相加再求MD5摘要,然後在摘要中再將這16位數字按規則摻入形成一個48位的字符串。
在驗證密碼時再從48位字符串中按規則提取16位數字,和用戶輸入的密碼相加再MD5。按照這種方法形成的結果肯定是不可直接反查的,且同一個密碼每次保存時形成的摘要也都是不同的。
信息加密技術
對稱加密
對稱密碼技術:發件人和收件人使用其共同擁有的單個密鑰 ,這種密鑰既用於加密,也用於解密,叫做機密密鑰(也稱爲對稱密鑰或會話密鑰)。能夠提供信息機密性(沒有密鑰信息不能被解密)、完整性(被改變的信息不能被解密)的服務。對稱式密碼學又稱:單鑰密碼學、祕密密鑰密碼學、會話密鑰密碼學、私鑰密碼學、共享祕鑰密碼學
常見的對稱式加密技術
DES(數據加密標準):分組式加密,算法源於Lucifer,作爲NIST對稱式加密標準;64位(有效位56位、校驗8位),分組算法
3DES:128位,分組算法
IDEA(國際數據加密算法):128位,比DES快,分組算法
Blowfish:32-448位,算法公開,分組算法
RC4:流密碼,密鑰長度可變
RC5:分組密碼,密鑰長度可變,最大2048位
Rijndael:128位/196位/256位
AES(高級加密標準):DES升級版,算法出自Rinjindael
對稱密碼的優點
用戶只需記憶一個密鑰,就可用於加密、解密;
與非對稱加密方法相比,加密解密的計算量小,速度快,簡單易用,適合於對海量數據進行加密處理
對稱密碼的缺點
如果密鑰交換不安全,密鑰的安全性就會喪失。特別是在電子商務環境下,當客戶是未知的、不可信的實體時,如何使客戶安全地獲得密鑰就成爲一大難題。
如果用戶較多情況下的密鑰管理問題。N*(N-1)/2
如果密鑰多個用戶被共享,不能提供抗抵賴性
對稱密碼案例
假設Alice和Bob是認識的,兩人爲了保證通信消息不被其它人截取,預先約定了一個密碼,用來加密在他們之間傳送的消息,這樣即使有人截取了消息沒有密碼也無法知道消息的內容。由此便實現了機密性。
/**
* DES加密介紹 DES是一種對稱加密算法,所謂對稱加密算法即:加密和解密使用相同密鑰的算法。DES加密算法出自IBM的研究,
* 後來被美國政府正式採用,之後開始廣泛流傳,但是近些年使用越來越少,因爲DES使用56位密鑰,以現代計算能力,
* 24小時內即可被破解。雖然如此,在某些簡單應用中,我們還是可以使用DES加密算法,本文簡單講解DES的JAVA實現 。
* 注意:DES加密和解密過程中,密鑰長度都必須是8的倍數
*/
public class DES {
public DES() {
}
// 測試
public static void main(String args[]) {
// 待加密內容
String str = "cryptology";
// 密碼,長度要是8的倍數
String password = "95880288";
byte[] result = DES.encrypt(str.getBytes(), password);
System.out.println("加密後:" + new String(result));
// 直接將如上內容解密
try {
byte[] decryResult = DES.decrypt(result, password);
System.out.println("解密後:" + new String(decryResult));
} catch (Exception e1) {
e1.printStackTrace();
}
}
/**
* 加密
*
* @param datasource
* byte[]
* @param password
* String
* @return byte[]
*/
public static byte[] encrypt(byte[] datasource, String password) {
try {
SecureRandom random = new SecureRandom();
DESKeySpec desKey = new DESKeySpec(password.getBytes());
// 創建一個密匙工廠,然後用它把DESKeySpec轉換成
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey securekey = keyFactory.generateSecret(desKey);
// Cipher對象實際完成加密操作
Cipher cipher = Cipher.getInstance("DES");
// 用密匙初始化Cipher對象,ENCRYPT_MODE用於將 Cipher 初始化爲加密模式的常量
cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
// 現在,獲取數據並加密
// 正式執行加密操作
return cipher.doFinal(datasource); // 按單部分操作加密或解密數據,或者結束一個多部分操作
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
/**
* 解密
*
* @param src
* byte[]
* @param password
* String
* @return byte[]
* @throws Exception
*/
public static byte[] decrypt(byte[] src, String password) throws Exception {
// DES算法要求有一個可信任的隨機數源
SecureRandom random = new SecureRandom();
// 創建一個DESKeySpec對象
DESKeySpec desKey = new DESKeySpec(password.getBytes());
// 創建一個密匙工廠
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");// 返回實現指定轉換的
// Cipher
// 對象
// 將DESKeySpec對象轉換成SecretKey對象
SecretKey securekey = keyFactory.generateSecret(desKey);
// Cipher對象實際完成解密操作
Cipher cipher = Cipher.getInstance("DES");
// 用密匙初始化Cipher對象
cipher.init(Cipher.DECRYPT_MODE, securekey, random);
// 真正開始解密操作
return cipher.doFinal(src);
}
}
非對稱加密
使用一對密鑰:一個用於加密信息,另一個則用於解密信息。兩個密鑰之間存在着相互依存關係:即用其中任一個密鑰加密的信息只能用另一個密鑰進行解密。其中加密密鑰不同於解密密鑰,公鑰加密私鑰解密,反之也可私鑰加密公鑰解密。密鑰依據性質劃分,將其中的一個向外界公開,稱爲公鑰;另一個則自己保留,稱爲私鑰。公鑰(Public key)常用於數據加密(用對方公鑰加密)或簽名驗證(用對方公鑰解密),私鑰(Private key)常用於數據解密(發送方用接收方公鑰加密)或數字簽名(用自己私鑰加密)。機密性、完整性、抗抵賴性
1.使用過程:
乙方生成兩把密鑰(公鑰和私鑰)
甲方獲取乙方的公鑰,然後用它對信息加密。
乙方得到加密後的信息,用私鑰解密,乙方也可用私鑰加密字符串
甲方獲取乙方私鑰加密數據,用公鑰解密
優點:難破解
缺點: 加密速度慢
常用算法:
RSA、Elgamal、揹包算法、Rabin、D-H、ECC(橢圓曲線加密算法)
RSA 工具類
/**
* RSA加解密工具類
*
* @author QiuFeihu
*
*/
public class RSAUtil {
public static String publicKey; // 公鑰
public static String privateKey; // 私鑰
/**
* 生成公鑰和私鑰
*/
public static void generateKey() {
// 1.初始化祕鑰
KeyPairGenerator keyPairGenerator;
try {
keyPairGenerator = KeyPairGenerator.getInstance("RSA");
SecureRandom sr = new SecureRandom(); // 隨機數生成器
keyPairGenerator.initialize(512, sr); // 設置512位長的祕鑰
KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 開始創建
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
// 進行轉碼
publicKey = Base64.encodeBase64String(rsaPublicKey.getEncoded());
// 進行轉碼
privateKey = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 私鑰匙加密或解密
*
* @param content
* @param privateKeyStr
* @return
*/
public static String encryptByprivateKey(String content, String privateKeyStr, int opmode) {
// 私鑰要用PKCS8進行處理
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyStr));
KeyFactory keyFactory;
PrivateKey privateKey;
Cipher cipher;
byte[] result;
String text = null;
try {
keyFactory = KeyFactory.getInstance("RSA");
// 還原Key對象
privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
cipher = Cipher.getInstance("RSA");
cipher.init(opmode, privateKey);
if (opmode == Cipher.ENCRYPT_MODE) { // 加密
result = cipher.doFinal(content.getBytes());
text = Base64.encodeBase64String(result);
} else if (opmode == Cipher.DECRYPT_MODE) { // 解密
result = cipher.doFinal(Base64.decodeBase64(content));
text = new String(result, "UTF-8");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return text;
}
/**
* 公鑰匙加密或解密
*
* @param content
* @param privateKeyStr
* @return
*/
public static String encryptByPublicKey(String content, String publicKeyStr, int opmode) {
// 公鑰要用X509進行處理
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyStr));
KeyFactory keyFactory;
PublicKey publicKey;
Cipher cipher;
byte[] result;
String text = null;
try {
keyFactory = KeyFactory.getInstance("RSA");
// 還原Key對象
publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
cipher = Cipher.getInstance("RSA");
cipher.init(opmode, publicKey);
if (opmode == Cipher.ENCRYPT_MODE) { // 加密
result = cipher.doFinal(content.getBytes());
text = Base64.encodeBase64String(result);
} else if (opmode == Cipher.DECRYPT_MODE) { // 解密
result = cipher.doFinal(Base64.decodeBase64(content));
text = new String(result, "UTF-8");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return text;
}
// 測試方法
public static void main(String[] args) {
/**
* 注意: 私鑰加密必須公鑰解密 公鑰加密必須私鑰解密
*/
System.out.println("-------------生成兩對祕鑰,分別發送方和接收方保管-------------");
RSAUtil.generateKey();
System.out.println("公鑰匙給接收方:" + RSAUtil.publicKey);
System.out.println("私鑰給發送方:" + RSAUtil.privateKey);
System.out.println("-------------第一個栗子,私鑰加密公鑰解密-------------");
// String textsr = "早啊,你吃早飯了嗎?O(∩_∩)O~";
// // 私鑰加密
// String cipherText = RSAUtil.encryptByprivateKey(textsr,
// RSAUtil.privateKey, Cipher.ENCRYPT_MODE);
// System.out.println("發送方用私鑰加密後:" + cipherText);
// // 公鑰解密
// String text = RSAUtil.encryptByPublicKey(cipherText,
// RSAUtil.publicKey, Cipher.DECRYPT_MODE);
// System.out.println("接收方用公鑰解密後:" + text);
System.out.println("-------------第二個栗子,公鑰加密私鑰解密-------------");
// 公鑰加密
String textsr = "吃過啦!你吃了嗎?O(∩_∩)O~";
String cipherText = RSAUtil.encryptByPublicKey(textsr, RSAUtil.publicKey, Cipher.ENCRYPT_MODE);
System.out.println("接收方用公鑰加密後:" + cipherText);
// 私鑰解密
String text = RSAUtil.encryptByprivateKey(cipherText, RSAUtil.privateKey, Cipher.DECRYPT_MODE);
System.out.print("發送方用私鑰解密後:" + text);
}
}
基於令牌方式隱藏參數
@RestController
public class PayController extends BaseApiService {
@Autowired
private BaseRedisService baseRedisService;
private static long timeToken = 15 * 60l;
@RequestMapping("/pay")
public ResponseBase pay(String token) {
// 獲取提交參數 數據庫保存.,
if (StringUtils.isEmpty(token)) {
return setResultError("token 不能爲空!");
}
String reuslt = (String) baseRedisService.getString(token);
if (StringUtils.isEmpty(reuslt)) {
return setResultError("參數不能空!");
}
System.out.println("獲取提交的參數reuslt:" + reuslt);
return setResultSuccess("獲取提交的參數reuslt:" + reuslt);
}
@RequestMapping("/getPayToken")
public String pay(Long userId, Long money) {
String payToken = UUID.randomUUID().toString();
baseRedisService.setString(payToken, userId + "-" + money, timeToken);
return payToken;
}
}