Java加密解密和數(shù)字簽名完整代碼示例
常見的加密算法
基本的單向加密算法:
BASE64嚴(yán)格地說,屬于編碼格式,而非加密算法
MD5(MessageDigestalgorithm5,信息摘要算法)
SHA(SecureHashAlgorithm,安全散列算法)
HMAC(HashMessageAuthenticationCode,散列消息鑒別碼)
復(fù)雜的對稱加密(DES、PBE)、非對稱加密算法:
DES(DataEncryptionStandard,數(shù)據(jù)加密算法)
PBE(Password-basedencryption,基于密碼驗(yàn)證)
RSA(算法的名字以發(fā)明者的名字命名:RonRivest,AdiShamir和LeonardAdleman)
DH(Diffie-Hellman算法,密鑰一致協(xié)議)
DSA(DigitalSignatureAlgorithm,數(shù)字簽名)
ECC(EllipticCurvesCryptography,橢圓曲線密碼編碼學(xué))
數(shù)字簽名
算法簡述
數(shù)字簽名算法可以看做是一種帶有密鑰的消息摘要算法,并且這種密鑰包含了公鑰和私鑰。也就是說,數(shù)字簽名算法是非對稱加密算法和消息摘要算法的結(jié)合體。
特點(diǎn)
數(shù)字簽名算法要求能夠驗(yàn)證數(shù)據(jù)完整性、認(rèn)證數(shù)據(jù)來源,并起到抗否認(rèn)的作用。
原理
數(shù)字簽名算法包含簽名和驗(yàn)證兩項操作,遵循私鑰簽名,公鑰驗(yàn)證的方式。
簽名時要使用私鑰和待簽名數(shù)據(jù),驗(yàn)證時則需要公鑰、簽名值和待簽名數(shù)據(jù),其核心算法主要是消息摘要算法。
1. 消息摘要
String beforeDegist = "asdf";
System.out.println("摘要前:"+beforeDegist);
//初始信息要轉(zhuǎn)換成字節(jié)流的形式
byte[] plainText = beforeDegist.getBytes("UTF8");
//使用getInstance("算法")來獲得消息摘要,這里使用SHA-1的160位算法或者M(jìn)D5算法
geDigest messageDigest = MessageDigest.getInstance("SHA-1");
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
System.out.println("/n" + messageDigest.getProvider().getInfo());
//開始使用算法
messageDigest.update(plainText);
//輸出算法運(yùn)算結(jié)果
String afterDegist = new String(messageDigest.digest(),"UTF8");
System.out.println("摘要后:"+afterDegist);
2. 私鑰加密
/**
* 此例子是對一個字符串信息,用一個私鑰(key)加密,然后在用該私鑰解密,驗(yàn)證是否一致
* 私鑰加密,是對稱加密
* 使用對稱算法。比如:A用一個密鑰對一個文件加密,而B讀取這個文件的話,則需要和A一樣的密鑰,雙方共享一
* 個私鑰(而在web環(huán)境下,私鑰在傳遞時容易被偵聽)
*
* 附:主要對稱算法有:DES(實(shí)際密鑰只用到56 位)
* AES(支持三種密鑰長度:128、192、256位),通常首先128位,其他的還有DESede等
*/
<span style="white-space: pre; "> </span>String before = "asdf";
byte[] plainText = before.getBytes("UTF8");
// STEP 1.
System.out.println("Start generate AES key.");
//得到一個使用AES算法的KeyGenerator的實(shí)例
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
//定義密鑰長度128位
keyGen.init(128);
//通過KeyGenerator產(chǎn)生一個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當(dāng)作參數(shù),初始化使用剛才獲得的私鑰加密類,Cipher.ENCRYPT_MODE意思是加密
cipher.init(Cipher.ENCRYPT_MODE, key);
//私鑰加密類Cipher進(jìn)行加密,加密后返回一個字節(jié)流byte[]
byte[] cipherText = cipher.doFinal(plainText);
//以UTF8格式把字節(jié)流轉(zhuǎn)化為String
String after1 = new String(cipherText, "UTF8");
System.out.println("用私鑰加密完成:"+after1);
// STEP 4.
[java] view plain copy
//使用私鑰對剛才加密的信息進(jìn)行解密,看看是否一致,Cipher.DECRYPT_MODE意思是解密鑰
System.out.println("/n用私鑰解密...");
cipher.init(Cipher.DECRYPT_MODE, key);
//對剛才私鑰加密的字節(jié)流進(jìn)行解密,解密后返回一個字節(jié)流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");
//產(chǎn)生一個RSA密鑰生成器KeyPairGenerator(顧名思義:一對鑰匙生成器)
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
//定義密鑰長度1024位
keyGen.initialize(1024);
//通過KeyPairGenerator產(chǎn)生密鑰,注意:這里的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());
//用公鑰進(jìn)行加密,返回一個字節(jié)流
byte[] cipherText = cipher.doFinal(plainText);
//以UTF8格式把字節(jié)流轉(zhuǎn)化為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());
//用私鑰進(jìn)行解密,返回一個字節(jié)流
byte[] newPlainText = cipher.doFinal(cipherText);
String after2 = new String(newPlainText, "UTF8");
System.out.println("用私鑰解密完成:"+after2);
4. 數(shù)字簽名
/**
* 此例子是數(shù)字簽名的例子,使用RSA私鑰對消息摘要(這里指的是原始數(shù)據(jù))進(jìn)行簽名,然后使用公鑰驗(yàn)證簽名
*
* A通過使用B的公鑰加密數(shù)據(jù)后發(fā)給B,B利用B的私鑰解密就得到了需要的數(shù)據(jù)(進(jìn)過B公鑰加密的數(shù)據(jù)只有B的私鑰能夠
* 解開,C沒有B的私鑰,所以C解不開,但C可以使用B的公鑰加密一份數(shù)據(jù)發(fā)給B,這樣一來,問題來了,B收到的數(shù)據(jù)到
* 底是A發(fā)過來的還是C發(fā)過來的呢)
* 由于私鑰是唯一的,那么A就可以利用A自己的私鑰進(jìn)行加密,然后B再利用A的公鑰來解密,就可以確定:一定是A的消
* 息,數(shù)字簽名的原理就基于此
*
* 總結(jié):A想將目標(biāo)數(shù)據(jù)傳給B,此時A需要準(zhǔn)備1和2兩部分
* 1:A使用B的公鑰將原始信息加密,以起到保密作用(只有B的私鑰能解開,其他人使用其他鑰匙都解不開,當(dāng)然就保密咯)
* 2:A使用A的私鑰將原始信息的摘要進(jìn)行簽名,以起到接收方B確定是A發(fā)過來的作用(A用A的私鑰對目標(biāo)數(shù)據(jù)的摘要進(jìn)行簽
* 名,然后傳給B,同時,C用C的私鑰對任意信息進(jìn)行簽名也傳給B,B想接受的是A的數(shù)據(jù)(比如說一個轉(zhuǎn)帳請求),于是B
* 就通過A的公鑰對接受到的兩個信息進(jìn)行解密,解開的就是A(A的公鑰能且只能解開A的私鑰加密的數(shù)據(jù)))
*/
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對象得到私鑰
//簽名對象得到原始數(shù)據(jù)
sig.update(plainText);
//sig對象得到原始數(shù)據(jù)(現(xiàn)實(shí)中用的是原始數(shù)據(jù)的摘要,摘要的是單向的,即摘要算法后無法解密)
byte[] signature = sig.sign();
//sig對象用私鑰對原始數(shù)據(jù)進(jìn)行簽名,簽名后得到簽名signature
em.out.println(sig.getProvider().getInfo());
String after1 = new String(signature, "UTF8");
System.out.println("/n用私鑰簽名后:"+after1);
//使用公鑰驗(yàn)證
sig.initVerify(key.getPublic());
//sig對象得到公鑰
//簽名對象得到原始信息
sig.update(plainText);
//sig對象得到原始數(shù)據(jù)(現(xiàn)實(shí)中是摘要)
try {
if (sig.verify(signature)) {
//sig對象用公鑰解密簽名signature得到原始數(shù)據(jù)(即摘要),一致則true
System.out.println("簽名驗(yàn)證正確??!"+new String(plainText, "UTF8"));
} else {
System.out.println("簽名驗(yàn)證失?。?!");
}
}
catch (SignatureException e) {
System.out.println("簽名驗(yàn)證失敗??!");
}
5. 數(shù)字證書
/**
* 此例是對“數(shù)字證書”文件的操作
* java平臺(在機(jī)器上安裝jdk)為你提供了密鑰庫(證書庫),cmd下提供了keytool命令就可以創(chuàng)建證書庫
*
* 在運(yùn)行此例前:
* 在c盤目錄下創(chuàng)建一個證書,指定證書庫為BocsoftKeyLib,創(chuàng)建別名為TestCertification的一條證書,它指定用
* RSA 算法生成,且指定密鑰長度為1024,證書有效期為1年
* 導(dǎo)出證書文件為TC.cer已存于本地磁盤C:/
* 密碼是qazzaq
*/
try {
//前提:將證書庫中的一條證書導(dǎo)出到證書文件(我寫的例子里證書文件叫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("轉(zhuǎn)換成String后的證書信息:"+c.toString());
*/
//或者不用上面代碼的方法,直接從證書庫中讀取證書信息,和上面的結(jié)果一摸一樣
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("轉(zhuǎn)換成String后的證書信息:"+c.toString());
//獲取獲取X509Certificate類型的對象,這是證書類獲取Certificate的子類,實(shí)現(xiàn)了更多方法
X509Certificate t=(X509Certificate)c;
//從信息中提取需要信息
System.out.println("版本號:"+t.getVersion());
System.out.println("序列號:"+t.getSerialNumber().toString(16));
System.out.println("主體名:"+t.getSubjectDN());
System.out.println("簽發(fā)者:"+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();
//證書的日期有效性檢查,頒發(fā)的證書都有一個有效性的日期區(qū)間
Date TimeNow=new Date();
t.checkValidity(TimeNow);
System.out.println("證書的日期有效性檢查:有效的證書日期!");
//驗(yàn)證證書簽名的有效性,通過數(shù)字證書認(rèn)證中心(CA)機(jī)構(gòu)頒布給客戶的CA證書,比如:caroot.crt文件
//我手里沒有CA頒給我的證書,所以下面代碼執(zhí)行不了
/*FileInputStream in3=new FileInputStream("caroot.crt");
//獲取CA證書
Certificate cac = cf.generateCertificate(in3);
//獲取CA的公鑰
PublicKey pbk=cac.getPublicKey();
//c為本地證書,也就是待檢驗(yàn)的證書,用CA的公鑰校驗(yàn)數(shù)字證書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();
}
}
總結(jié)
以上就是本文關(guān)于Java加密解密和數(shù)字簽名完整代碼示例的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
相關(guān)文章
java的Builder原理和實(shí)現(xiàn)詳解
大家好,本篇文章主要講的是java的Builder原理和實(shí)現(xiàn)詳解,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽2021-12-12
springboot集成junit編寫單元測試實(shí)戰(zhàn)
在做單元測試時,代碼覆蓋率常常被拿來作為衡量測試好壞的指標(biāo),本文主要介紹了springboot集成junit編寫單元測試實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-02-02
解決SpringAop內(nèi)部調(diào)用時不經(jīng)過代理類的問題
這篇文章主要介紹了解決SpringAop內(nèi)部調(diào)用時不經(jīng)過代理類的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01
Spring Security實(shí)現(xiàn)退出登錄和退出處理器
本文主要介紹了Spring Security實(shí)現(xiàn)退出登錄和退出處理器,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05

