Java對(duì)稱與非對(duì)稱加密算法原理詳細(xì)講解
一、對(duì)稱加密算法
1.概述
對(duì)稱加密算法就是傳統(tǒng)的用一個(gè)密碼進(jìn)行加密和解密。例如,我們常用的 WinZIP 和 WinRAR 對(duì)壓縮包 的加密和解密,就是使用對(duì)稱加密算法。
從程序的角度看,所謂加密,就是這樣一個(gè)函數(shù):
它接收密碼和明文,然后輸出密文: secret = encrypt(key, message);
而解密則相反,它接收密碼和密文,然后輸出明文: plain = decrypt(key, secret)。
2.常用的對(duì)稱加密算法
密鑰長(zhǎng)度直接決定加密強(qiáng)度,而工作模式和填充模式可以看成是對(duì)稱加密算法的參 數(shù)和格式選擇。Java標(biāo)準(zhǔn)庫(kù)提供的算法實(shí)現(xiàn)并不包括所有的工作模式和所有填充模式,但是通常我們只需要挑選常用的使用就可以了。
注意:DES 算法由于密鑰過(guò)短,現(xiàn)在已經(jīng)不安全了
3.AES加密
AES 算法是目前應(yīng)用最廣泛的加密算法。比較常見(jiàn)的工作模式是 ECB 和 CBC。
①ECB模式
import java.security.*; import java.util.Base64; import javax.crypto.*; import javax.crypto.spec.*; public class Main { public static void main(String[] args) throws Exception { // 原文: String message = "Hello, world!"; System.out.println("Message(原始信息): " + message); // 128位密鑰 = 16 bytes Key: byte[] key = "1234567890abcdef".getBytes(); // 加密: byte[] data = message.getBytes(); byte[] encrypted = encrypt(key, data); System.out.println("Encrypted(加密內(nèi)容): " + Base64.getEncoder().encodeToString(encrypted)); // 解密: byte[] decrypted = decrypt(key, encrypted); System.out.println("Decrypted(解密內(nèi)容): " + new String(decrypted)); } // 加密: public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException { // 創(chuàng)建密碼對(duì)象,需要傳入算法/工作模式/填充模式 Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // 根據(jù)key的字節(jié)內(nèi)容,"恢復(fù)"秘鑰對(duì)象 SecretKey keySpec = new SecretKeySpec(key, "AES"); // 初始化秘鑰:設(shè)置加密模式ENCRYPT_MODE cipher.init(Cipher.ENCRYPT_MODE, keySpec); // 根據(jù)原始內(nèi)容(字節(jié)),進(jìn)行加密 return cipher.doFinal(input); } // 解密: public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException { // 創(chuàng)建密碼對(duì)象,需要傳入算法/工作模式/填充模式 Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // 根據(jù)key的字節(jié)內(nèi)容,"恢復(fù)"秘鑰對(duì)象 SecretKey keySpec = new SecretKeySpec(key, "AES"); // 初始化秘鑰:設(shè)置解密模式DECRYPT_MODE cipher.init(Cipher.DECRYPT_MODE, keySpec); // 根據(jù)原始內(nèi)容(字節(jié)),進(jìn)行解密 return cipher.doFinal(input); } }
②CBC模式
ECB 模式是最簡(jiǎn)單的 AES 加密模式,它只需要一個(gè)固定長(zhǎng)度的密鑰,固定的明文會(huì)生成固定的密文, 這種一對(duì)一的加密方式會(huì)導(dǎo)致安全性降低,更好的方式是通過(guò) CBC 模式,它需要一個(gè)隨機(jī)數(shù)作為 IV 參 數(shù),這樣對(duì)于同一份明文,每次生成的密文都不同:
package com.apesource.demo04; import java.security.*; import java.util.Base64; import javax.crypto.*; import javax.crypto.spec.*; public class Main { public static void main(String[] args) throws Exception { // 原文: String message = "Hello, world!"; System.out.println("Message(原始信息): " + message); // 256位密鑰 = 32 bytes Key: byte[] key = "1234567890abcdef1234567890abcdef".getBytes(); // 加密: byte[] data = message.getBytes(); byte[] encrypted = encrypt(key, data); System.out.println("Encrypted(加密內(nèi)容): " + Base64.getEncoder().encodeToString(encrypted)); // 解密: byte[] decrypted = decrypt(key, encrypted); System.out.println("Decrypted(解密內(nèi)容): " + new String(decrypted)); } // 加密: public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException { // 設(shè)置算法/工作模式CBC/填充 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // 恢復(fù)秘鑰對(duì)象 SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); // CBC模式需要生成一個(gè)16 bytes的initialization vector: SecureRandom sr = SecureRandom.getInstanceStrong(); byte[] iv = sr.generateSeed(16); // 生成16個(gè)字節(jié)的隨機(jī)數(shù) System.out.println(Arrays.toString(iv)); IvParameterSpec ivps = new IvParameterSpec(iv); // 隨機(jī)數(shù)封裝成IvParameterSpec參數(shù)對(duì)象 // 初始化秘鑰:操作模式、秘鑰、IV參數(shù) cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivps); // 加密 byte[] data = cipher.doFinal(input); // IV不需要保密,把IV和密文一起返回: return join(iv, data); } // 解密: public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException { // 把input分割成IV和密文: byte[] iv = new byte[16]; byte[] data = new byte[input.length - 16]; System.arraycopy(input, 0, iv, 0, 16); // IV System.arraycopy(input, 16, data, 0, data.length); //密文 System.out.println(Arrays.toString(iv)); // 解密: Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // 密碼對(duì)象 SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); // 恢復(fù)秘鑰 IvParameterSpec ivps = new IvParameterSpec(iv); // 恢復(fù)IV // 初始化秘鑰:操作模式、秘鑰、IV參數(shù) cipher.init(Cipher.DECRYPT_MODE, keySpec, ivps); // 解密操作 return cipher.doFinal(data); } // 合并數(shù)組 public static byte[] join(byte[] bs1, byte[] bs2) { byte[] r = new byte[bs1.length + bs2.length]; System.arraycopy(bs1, 0, r, 0, bs1.length); System.arraycopy(bs2, 0, r, bs1.length, bs2.length); return r; } }
在 CBC 模式下,需要一個(gè)隨機(jī)生成的 16 字節(jié)IV參數(shù),必須使用 SecureRandom 生 成。因?yàn)槎嗔艘粋€(gè) IvParameterSpec 實(shí)例,因此,初始化方法需要調(diào)用 Cipher 的一個(gè) 重載方法并傳入 IvParameterSpec 。 觀察輸出,可以發(fā)現(xiàn)每次生成的 IV 不同,密文也不同。
二、秘鑰交換算法
使用Java實(shí)現(xiàn)DH算法:
import java.math.BigInteger; import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.X509EncodedKeySpec; import javax.crypto.KeyAgreement; public class Main04 { public static void main(String[] args) { // Bob和Alice: Person bob = new Person("Bob"); Person alice = new Person("Alice"); // 各自生成KeyPair: 公鑰+私鑰 bob.generateKeyPair(); alice.generateKeyPair(); // 雙方交換各自的PublicKey(公鑰): // Bob根據(jù)Alice的PublicKey生成自己的本地密鑰(共享公鑰): bob.generateSecretKey(alice.publicKey.getEncoded()); // Alice根據(jù)Bob的PublicKey生成自己的本地密鑰(共享公鑰): alice.generateSecretKey(bob.publicKey.getEncoded()); // 檢查雙方的本地密鑰是否相同: bob.printKeys(); alice.printKeys(); // 雙方的SecretKey相同,后續(xù)通信將使用SecretKey作為密鑰進(jìn)行AES加解密... } } // 用戶類 class Person { public final String name; // 姓名 // 密鑰 public PublicKey publicKey; // 公鑰 private PrivateKey privateKey; // 私鑰 private byte[] secretKey; // 本地秘鑰(共享密鑰) // 構(gòu)造方法 public Person(String name) { this.name = name; } // 生成本地KeyPair:(公鑰+私鑰) public void generateKeyPair() { try { // 創(chuàng)建DH算法的“秘鑰對(duì)”生成器 KeyPairGenerator kpGen = KeyPairGenerator.getInstance("DH"); kpGen.initialize(512); // 生成一個(gè)"密鑰對(duì)" KeyPair kp = kpGen.generateKeyPair(); this.privateKey = kp.getPrivate(); // 私鑰 this.publicKey = kp.getPublic(); // 公鑰 } catch (GeneralSecurityException e) { throw new RuntimeException(e); } } // 按照 "對(duì)方的公鑰" => 生成"共享密鑰" public void generateSecretKey(byte[] receivedPubKeyBytes) { try { // 從byte[]恢復(fù)PublicKey: X509EncodedKeySpec keySpec = new X509EncodedKeySpec(receivedPubKeyBytes); // 根據(jù)DH算法獲取KeyFactory KeyFactory kf = KeyFactory.getInstance("DH"); // 通過(guò)KeyFactory創(chuàng)建公鑰 PublicKey receivedPublicKey = kf.generatePublic(keySpec); // 生成本地密鑰(共享公鑰) KeyAgreement keyAgreement = KeyAgreement.getInstance("DH"); keyAgreement.init(this.privateKey); // 初始化"自己的PrivateKey" keyAgreement.doPhase(receivedPublicKey, true); // 根據(jù)"對(duì)方的PublicKey" // 生成SecretKey本地密鑰(共享公鑰) this.secretKey = keyAgreement.generateSecret(); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } } public void printKeys() { System.out.printf("Name: %s\n", this.name); System.out.printf("Private key: %x\n", new BigInteger(1, this.privateKey.getEncoded())); System.out.printf("Public key: %x\n", new BigInteger(1, this.publicKey.getEncoded())); System.out.printf("Secret key: %x\n", new BigInteger(1, this.secretKey)); } }
DH 算法是一種密鑰交換協(xié)議,通信雙方通過(guò)不安全的信道協(xié)商密鑰,然后進(jìn)行對(duì)稱加密傳輸。
三、非對(duì)稱加密算法
1.概述
從 DH 算法我們可以看到,公鑰-私鑰組成的密鑰對(duì)是非常有用的加密方式,因?yàn)楣€是可以公開(kāi)的,而私鑰是完全保密的,由此奠定了非對(duì)稱加密的基礎(chǔ)。
非對(duì)稱加密:加密和解密使用的不是相同的密鑰,只有同一個(gè)公鑰-私鑰對(duì)才能正常加解密。
例如:小明要加密一個(gè)文件發(fā)送給小紅,他應(yīng)該首先向小紅索取她的公鑰,然后, 他用小紅的公鑰加密,把加密文件發(fā)送給小紅,此文件只能由小紅的私鑰解開(kāi),因?yàn)樾?紅的私鑰在她自己手里,所以,除了小紅,沒(méi)有任何人能解開(kāi)此文件。
2.RSA算法
非對(duì)稱加密的典型算法就是 RSA 算法,它是由Ron Rivest,Adi Shamir,Leonard Adleman這三個(gè)人 一起發(fā)明的,所以用他們?nèi)齻€(gè)人的姓氏首字母縮寫(xiě)表示。
import java.math.BigInteger; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import javax.crypto.Cipher; // RSA public class Main { public static void main(String[] args) throws Exception { // 明文: byte[] plain = "Hello, encrypt use RSA".getBytes("UTF-8"); // 創(chuàng)建公鑰/私鑰對(duì): Human alice = new Human("Alice"); // 用Alice的公鑰加密: // 獲取Alice的公鑰,并輸出 byte[] pk = alice.getPublicKey(); System.out.println(String.format("public key(公鑰): %x", new BigInteger(1, pk))); // 使用公鑰加密 byte[] encrypted = alice.encrypt(plain); System.out.println(String.format("encrypted(加密): %x", new BigInteger(1, encrypted))); // 用Alice的私鑰解密: // 獲取Alice的私鑰,并輸出 byte[] sk = alice.getPrivateKey(); System.out.println(String.format("private key(私鑰): %x", new BigInteger(1, sk))); // 使用私鑰解密 byte[] decrypted = alice.decrypt(encrypted); System.out.println("decrypted(解密): " + new String(decrypted, "UTF-8")); } } // 用戶類 class Human { // 姓名 String name; // 私鑰: PrivateKey sk; // 公鑰: PublicKey pk; // 構(gòu)造方法 public Human(String name) throws GeneralSecurityException { // 初始化姓名 this.name = name; // 生成公鑰/私鑰對(duì): KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA"); kpGen.initialize(1024); KeyPair kp = kpGen.generateKeyPair(); this.sk = kp.getPrivate(); this.pk = kp.getPublic(); } // 把私鑰導(dǎo)出為字節(jié) public byte[] getPrivateKey() { return this.sk.getEncoded(); } // 把公鑰導(dǎo)出為字節(jié) public byte[] getPublicKey() { return this.pk.getEncoded(); } // 用公鑰加密: public byte[] encrypt(byte[] message) throws GeneralSecurityException { Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, this.pk); // 使用公鑰進(jìn)行初始化 return cipher.doFinal(message); } // 用私鑰解密: public byte[] decrypt(byte[] input) throws GeneralSecurityException { Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, this.sk); // 使用私鑰進(jìn)行初始化 return cipher.doFinal(input); } }
RSA 算法的密鑰有 256 / 512 / 1024 / 2048 / 4096 等不同的長(zhǎng)度。長(zhǎng)度越長(zhǎng),密碼強(qiáng)度越大,當(dāng)然計(jì)算速度也越慢。
3.非對(duì)稱加密算法的優(yōu)缺點(diǎn)
非對(duì)稱加密的優(yōu)點(diǎn):對(duì)稱加密需要協(xié)商密鑰,而非對(duì)稱加密可以安全地公開(kāi)各自的 公鑰,在N個(gè)人之間通信的時(shí)候:使用非對(duì)稱加密只需要N個(gè)密鑰對(duì),每個(gè)人只管理自己的密鑰對(duì)。而使用對(duì)稱加密需要?jiǎng)t需要N*(N-1)/2個(gè)密鑰,因此每個(gè)人需要管理N-1個(gè)密鑰,密鑰管理難度大,而且非常容易泄漏。
非對(duì)稱加密的缺點(diǎn):運(yùn)算速度非常慢,比對(duì)稱加密要慢很多。
所以,在實(shí)際應(yīng)用的時(shí)候,非對(duì)稱加密總是和對(duì)稱加密一起使用。
四、總結(jié)
- 對(duì)稱加密算法使用同一個(gè)密鑰進(jìn)行加密和解密,常用算法有 DES、AES 和 IDEA 等;
- 對(duì)稱加密算法密鑰長(zhǎng)度由算法設(shè)計(jì)決定, AES 的密鑰長(zhǎng)度是 128 / 192 / 256 位;
- 使用對(duì)稱加密算法需要指定算法名稱、工作模式和填充模式。
- DH算法是一種密鑰交換協(xié)議,通信雙方通過(guò)不安全的信道協(xié)商密鑰,進(jìn)行對(duì)稱加密傳輸;
- 非對(duì)稱加密就是加密和解密使用的不是相同的密鑰,只有同一個(gè)公鑰-私鑰對(duì)才能正常加解密。
到此這篇關(guān)于Java對(duì)稱與非對(duì)稱加密算法實(shí)現(xiàn)詳解的文章就介紹到這了,更多相關(guān)Java對(duì)稱與非對(duì)稱加密算法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- JAVA加密算法- 非對(duì)稱加密算法(DH,RSA)的詳細(xì)介紹
- Java編程實(shí)現(xiàn)非對(duì)稱加密的方法詳解
- java 非對(duì)稱加密算法RSA實(shí)現(xiàn)詳解
- java 非對(duì)稱加密算法DH實(shí)現(xiàn)詳解
- 解決JAVA非對(duì)稱加密不同系統(tǒng)加密結(jié)果不一致的問(wèn)題
- Java 實(shí)現(xiàn)常見(jiàn)的非對(duì)稱加密算法
- 教你用Java實(shí)現(xiàn)RSA非對(duì)稱加密算法
- Java 實(shí)現(xiàn)RSA非對(duì)稱加密算法
- 淺析Java中對(duì)稱與非對(duì)稱加密算法原理與使用
- Java實(shí)現(xiàn)非對(duì)稱加密的三種方法
相關(guān)文章
Java注解如何基于Redission實(shí)現(xiàn)分布式鎖
這篇文章主要介紹了Java注解如何基于Redission實(shí)現(xiàn)分布式鎖,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01springmvc使用REST出現(xiàn):Request?method?'PUT'?not?sup
這篇文章主要介紹了springmvc使用REST出現(xiàn):Request?method?'PUT'?not?supported問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02Springboot視圖解析器ViewResolver使用實(shí)例
這篇文章主要介紹了Springboot視圖解析器ViewResolver使用實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04簡(jiǎn)單了解Java方法的定義和使用實(shí)現(xiàn)
這篇文章主要給大家介紹了關(guān)于Java中方法使用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-06-06Java利用openoffice將doc、docx轉(zhuǎn)為pdf實(shí)例代碼
這篇文章主要介紹了Java利用openoffice將doc、docx轉(zhuǎn)為pdf實(shí)例代碼,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01詳解Java如何實(shí)現(xiàn)一個(gè)像String一樣不可變的類
說(shuō)到?String?大家都知道?String?是一個(gè)不可變的類;雖然用的很多,那不知道小伙伴們有沒(méi)有想過(guò)怎么樣創(chuàng)建一個(gè)自己的不可變的類呢?這篇文章就帶大家來(lái)實(shí)踐一下,創(chuàng)建一個(gè)自己的不可變的類2022-11-11