Java加解密密和數字簽名
阿新 • • 發佈:2018-12-31
最近初步接觸了下Java加密和數字簽名的相關內容,目前學習的主要內容如下:
1)訊息摘要
2)私鑰加密
3)公鑰加密
4)數字簽名
5)數字證書
以下是對這幾步的程式碼例項,程式碼就是我們的語言 哈
1. 訊息摘要
String beforeDegist = "asdf"; System.out.println("摘要前:"+beforeDegist); //初始資訊要轉換成位元組流的形式 byte[] plainText = beforeDegist.getBytes("UTF8"); //使用getInstance("演算法")來獲得訊息摘要,這裡使用SHA-1的160位演算法或者MD5演算法 //MessageDigest messageDigest = MessageDigest.getInstance("SHA-1"); MessageDigest messageDigest = MessageDigest.getInstance("MD5"); //System.out.println("/n" + messageDigest.getProvider().getInfo()); //開始使用演算法 messageDigest.update(plainText); //輸出演算法運算結果 String afterDegist = new String(messageDigest.digest(),"UTF8"); System.out.println("摘要後:"+afterDegist);
2. 私鑰加密
/** * 此例子是對一個字串資訊,用一個私鑰(key)加密,然後在用該私鑰解密,驗證是否一致 * 私鑰加密,是對稱加密 * 使用對稱演算法。比如:A用一個金鑰對一個檔案加密,而B讀取這個檔案的話,則需要和A一樣的金鑰,雙方共享一 * 個私鑰(而在web環境下,私鑰在傳遞時容易被偵聽) * * 附:主要對稱演算法有:DES(實際金鑰只用到56 位) * AES(支援三種金鑰長度:128、192、256位),通常首先128位,其他的還有DESede等 */ String before = "asdf"; byte[] plainText = before.getBytes("UTF8"); // STEP 1.
System.out.println("Start generate AES key."); //得到一個使用AES演算法的KeyGenerator的例項 KeyGenerator keyGen = KeyGenerator.getInstance("AES"); //定義金鑰長度128位 keyGen.init(128); //通過KeyGenerator產生一個key(金鑰演算法剛才已定義,為AES) Key key = keyGen.generateKey(); System.out.println("Finish generating AES key="+key);
//STEP 2.
//獲得一個私鑰加密類Cipher,定義Cipher的基本資訊:ECB是加密方式,PKCS5Padding是填充方法
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
//System.out.println("/n" + cipher.getProvider().getInfo());
//STEP 3.
// 使用私鑰加密
System.out.println("/n用私鑰加密...");
// 把剛才生成的key當作引數,初始化使用剛才獲得的私鑰加密類,Cipher.ENCRYPT_MODE意思是加密
cipher.init(Cipher.ENCRYPT_MODE, key);
//私鑰加密類Cipher進行加密,加密後返回一個位元組流byte[]
byte[] cipherText = cipher.doFinal(plainText);
//以UTF8格式把位元組流轉化為String
String after1 = new String(cipherText, "UTF8");
System.out.println("用私鑰加密完成:"+after1);
// STEP 4.
//使用私鑰對剛才加密的資訊進行解密,看看是否一致,Cipher.DECRYPT_MODE意思是解金鑰
System.out.println("/n用私鑰解密...");
cipher.init(Cipher.DECRYPT_MODE, key);
//對剛才私鑰加密的位元組流進行解密,解密後返回一個位元組流byte[]
byte[] newPlainText = cipher.doFinal(cipherText);
String after2 = new String(newPlainText, "UTF8");
System.out.println("用私鑰解密完成:"+after2);
3. 公鑰加密
String before = "asdf";
byte[] plainText = before.getBytes("UTF8");
//產生一個RSA金鑰生成器KeyPairGenerator(顧名思義:一對鑰匙生成器)
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
//定義金鑰長度1024位
keyGen.initialize(1024);
//通過KeyPairGenerator產生金鑰,注意:這裡的key是一對鑰匙!!
KeyPair key = keyGen.generateKeyPair();
//獲得一個RSA的Cipher類,使用公鑰加密
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
//System.out.println("/n" + cipher.getProvider().getInfo());
System.out.println("/n用公鑰加密...");
//Cipher.ENCRYPT_MODE意思是加密,從一對鑰匙中得到公鑰 key.getPublic()
cipher.init(Cipher.ENCRYPT_MODE, key.getPublic());
//用公鑰進行加密,返回一個位元組流
byte[] cipherText = cipher.doFinal(plainText);
//以UTF8格式把位元組流轉化為String
String after1 = new String(cipherText, "UTF8");
System.out.println("用公鑰加密完成:"+after1);
//使用私鑰解密
System.out.println("/n用私鑰解密...");
//Cipher.DECRYPT_MODE意思是解密模式,從一對鑰匙中得到私鑰 key.getPrivate()
cipher.init(Cipher.DECRYPT_MODE, key.getPrivate());
//用私鑰進行解密,返回一個位元組流
byte[] newPlainText = cipher.doFinal(cipherText);
String after2 = new String(newPlainText, "UTF8");
System.out.println("用私鑰解密完成:"+after2);
4. 數字簽名
/**
* 此例子是數字簽名的例子,使用RSA私鑰對訊息摘要(這裡指的是原始資料)進行簽名,然後使用公鑰驗證簽名
*
* A通過使用B的公鑰加密資料後發給B,B利用B的私鑰解密就得到了需要的資料(進過B公鑰加密的資料只有B的私鑰能夠
* 解開,C沒有B的私鑰,所以C解不開,但C可以使用B的公鑰加密一份資料發給B,這樣一來,問題來了,B收到的資料到
* 底是A發過來的還是C發過來的呢)
* 由於私鑰是唯一的,那麼A就可以利用A自己的私鑰進行加密,然後B再利用A的公鑰來解密,就可以確定:一定是A的消
* 息,數字簽名的原理就基於此
*
* 總結:A想將目標資料傳給B,此時A需要準備1和2兩部分
* 1:A使用B的公鑰將原始資訊加密,以起到保密作用(只有B的私鑰能解開,其他人使用其他鑰匙都解不開,當然就保密咯)
* 2:A使用A的私鑰將原始資訊的摘要進行簽名,以起到接收方B確定是A發過來的作用(A用A的私鑰對目標資料的摘要進行籤
* 名,然後傳給B,同時,C用C的私鑰對任意資訊進行簽名也傳給B,B想接受的是A的資料(比如說一個轉帳請求),於是B
* 就通過A的公鑰對接受到的兩個資訊進行解密,解開的就是A(A的公鑰能且只能解開A的私鑰加密的資料))
*/
String before = "asdf";
byte[] plainText = before.getBytes("UTF8");
//形成RSA公鑰對
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair key = keyGen.generateKeyPair();
//使用私鑰簽名**********************************************************
Signature sig = Signature.getInstance("SHA1WithRSA");
sig.initSign(key.getPrivate());//sig物件得到私鑰
//簽名物件得到原始資料
sig.update(plainText);//sig物件得到原始資料(現實中用的是原始資料的摘要,摘要的是單向的,即摘要演算法後無法解密)
byte[] signature = sig.sign();//sig物件用私鑰對原始資料進行簽名,簽名後得到簽名signature
//System.out.println(sig.getProvider().getInfo());
String after1 = new String(signature, "UTF8");
System.out.println("/n用私鑰簽名後:"+after1);
//使用公鑰驗證
key = keyGen.generateKeyPair();
sig.initVerify(key.getPublic());//sig物件得到公鑰
//簽名物件得到原始資訊
sig.update(plainText);//sig物件得到原始資料(現實中是摘要)
try {
if (sig.verify(signature)) {//sig物件用公鑰解密簽名signature得到原始資料(即摘要),一致則true
System.out.println("簽名驗證正確!!"+new String(plainText, "UTF8"));
} else {
System.out.println("簽名驗證失敗!!");
}
} catch (SignatureException e) {
System.out.println("簽名驗證失敗!!");
}
5. 數字證書
/**
* 此例是對“數字證書”檔案的操作
* java平臺(在機器上安裝jdk)為你提供了金鑰庫(證書庫),cmd下提供了keytool命令就可以建立證書庫
*
* 在執行此例前:
* 在c盤目錄下建立一個證書,指定證書庫為BocsoftKeyLib,建立別名為TestCertification的一條證書,它指定用
* RSA 演算法生成,且指定金鑰長度為1024,證書有效期為1年
* 匯出證書檔案為TC.cer已存於本地磁碟C:/
* 密碼是qazzaq
*/
try {
//前提:將證書庫中的一條證書匯出到證書檔案(我寫的例子裡證書檔案叫TC.cer)
//從證書檔案TC.cer裡讀取證書資訊
CertificateFactory cf = CertificateFactory.getInstance("X.509");
FileInputStream in = new FileInputStream("C:/TC.cer");
//將檔案以檔案流的形式讀入證書類Certificate中
Certificate c = cf.generateCertificate(in);
System.err.println("轉換成String後的證書資訊:"+c.toString());*/
//或者不用上面程式碼的方法,直接從證書庫中讀取證書資訊,和上面的結果一摸一樣
String pass="qazzaq";
FileInputStream in2=new FileInputStream("C:/BocsoftKeyLib");
KeyStore ks=KeyStore.getInstance("JKS");
ks.load(in2,pass.toCharArray());
String alias = "TestCertification"; //alias為條目的別名
Certificate c=ks.getCertificate(alias);
System.err.println("轉換成String後的證書資訊:"+c.toString());
//獲取獲取X509Certificate型別的物件,這是證書類獲取Certificate的子類,實現了更多方法
X509Certificate t=(X509Certificate)c;
//從資訊中提取需要資訊
System.out.println("版本號:"+t.getVersion());
System.out.println("序列號:"+t.getSerialNumber().toString(16));
System.out.println("主體名:"+t.getSubjectDN());
System.out.println("簽發者:"+t.getIssuerDN());
System.out.println("有效期:"+t.getNotBefore());
System.out.println("簽名演算法:"+t.getSigAlgName());
byte [] sig=t.getSignature();//簽名值
PublicKey pk = t.getPublicKey();
byte [] pkenc=pk.getEncoded();
System.out.println("公鑰:");
for(int i=0;i<pkenc.length;i++){
System.out.print(pkenc[i]+",");
}
System.err.println();
//證書的日期有效性檢查,頒發的證書都有一個有效性的日期區間
Date TimeNow=new Date();
t.checkValidity(TimeNow);
System.out.println("證書的日期有效性檢查:有效的證書日期!");
//驗證證書籤名的有效性,通過數字證書認證中心(CA)機構頒佈給客戶的CA證書,比如:caroot.crt檔案
//我手裡沒有CA頒給我的證書,所以下面程式碼執行不了
/*FileInputStream in3=new FileInputStream("caroot.crt");
//獲取CA證書
Certificate cac = cf.generateCertificate(in3);
//獲取CA的公鑰
PublicKey pbk=cac.getPublicKey();
//c為本地證書,也就是待檢驗的證書,用CA的公鑰校驗數字證書c的有效性
c.verify(pbk);
} catch(CertificateExpiredException e){//證書的日期有效性檢查:過期
System.out.println("證書的日期有效性檢查:過期");
} catch(CertificateNotYetValidException e){ //證書的日期有效性檢查:尚未生效
System.out.println("證書的日期有效性檢查:尚未生效");
} catch (CertificateException ce) {
ce.printStackTrace();
} catch (FileNotFoundException fe) {
fe.printStackTrace();
} /*catch (IOException ioe){
} catch (KeyStoreException kse){
}*/ catch (Exception e){
e.printStackTrace();
}
}