Java對(duì)稱加密與非對(duì)稱加密區(qū)別、用途及工具類實(shí)戰(zhàn)教程
前言
在Java開發(fā)中,加密是保障數(shù)據(jù)安全的核心手段,常用的加密方式主要分為對(duì)稱加密和非對(duì)稱加密。很多開發(fā)者剛接觸時(shí)會(huì)混淆兩者的用法,這篇文章就用通俗的語言講清它們的區(qū)別、適用場(chǎng)景,再附上可直接復(fù)用的工具類,新手也能快速上手。
一、核心區(qū)別:一張表看懂
| 對(duì)比維度 | 對(duì)稱加密 | 非對(duì)稱加密 |
|---|---|---|
| 密鑰特點(diǎn) | 加密和解密用同一把密鑰(密鑰需嚴(yán)格保密) | 用公鑰+私鑰成對(duì)工作(公鑰可公開,私鑰必須保密) |
| 加密效率 | 速度極快(適合大數(shù)據(jù)量加密) | 速度較慢(數(shù)學(xué)運(yùn)算復(fù)雜,適合小數(shù)據(jù)量加密) |
| 安全性 | 安全性依賴密鑰保管(密鑰泄露則數(shù)據(jù)全暴露) | 安全性更高(私鑰不對(duì)外傳輸,僅需保管好私鑰) |
| 密鑰分發(fā) | 密鑰分發(fā)困難(需安全通道傳輸,否則易被攔截) | 密鑰分發(fā)簡單(公鑰可公開傳播,無需保密) |
| 典型算法 | AES、DES、3DES | RSA、DSA、ECC |
二、各自用途:什么時(shí)候用哪種?
1. 對(duì)稱加密:適合“大數(shù)據(jù)量加密”場(chǎng)景
對(duì)稱加密的核心優(yōu)勢(shì)是快,所以主要用于對(duì)大量數(shù)據(jù)進(jìn)行加密,比如:
- 數(shù)據(jù)庫中敏感字段加密(如用戶手機(jī)號(hào)、身份證號(hào));
- 文件傳輸加密(如上傳/下載的Excel、PDF文件);
- 接口請(qǐng)求體加密(如APP向后端傳輸?shù)挠脩粜袨閿?shù)據(jù))。
舉個(gè)實(shí)際開發(fā)場(chǎng)景:用戶在APP上提交實(shí)名認(rèn)證信息(姓名+身份證號(hào)),后端接收后需要加密存儲(chǔ)到MySQL。此時(shí)用AES對(duì)稱加密,既能保證數(shù)據(jù)安全,又不會(huì)因?yàn)榧用芎臅r(shí)影響接口響應(yīng)速度。
2. 非對(duì)稱加密:適合“密鑰交換/數(shù)字簽名”場(chǎng)景
非對(duì)稱加密的核心優(yōu)勢(shì)是安全且密鑰易分發(fā),但速度慢,所以主要用于:
- 密鑰交換(如對(duì)稱加密的密鑰,通過非對(duì)稱加密傳輸);
- 數(shù)字簽名(如接口防篡改、數(shù)據(jù)來源認(rèn)證);
- 小數(shù)據(jù)加密(如加密用戶登錄密碼的驗(yàn)證碼)。
舉個(gè)實(shí)際開發(fā)場(chǎng)景:APP與后端首次建立連接時(shí),需要協(xié)商一個(gè)對(duì)稱加密的密鑰(用于后續(xù)數(shù)據(jù)傳輸加密)。此時(shí)后端生成RSA公鑰和私鑰,將公鑰傳給APP,APP用公鑰加密自己生成的AES密鑰,后端用私鑰解密得到AES密鑰,后續(xù)就用AES加密傳輸數(shù)據(jù)——既解決了對(duì)稱密鑰的安全分發(fā)問題,又保證了傳輸效率。
三、Java工具類實(shí)戰(zhàn)(可直接復(fù)制使用)
1. 對(duì)稱加密工具類(AES算法)
AES是目前最常用的對(duì)稱加密算法,安全性高、效率快,推薦優(yōu)先使用。以下工具類支持AES-128/256加密,包含加密、解密、生成密鑰功能:
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
/**
* AES對(duì)稱加密工具類(可直接復(fù)用)
* 說明:128位密鑰無需額外配置,256位密鑰需安裝JCE無限制權(quán)限文件
*/
public class AesEncryptUtil {
// 加密算法:AES,模式:ECB,填充方式:PKCS5Padding(實(shí)際開發(fā)推薦用CBC模式,需加偏移量)
private static final String ALGORITHM = "AES/ECB/PKCS5Padding";
private static final String KEY_ALGORITHM = "AES";
private static final int KEY_SIZE = 128; // 密鑰長度:128/256
/**
* 生成AES密鑰(返回Base64編碼后的密鑰字符串,方便存儲(chǔ))
*/
public static String generateKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
keyGenerator.init(KEY_SIZE);
SecretKey secretKey = keyGenerator.generateKey();
return Base64.getEncoder().encodeToString(secretKey.getEncoded());
}
/**
* 加密:明文 -> Base64編碼的密文
* @param plainText 明文(待加密的字符串)
* @param keyStr Base64編碼后的密鑰
*/
public static String encrypt(String plainText, String keyStr) throws Exception {
// 1. 解碼密鑰
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
// 2. 初始化加密器
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// 3. 加密并返回Base64編碼的密文
byte[] encryptBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(encryptBytes);
}
/**
* 解密:Base64編碼的密文 -> 明文
* @param cipherText Base64編碼后的密文
* @param keyStr Base64編碼后的密鑰
*/
public static String decrypt(String cipherText, String keyStr) throws Exception {
// 1. 解碼密鑰和密文
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
byte[] cipherBytes = Base64.getDecoder().decode(cipherText);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
// 2. 初始化解密器
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
// 3. 解密并返回明文
byte[] decryptBytes = cipher.doFinal(cipherBytes);
return new String(decryptBytes, "UTF-8");
}
// 測(cè)試方法
public static void main(String[] args) throws Exception {
// 1. 生成密鑰(實(shí)際開發(fā)中密鑰需妥善保管,如配置在Nacos、Vault中)
String key = generateKey();
System.out.println("AES密鑰(Base64編碼):" + key);
// 2. 加密
String plainText = "用戶身份證號(hào):110101199001011234";
String cipherText = encrypt(plainText, key);
System.out.println("加密后的密文:" + cipherText);
// 3. 解密
String decryptText = decrypt(cipherText, key);
System.out.println("解密后的明文:" + decryptText);
}
}
2. 非對(duì)稱加密工具類(RSA算法)
RSA是最常用的非對(duì)稱加密算法,適合密鑰交換和數(shù)字簽名。以下工具類包含密鑰對(duì)生成、公鑰加密、私鑰解密、私鑰簽名、公鑰驗(yàn)簽功能:
import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/**
* RSA非對(duì)稱加密工具類(可直接復(fù)用)
* 說明:密鑰長度推薦2048位(安全性足夠),4096位加密速度較慢
*/
public class RsaEncryptUtil {
private static final String ALGORITHM = "RSA";
private static final int KEY_SIZE = 2048; // 密鑰長度:2048/4096
// 簽名算法:SHA256withRSA(SHA256哈希+RSA簽名,安全性更高)
private static final String SIGN_ALGORITHM = "SHA256withRSA";
/**
* 生成RSA密鑰對(duì)(公鑰+私鑰),返回Base64編碼后的字符串
* @return 數(shù)組:[0]公鑰,[1]私鑰
*/
public static String[] generateKeyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
keyPairGenerator.initialize(KEY_SIZE);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 公鑰編碼為Base64字符串
String publicKey = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
// 私鑰編碼為Base64字符串
String privateKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
return new String[]{publicKey, privateKey};
}
/**
* 公鑰加密(適合加密小數(shù)據(jù),如AES密鑰)
* @param plainText 明文(待加密的字符串)
* @param publicKeyStr Base64編碼后的公鑰
*/
public static String encryptByPublicKey(String plainText, String publicKeyStr) throws Exception {
// 1. 解碼公鑰
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyStr);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(keySpec);
// 2. 初始化加密器
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// 3. 加密(RSA加密有長度限制,需分段加密,這里簡化處理短字符串)
byte[] encryptBytes = cipher.doFinal(plainText.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(encryptBytes);
}
/**
* 私鑰解密
* @param cipherText Base64編碼后的密文
* @param privateKeyStr Base64編碼后的私鑰
*/
public static String decryptByPrivateKey(String cipherText, String privateKeyStr) throws Exception {
// 1. 解碼私鑰
byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyStr);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
// 2. 初始化解密器
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// 3. 解密
byte[] cipherBytes = Base64.getDecoder().decode(cipherText);
byte[] decryptBytes = cipher.doFinal(cipherBytes);
return new String(decryptBytes, "UTF-8");
}
/**
* 私鑰簽名(用于數(shù)據(jù)防篡改、來源認(rèn)證)
* @param data 待簽名的數(shù)據(jù)
* @param privateKeyStr Base64編碼后的私鑰
*/
public static String sign(String data, String privateKeyStr) throws Exception {
// 1. 解碼私鑰
byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyStr);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
// 2. 生成簽名
Signature signature = Signature.getInstance(SIGN_ALGORITHM);
signature.initSign(privateKey);
signature.update(data.getBytes("UTF-8"));
byte[] signBytes = signature.sign();
return Base64.getEncoder().encodeToString(signBytes);
}
/**
* 公鑰驗(yàn)簽(驗(yàn)證數(shù)據(jù)是否被篡改、簽名是否有效)
* @param data 原始數(shù)據(jù)
* @param sign 簽名值(Base64編碼)
* @param publicKeyStr Base64編碼后的公鑰
*/
public static boolean verifySign(String data, String sign, String publicKeyStr) throws Exception {
// 1. 解碼公鑰
byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyStr);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(keySpec);
// 2. 驗(yàn)證簽名
Signature signature = Signature.getInstance(SIGN_ALGORITHM);
signature.initVerify(publicKey);
signature.update(data.getBytes("UTF-8"));
return signature.verify(Base64.getDecoder().decode(sign));
}
// 測(cè)試方法
public static void main(String[] args) throws Exception {
// 1. 生成密鑰對(duì)
String[] keyPair = generateKeyPair();
String publicKey = keyPair[0];
String privateKey = keyPair[1];
System.out.println("RSA公鑰(Base64編碼):" + publicKey);
System.out.println("RSA私鑰(Base64編碼):" + privateKey);
// 2. 加密解密(加密AES密鑰示例)
String aesKey = "1234567890abcdef"; // 假設(shè)生成的AES密鑰
String encryptAesKey = encryptByPublicKey(aesKey, publicKey);
System.out.println("公鑰加密后的AES密鑰:" + encryptAesKey);
String decryptAesKey = decryptByPrivateKey(encryptAesKey, privateKey);
System.out.println("私鑰解密后的AES密鑰:" + decryptAesKey);
// 3. 簽名驗(yàn)簽
String data = "接口返回?cái)?shù)據(jù):{\"code\":200,\"msg\":\"success\"}";
String sign = sign(data, privateKey);
System.out.println("私鑰簽名值:" + sign);
boolean verifyResult = verifySign(data, sign, publicKey);
System.out.println("公鑰驗(yàn)簽結(jié)果:" + verifyResult); // true表示驗(yàn)簽通過
}
}
四、開發(fā)注意事項(xiàng)
- 密鑰保管:對(duì)稱加密的密鑰和非對(duì)稱加密的私鑰,不能硬編碼在代碼里,建議存儲(chǔ)在配置中心(如Nacos)或密鑰管理工具(如Vault);
- 算法選擇:對(duì)稱加密優(yōu)先用AES(避免用DES,安全性較低),非對(duì)稱加密優(yōu)先用RSA-2048(兼顧安全性和效率);
- 數(shù)據(jù)長度:RSA加密有長度限制(如2048位密鑰最多加密245字節(jié)),加密大數(shù)據(jù)需分段,或用“非對(duì)稱加密+對(duì)稱加密”組合方案;
- 填充方式:對(duì)稱加密推薦用PKCS5Padding(兼容性好),非對(duì)稱加密無需手動(dòng)指定填充方式(Java默認(rèn)實(shí)現(xiàn)已優(yōu)化)。
總結(jié)
到此這篇關(guān)于Java對(duì)稱加密與非對(duì)稱加密區(qū)別、用途及工具類的文章就介紹到這了,更多相關(guān)Java對(duì)稱加密與非對(duì)稱加密內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot?注解EqualsAndHashCode詳解
注解@EqualsAndHashCode主要用于自動(dòng)生成equals方法和hashCode方法,callSuper屬性為true時(shí),生成的方法會(huì)包括父類字段,為false則只包含當(dāng)前類字段,IDEA工具中有檢查提示并可自動(dòng)修復(fù)相關(guān)代碼,確保注解正確使用,更多詳解可查閱相關(guān)文檔2024-10-10
Spring Boot 配置和使用多線程池的實(shí)現(xiàn)
這篇文章主要介紹了Spring Boot 配置和使用多線程池的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-06
Vue實(shí)現(xiàn)驗(yàn)證碼登錄的超詳細(xì)步驟
這篇文章主要給大家介紹了關(guān)于Vue實(shí)現(xiàn)驗(yàn)證碼登錄的超詳細(xì)步驟,我們?cè)谑褂胿ue進(jìn)行前端開發(fā)時(shí)都需要登錄驗(yàn)證,文中通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下2023-09-09
Java異常處理之try...catch...finally詳解
今天小編就為大家分享一篇關(guān)于Java異常處理之try...catch...finally詳解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-01-01

