一、摘要算法
1》MD5算法(Message Digest Algorithm 5) 可以保證數據傳輸完整性和一致性 摘要後長度爲16字節 摘要信息中不包含原文信息
所有加密結果不可逆(無法解密) 一般在傳送文件時 對源文件進行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);
}
二、編碼和解碼
1》16進制 編碼 計算機系統使用 2進制 爲了編寫存儲方便一般將2進制 轉換爲16進制字符串 其中base64也是其中類似轉換一種 16進制編碼和base64都是
可逆的 一般用於存儲
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);
}