一。摘要算法html
1》MD5算法(Message Digest Algorithm 5) 能夠保證數據傳輸完整性和一致性 摘要後長度爲16字節 摘要信息中不包含原文信息java
全部加密結果不可逆(沒法解密) 通常在傳送文件時 對源文件進行md5 hash 傳送到對方後 檢測hash值是否相等 若是相等文件傳輸正確算法
若是不相等 說明文件被篡改(加入木馬)或者未傳送完成數據庫
其餘MD算法 MD2(16字節)數組
public static void main(String[] args) throws NoSuchAlgorithmException { MessageDigest md=MessageDigest.getInstance("MD5") ; String code="hello"; byte[] bt=md.digest(code.getBytes()); System.out.println(bt.length); }2》SHA算法Secure Hash Algorithm(安全hash算法) 安全散列算法(hash函數 將原始信息壓縮 返回散列值)能夠是SHA-1,SHA1是目前最安全
的摘要算法 摘要的長度爲 20字節 安全
其餘的SHA 包括 SHA-256(32字節)服務器
public static void main(String[] args) throws NoSuchAlgorithmException { MessageDigest md=MessageDigest.getInstance("SHA") ;//或者SHA-1 SHA1 String code="hello"; byte[] bt=md.digest(code.getBytes()); System.out.println(bt.length); }
二。編碼和解碼app
1》16進制 編碼 計算機系統使用 2進制 爲了編寫存儲方便通常將2進制 轉換爲16進制字符串 其中base64也是其中相似轉換一種 16進制編碼和base64都是dom
可逆的 通常用於存儲函數
public static byte[] toByte(String src){ ByteArrayOutputStream baos=new ByteArrayOutputStream(); for(int i=0;i<src.length();i=i+2){ char fchar=src.charAt(i); char nchar=src.charAt(i+1); byte srcb=0; if(fchar=='0'){ srcb=Byte.parseByte(nchar+"", 16); }else{ srcb=(byte)(Integer.parseInt(fchar+""+nchar, 16)); } baos.write(srcb); } return baos.toByteArray(); } public static String toHex(byte[] src){ StringBuffer sb=new StringBuffer(); for(byte s:src){ //0XFF表示 8位的 11111111 和它&後 只剩下 8位 其餘位都爲0 String result=Integer.toHexString(s&0xFF); if(result.length()==1){ result='0'+result; } sb.append(result); } return sb.toString(); }
2》Base64編碼 用於將字節數組和字符串互相轉換
public static void main(String[] args) throws NoSuchAlgorithmException, IOException { byte[] src="hello".getBytes(); //摘要出來的結果爲字節數組 存儲到數據庫不方便 MessageDigest md=MessageDigest.getInstance("SHA") ; byte[] bt=md.digest(src); //使用base64轉換爲字符串方便存儲 BASE64Encoder base=new BASE64Encoder(); String str=base.encode(bt); System.out.println(str); //還原成字節數組 BASE64Decoder de=new BASE64Decoder(); byte[] bts=de.decodeBuffer(str); System.out.println(bt.length==bts.length); }
三。對稱加密
1》DES算法 (Data Encryptin Standard) 是對稱加密算法的一種 使用祕鑰加解密 祕鑰必須是56字節
概念解釋:
祕鑰 :用於加密和解密的鑰匙 祕鑰可使用 getEncoded方法 獲取byte[] 存儲在文件系統中
公鑰和私鑰:用於非對稱加密的鑰匙 公鑰加密 私鑰解密 私鑰通常用於解密因此私鑰通常存儲在密鑰庫中
口令:通常是自定義的字符串 能夠經過口令和鹽生成祕鑰
/** * 生成56字節的祕鑰 */ public static SecretKey genKey(int len) throws NoSuchAlgorithmException{ KeyGenerator kg=KeyGenerator.getInstance("DES"); kg.init(len); return kg.generateKey(); } public static void main(String[] args) throws Exception { //SecretKey sk=new SecretKeySpec(kl.getBytes(), "DES"); SecretKey sk=genKey(57); //---------加密 String password="tiger"; Cipher cipher=Cipher.getInstance("DES"); cipher.init(Cipher.ENCRYPT_MODE, sk); //被加密以後獲取的字節數組 byte[] mcontent=cipher.doFinal(password.getBytes()); //---------解密 Cipher cipher1=Cipher.getInstance("DES"); cipher1.init(Cipher.DECRYPT_MODE, sk); System.out.println(new String(cipher1.doFinal(mcontent))); }
2》AES算法 (Advanced Encryptin Standard 高級加密標準) 是對稱加密算法一種升級 由於 56位祕鑰 在計算機系統性能愈來愈高的前提下 56位很容易被
破解 因此 AES將祕鑰的長度提升到128, 192 or 256 必須是這三個數 128默承認以使用 192和256因爲美國限制 須要相關受權 不然拋出異常
public static final String AL="AES"; /** * 生成56字節的祕鑰 */ public static SecretKey genKey(int len) throws NoSuchAlgorithmException{ KeyGenerator kg=KeyGenerator.getInstance(AL); kg.init(len); return kg.generateKey(); } public static void main(String[] args) throws Exception { //SecretKey sk=new SecretKeySpec(kl.getBytes(), "DES"); SecretKey sk=genKey(128); //---------加密 String password="tiger"; Cipher cipher=Cipher.getInstance(AL); cipher.init(Cipher.ENCRYPT_MODE, sk); //被加密以後獲取的字節數組 byte[] mcontent=cipher.doFinal(password.getBytes()); //---------解密 Cipher cipher1=Cipher.getInstance(AL); cipher1.init(Cipher.DECRYPT_MODE, sk); System.out.println(new String(cipher1.doFinal(mcontent))); }
3》PBE算法(Password Base Encryption) 基於自定義口令的加解密算法 定義口令 同時還必須定義 鹽和 使用鹽混淆的次數
加解密過程當中 該三個參數都必須一致
//鹽 用於將明文進行屢次混淆 static byte[] salt = new byte[8]; static Random r = new Random(); static int saltCount=100; static{ r.nextBytes(salt); } public static final String AL="PBEWithMD5AndDES"; /** * 生成自定義口令的祕鑰 */ public static SecretKey genKey(String kl) throws Exception{ char[] klChar=kl.toCharArray(); PBEKeySpec pbe=new PBEKeySpec(klChar); SecretKeyFactory skf=SecretKeyFactory.getInstance(AL); return skf.generateSecret(pbe); } /** * 使用口令和鹽進行加密 */ public static byte[] encrypt(SecretKey key,byte[] src) throws Exception{ Cipher cipher=Cipher.getInstance(AL); //使用口令 鹽(100次混淆) PBEParameterSpec parameter=new PBEParameterSpec(salt, saltCount); cipher.init(Cipher.ENCRYPT_MODE, key,parameter); //被加密以後獲取的字節數組 byte[] mcontent=cipher.doFinal(src); return mcontent; } /** * 使用口令和鹽進行解密 鹽和口令和混淆的次數都必須和加密以前一致 */ public static byte[] decrypt(SecretKey key,byte[] src) throws Exception{ Cipher cipher=Cipher.getInstance(AL); //使用口令 鹽(100次混淆) PBEParameterSpec parameter=new PBEParameterSpec(salt, saltCount); cipher.init(Cipher.DECRYPT_MODE, key,parameter); //被加密以後獲取的字節數組 byte[] mcontent=cipher.doFinal(src); return mcontent; } public static void main(String[] args) throws Exception { //SecretKey sk=new SecretKeySpec(kl.getBytes(), "DES"); SecretKey sk=genKey("123456"); //---------加密 String password="tiger"; byte[] mw=encrypt(sk, password.getBytes()); //---------解密 System.out.println(new String(decrypt(sk, mw))); }
四。非對稱加密
1》DH算法 是一種對稱加密到非對稱加密的過分算法 使用DH算法生成密鑰對 使用對稱加密算法獲取祕鑰後 進行加解密 雙方必須都存在公鑰和私鑰
模型分析
咱們以消息傳遞模型爲例,甲方做爲發送者,乙方做爲接受者,分述甲乙雙方如何構建密鑰、交互密鑰和加密數據。
首先,甲乙雙方須要在收發消息前構建本身的密鑰對,如圖1所示。
甲乙雙方構建密鑰須要通過如下幾個步驟:
1)由消息發送的一方構建密鑰,這裏由甲方構建密鑰。
2)由構建密鑰的一方向對方公佈其公鑰,這裏由甲方向乙方發佈公鑰。
3)由消息接收的一方經過對方公鑰構建自身密鑰,這裏由乙方使用甲方公鑰構建乙方密鑰。
4)由消息接收的一方向對方公佈其公鑰,這裏由乙方向甲方公佈公鑰。
這裏要注意的是,乙方構建本身密鑰對的時候須要使用甲方公鑰做爲參數這是很關鍵的一點,若是缺乏了這一環節則沒法確保甲乙雙方得到同一個密鑰,消息加密更無從談起。
其次,假設甲乙雙方事先約定好了用於數據加密的對稱加密算法(如AES算法),並構建本地密鑰(即對稱加密算法中的密鑰),如圖2所示。
甲方須要使用本身的私鑰和乙方的公鑰才能構建本身的本地密鑰,乙方須要使用本身的私鑰和甲方的公鑰才能構建本身的本地密鑰。
雖然甲乙雙方使用了不一樣的密鑰來構建本地密鑰,可是甲乙兩方獲得的密鑰實際上是一致的,後面的demo能夠證實,也正是基於此,甲乙雙方纔能順利地進行加密消息的傳送。
最後,甲乙雙方構建了本地密鑰後,可按照基於對稱加密算法的消息傳遞模型完成消息傳遞。如圖4所示。
做爲對稱加密體制向非對稱加密體制的一種過渡,DH算法僅僅比通常的對稱加密算法多了密鑰對的構建和本地密鑰的構建這兩項操做,而真正的數據加密/解密操做仍由對稱加密算法完成。
測試代碼:
public static void main(String[] args) throws Exception { // 生成DH密鑰對 KeyPairGenerator kpg=KeyPairGenerator.getInstance("DH"); kpg.initialize(512); KeyPair kp=kpg.generateKeyPair(); PublicKey pk=kp.getPublic(); PrivateKey prk=kp.getPrivate(); //實例化 KeyAgreement keyAgree = KeyAgreement.getInstance("DH"); //初始化 keyAgree.init(prk); keyAgree.doPhase(pk, true); //生成本地密鑰 SecretKey secretKey = keyAgree.generateSecret("DES"); //仍是使用對稱方式加解密 //使用本地祕鑰加密 Cipher cip=Cipher.getInstance("DES"); cip.init(Cipher.ENCRYPT_MODE, secretKey); byte[] mw=cip.doFinal("test".getBytes()); //使用本地祕鑰解密 Cipher cip1=Cipher.getInstance("DES"); cip1.init(Cipher.DECRYPT_MODE, secretKey); System.out.println(new String(cip1.doFinal(mw))); }
dh算法過程:
package alao; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.KeyAgreement; import javax.crypto.SecretKey; import javax.crypto.interfaces.DHPublicKey; import javax.crypto.spec.DHParameterSpec; public class DH { public static KeyPair genernateKeyPair() throws NoSuchAlgorithmException { KeyPairGenerator kpg=KeyPairGenerator.getInstance("DH"); kpg.initialize(512); KeyPair kp=kpg.generateKeyPair(); return kp; } public static byte[] encrypt(SecretKey secretKey,byte[] data) throws Exception { // 數據加密 Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm()); cipher.init(Cipher.ENCRYPT_MODE, secretKey); return cipher.doFinal(data); } public static byte[] decrypt(SecretKey secretKey,byte[] data) throws Exception { // 數據加密 Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm()); cipher.init(Cipher.DECRYPT_MODE, secretKey); return cipher.doFinal(data); } public static void main(String[] args) throws Exception { //生成A用戶的公私鑰對 KeyPair apair=genernateKeyPair(); PublicKey apubKey=apair.getPublic(); System.out.println("B接受到A的公鑰:"+Base64.getEncoder().encodeToString(apubKey.getEncoded())); //生成B用戶的公私鑰對 //A和B協商生成對象加密的祕鑰過程 //1 A發送公鑰給B 模擬獲取變量 傳送b過程 getEncode獲取byte數組後 轉換base64 傳送 DHParameterSpec dhParameterSpec = ((DHPublicKey)apair.getPublic()).getParams(); KeyPairGenerator kpg=KeyPairGenerator.getInstance("DH"); kpg.initialize(dhParameterSpec);//傳入A的公鑰 KeyPair bpair=kpg.generateKeyPair(); //2 B經過A的公鑰和本身的公私鑰 生成祕鑰 //實例化 KeyAgreement keyAgree = KeyAgreement.getInstance("DH"); //傳入B的私鑰和A的公鑰 keyAgree.init(bpair.getPrivate()); keyAgree.doPhase(apair.getPublic(), true); //生成本地密鑰 SecretKey secretKey = keyAgree.generateSecret("DES"); System.out.println("此時A生成了祕鑰:"+Base64.getEncoder().encodeToString(secretKey.getEncoded())); //4 A經過B的公鑰和本身的公私鑰 生成祕鑰 //實例化 keyAgree = KeyAgreement.getInstance("DH"); //傳入A的私鑰和B的公鑰 keyAgree.init(apair.getPrivate()); keyAgree.doPhase(bpair.getPublic(), true); //生成本地密鑰 SecretKey secretKey1 = keyAgree.generateSecret("DES"); System.out.println("此時B生成了祕鑰:"+Base64.getEncoder().encodeToString(secretKey1.getEncoded())); byte[] bbb=encrypt(secretKey, "hello".getBytes()); System.out.println(new String(decrypt(secretKey1, bbb))); } }
2》RSA算法 目前影響力最大的非對稱加密算法 通常公鑰對外公開 加密後傳送給服務器 服務器使用獨有的私鑰解密(固然也能夠私鑰加密 公鑰解密 通常不這樣 由於誰都有公鑰都能解密 加密就沒有意義了) 加密的數據在傳輸過程是 沒法破解的 祕鑰對初始化大小必須是64的倍數 實際值 只能在512-1024中
public static void main(String[] args) throws Exception { // 生成RSA密鑰對 KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA"); kpg.initialize(512); KeyPair kp=kpg.generateKeyPair(); PublicKey pk=kp.getPublic(); PrivateKey prk=kp.getPrivate(); //公鑰加密 Cipher cip=Cipher.getInstance("RSA"); cip.init(Cipher.ENCRYPT_MODE, pk); byte[] mw=cip.doFinal("test".getBytes()); //私鑰解密 Cipher cip1=Cipher.getInstance("RSA"); cip1.init(Cipher.DECRYPT_MODE, prk); System.out.println(new String(cip1.doFinal(mw))); }
RSA算法中 通常公鑰和私鑰都均可以調用getEncoded()轉換爲byte數組 使用base64編碼後 存儲在文件中 方便公鑰分發 讀取文件後須要轉換對應
的公鑰和私鑰的方法爲:
/** * 讀取公鑰字節數組轉換爲對象 * @throws Exception */ public PublicKey getPub(byte[] bt) throws Exception{ X509EncodedKeySpec x=new X509EncodedKeySpec(bt); KeyFactory fac=KeyFactory.getInstance("RSA"); return fac.generatePublic(x); } /** * 讀取私鑰字節數組轉換爲對象 * @throws Exception */ public PrivateKey getPri(byte[] bt) throws Exception{ PKCS8EncodedKeySpec x=new PKCS8EncodedKeySpec(bt); KeyFactory fac=KeyFactory.getInstance("RSA"); return fac.generatePrivate(x); }
五。數據簽名(DSA)
簽名是非對稱加密技術和摘要技術的綜合運用用戶A將明文和使用私鑰加密的明文摘要一塊兒發送給用戶B 用戶B使用公鑰解密出摘要 而後使用相同的摘要
算法將明文摘要 將兩個摘要字符串比較若是相等 則代表內容沒有被篡改
原理實現過程以下
public static void main(String[] args) throws Exception { //獲取公鑰和私鑰 KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA"); kpg.initialize(512); KeyPair kp=kpg.generateKeyPair(); PublicKey pk=kp.getPublic(); PrivateKey prk=kp.getPrivate(); //使用私鑰簽名 String message="hello my name is jiaozi"; //返回的byte就能夠進行傳輸 byte[] srcByte=sign(message.getBytes(),prk); //假設這裏模擬篡改數據 確定會出現異常 或者檢驗不經過 srcByte[9]=10; //存在公鑰的用戶 接受到該srcByte 就能夠驗證是否被篡改 System.out.println(verify(srcByte,pk)); } /** * 簽名過程 */ public static byte[] sign(byte[] content,PrivateKey pk) throws Exception{ //對明文進行摘要 MessageDigest md=MessageDigest.getInstance("MD5"); byte[] zy=md.digest(content); //對摘要進行加密 Cipher cp=Cipher.getInstance("RSA"); cp.init(Cipher.ENCRYPT_MODE, pk); byte[] enZy=cp.doFinal(zy); //要一塊兒傳送的數據 雙方約定好使用Map Map map=new HashMap(); map.put("content", content); map.put("enZy", enZy); //傳輸過程使用byte數組 這裏使用序列化將對象打包轉換爲字節數組 ByteArrayOutputStream baos=new ByteArrayOutputStream(); ObjectOutputStream oos=new ObjectOutputStream(baos); oos.writeObject(map); oos.close(); return baos.toByteArray(); } /** * 驗證簽名過程 */ public static boolean verify(byte[] content,PublicKey pk) throws Exception{ //將獲取的數據轉換爲Map ByteArrayInputStream baos=new ByteArrayInputStream(content); ObjectInputStream oos=new ObjectInputStream(baos); Map map=(Map)oos.readObject(); oos.close(); //獲取到明文和加密的摘要信息 byte[] srcContent=(byte[])map.get("content"); byte[] enZy=(byte[])map.get("enZy"); //使用相同的摘要算法 將明文摘要 MessageDigest md=MessageDigest.getInstance("MD5"); byte[] contentZy=md.digest(srcContent); //將加密的摘要解密 Cipher cp=Cipher.getInstance("RSA"); cp.init(Cipher.DECRYPT_MODE, pk); byte[] zy=cp.doFinal(enZy); BASE64Encoder bas=new BASE64Encoder(); if(bas.encode(contentZy).equals(bas.encode(zy))){ return true; } return false; }本身實現這個過程略顯複雜 java提供了MD5WithRSA和SHA1WithRAS算法直接實現上述過程
public static void main(String[] args) throws Exception { //獲取公鑰和私鑰 KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA"); kpg.initialize(512); KeyPair kp=kpg.generateKeyPair(); PublicKey pk=kp.getPublic(); PrivateKey prk=kp.getPrivate(); //使用私鑰簽名 String message="hello my name is jiaozi"; //返回的byte和明文就能夠進行傳輸 byte[] hash=sign(message.getBytes(),prk); //存在公鑰的用戶 接受到該srcByte 就能夠驗證是否被篡改 System.out.println(verify(message.getBytes(),hash,pk)); } /** * 簽名過程 返回的是加密的摘要 */ public static byte[] sign(byte[] content,PrivateKey pk) throws Exception{ Signature si=Signature.getInstance("MD5WithRSA");//或者使用SHA1WithRSA si.initSign(pk); si.update(content); return si.sign(); } /** * 驗證簽名過程 content表示原文可能獲取原文的hash hash表示sign函數返回的加密摘要 */ public static boolean verify(byte[] content,byte[] hash,PublicKey pk) throws Exception{ Signature si=Signature.getInstance("MD5WithRSA");//或者使用SHA1WithRSA si.initVerify(pk); si.update(content); return si.verify(hash); }