【非对称加密】详解及Java实现
非对称加密详解及Java实现
一、非对称加密概述
非对称加密(Asymmetric Cryptography),也称为公钥加密,是一种使用一对密钥(公钥和私钥)进行加密和解密的加密方法。它与对称加密的主要区别在于使用了不同的密钥进行加密和解密操作。
核心特点:
- 密钥成对出现:公钥(Public Key)和私钥(Private Key)
- 公钥可公开,私钥必须严格保密
- 加密解密方向性:
- 公钥加密 → 私钥解密(用于加密通信)
- 私钥加密 → 公钥解密(用于数字签名)
- 计算复杂度高,速度比对称加密慢很多
常见算法:
- RSA(最常用)
- DSA(主要用于数字签名)
- ECC(椭圆曲线加密)
- ElGamal
- Diffie-Hellman(密钥交换)
二、RSA算法原理
RSA是最经典的非对称加密算法,由Ron Rivest、Adi Shamir和Leonard Adleman于1977年提出。
密钥生成步骤:
- 选择两个大素数p和q
- 计算n = p × q
- 计算欧拉函数φ(n) = (p-1)(q-1)
- 选择整数e,使得1 < e < φ(n)且e与φ(n)互质
- 计算d,使得d × e ≡ 1 mod φ(n)
- 公钥:(e, n),私钥:(d, n)
加密解密过程:
- 加密:c ≡ m^e mod n
- 解密:m ≡ c^d mod n
三、Java实现RSA加密
1. 密钥对生成
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
public class RSAKeyGenerator {
public static void main(String[] args) {
try {
// 1. 创建KeyPairGenerator实例,指定算法为RSA
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
// 2. 初始化密钥长度,通常为1024、2048或4096位
// 密钥越长越安全,但计算速度越慢
keyPairGenerator.initialize(2048);
// 3. 生成密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 4. 获取公钥和私钥
System.out.println("Public Key: " + keyPair.getPublic());
System.out.println("Private Key: " + keyPair.getPrivate());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
代码解释:
KeyPairGenerator
是Java中用于生成非对称密钥对的类initialize(2048)
指定密钥长度为2048位(目前推荐的最小安全长度)- 生成的
KeyPair
包含公钥和私钥
2. RSA加密解密实现
import javax.crypto.Cipher;
import java.security.*;
import java.util.Base64;
public class RSAEncryptionExample {
public static void main(String[] args) throws Exception {
// 1. 生成RSA密钥对
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 获取公钥和私钥
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// 原始消息
String originalMessage = "这是一条需要加密的敏感信息";
System.out.println("原始消息: " + originalMessage);
// 2. 使用公钥加密
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = encryptCipher.doFinal(originalMessage.getBytes());
String encryptedMessage = Base64.getEncoder().encodeToString(encryptedBytes);
System.out.println("加密后的消息: " + encryptedMessage);
// 3. 使用私钥解密
Cipher decryptCipher = Cipher.getInstance("RSA");
decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedBytes = decryptCipher.doFinal(Base64.getDecoder().decode(encryptedMessage));
String decryptedMessage = new String(decryptedBytes);
System.out.println("解密后的消息: " + decryptedMessage);
}
}
代码解释:
- 使用
Cipher
类进行加密解密操作 ENCRYPT_MODE
表示加密模式,使用公钥初始化DECRYPT_MODE
表示解密模式,使用私钥初始化- 由于加密后的字节数组不易显示,使用Base64编码转换为字符串
- RSA加密有长度限制,加密的数据长度不能超过密钥长度减去一定的padding长度
3. 处理长文本加密
由于RSA加密有长度限制,对于长文本需要分段加密:
import javax.crypto.Cipher;
import java.security.*;
import java.util.Base64;
public class RSALongTextEncryption {
private static final int KEY_SIZE = 2048;
private static final int MAX_ENCRYPT_BLOCK = KEY_SIZE / 8 - 11; // 加密块最大长度
public static void main(String[] args) throws Exception {
// 生成密钥对
KeyPair keyPair = generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// 长文本
StringBuilder longText = new StringBuilder();
for (int i = 0; i < 100; i++) {
longText.append("这是一段非常长的文本,需要进行分段加密处理。");
}
// 加密
byte[] encryptedData = encryptLongText(longText.toString(), publicKey);
String encryptedBase64 = Base64.getEncoder().encodeToString(encryptedData);
System.out.println("加密后长度: " + encryptedBase64.length());
// 解密
String decryptedText = decryptLongText(Base64.getDecoder().decode(encryptedBase64), privateKey);
System.out.println("解密是否成功: " + longText.toString().equals(decryptedText));
}
public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(KEY_SIZE);
return keyPairGenerator.generateKeyPair();
}
public static byte[] encryptLongText(String data, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] dataBytes = data.getBytes();
int inputLen = dataBytes.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段加密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(dataBytes, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(dataBytes, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_ENCRYPT_BLOCK;
}
return out.toByteArray();
}
public static String decryptLongText(byte[] encryptedData, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
int inputLen = encryptedData.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
int blockSize = KEY_SIZE / 8; // 解密块大小
// 对数据分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > blockSize) {
cache = cipher.doFinal(encryptedData, offSet, blockSize);
} else {
cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * blockSize;
}
return out.toString();
}
}
关键点:
- 2048位RSA密钥最大加密块为245字节(2048/8-11)
- 解密块大小为256字节(2048/8)
- 使用
ByteArrayOutputStream
收集所有加密/解密后的分段数据
四、数字签名
非对称加密还可用于数字签名,验证数据完整性和身份认证。
Java实现数字签名
import java.security.*;
import java.util.Base64;
public class DigitalSignatureExample {
public static void main(String[] args) throws Exception {
// 1. 生成密钥对
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 待签名的数据
String data = "这是一份重要合同内容";
byte[] dataBytes = data.getBytes();
// 2. 创建签名实例并初始化
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(keyPair.getPrivate()); // 用私钥签名
// 3. 更新要签名的数据
signature.update(dataBytes);
// 4. 生成签名
byte[] digitalSignature = signature.sign();
String signatureBase64 = Base64.getEncoder().encodeToString(digitalSignature);
System.out.println("数字签名: " + signatureBase64);
// 5. 验证签名
Signature verifySignature = Signature.getInstance("SHA256withRSA");
verifySignature.initVerify(keyPair.getPublic()); // 用公钥验证
verifySignature.update(dataBytes);
boolean isVerified = verifySignature.verify(digitalSignature);
System.out.println("签名验证结果: " + isVerified);
}
}
代码解释:
- 使用
Signature
类进行数字签名操作 SHA256withRSA
表示使用SHA-256哈希算法和RSA签名算法- 私钥用于生成签名,公钥用于验证签名
- 如果数据或签名被篡改,验证将失败
五、非对称加密的应用场景
安全通信(如HTTPS、SSL/TLS)
- 客户端使用服务器公钥加密对称密钥
- 服务器用私钥解密获取对称密钥
- 后续通信使用对称加密
数字签名
- 验证数据完整性和来源真实性
- 用于软件发布、合同签署等场景
密钥交换
- 如Diffie-Hellman密钥交换协议
- 在不安全通道上安全地协商对称密钥
数字证书
- 证书颁发机构(CA)使用私钥签名证书
- 用户使用CA公钥验证证书真实性
六、安全注意事项
- 密钥长度:目前推荐至少2048位RSA密钥,3072或4096位更安全
- 密钥管理:私钥必须严格保护,建议使用HSM(硬件安全模块)存储
- 加密模式:使用适当的padding模式(如OAEP),避免使用PKCS1v1.5
- 性能考虑:非对称加密计算量大,通常仅用于加密小数据或密钥交换
- 算法选择:考虑使用ECC(椭圆曲线加密)替代RSA,在相同安全强度下密钥更短
七、与其他加密方式比较
特性 | 对称加密 | 非对称加密 |
---|---|---|
密钥 | 单一密钥 | 公钥/私钥对 |
速度 | 快 | 慢 |
密钥分发 | 困难 | 容易(公钥可公开) |
用途 | 大数据加密 | 密钥交换、数字签名、小数据加密 |
常见算法 | AES, DES, 3DES | RSA, ECC, DSA |
密钥长度 | 128/256位 | 2048/4096位(RSA) |
八、总结
非对称加密是现代密码学的基石,解决了密钥分发和数字签名等关键安全问题。Java通过java.security
和javax.crypto
包提供了完整的非对称加密支持。在实际应用中,通常将对称加密和非对称加密结合使用,发挥各自优势:使用非对称加密安全地交换对称密钥,然后使用对称加密加密实际通信数据。
理解非对称加密的原理和正确使用方式,对于构建安全系统至关重要。开发者应当根据具体场景选择适当的算法、密钥长度和加密模式,并遵循最佳实践来管理密钥。
【非对称加密】详解及Java实现的更多相关文章
- java加密算法入门(三)-非对称加密详解
1.简单介绍 这几天一直在看非对称的加密,相比之前的两篇内容,这次看了两倍多的时间还云里雾里的,所以这篇文章相对之前的两篇,概念性的东西多了些,另外是代码的每一步我都做了介绍,方便自己以后翻阅,也方便 ...
- JAVA RSA非对称加密详解[转载]
一.概述1.RSA是基于大数因子分解难题.目前各种主流计算机语言都支持RSA算法的实现2.java6支持RSA算法3.RSA算法可以用于数据加密和数字签名4.RSA算法相对于DES/AES等对称加密算 ...
- MD5加密详解
MD5加密详解 引言: 我在百度百科上查找到了关于MD5的介绍,我从中摘要一些重要信息: Message Digest Algorithm MD5(中文名为信息摘要算法第五版)为计算机安全领域广泛使用 ...
- 事件驱动模型实例详解(Java篇)
或许每个软件从业者都有从学习控制台应用程序到学习可视化编程的转变过程,控制台应用程序的优点在于可以方便的练习某个语言的语法和开发习惯(如.net和java),而可视化编程的学习又可以非常方便开发出各类 ...
- 【桥接设计模式详解】Java/JS/Go/Python/TS不同语言实现
[桥接设计模式详解]Java/JS/Go/Python/TS不同语言实现 简介 桥接模式(Bridge Pattern)是一种结构型设计模式,它将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的 ...
- 对称加密详解,以及JAVA简单实现
(原) 常用的加密有3种 1.正向加密,如MD5,加密后密文固定,目前还没办法破解,但是可以能过数据库撞库有一定概率找到,不过现在一般用这种方式加密都会加上盐值. 2.对称加密,通过一个固定的对称密钥 ...
- Myeclipse Templates详解(一) —— Java模板基础
目录 Templates简介 MyEclipse自带Templates详解 新建Template 自定义Template 因为自己比较懒,尤其是对敲重复代码比较厌恶,所以经常喜欢用快捷键和模板,Mye ...
- Heapsort 堆排序算法详解(Java实现)
Heapsort (堆排序)是最经典的排序算法之一,在google或者百度中搜一下可以搜到很多非常详细的解析.同样好的排序算法还有quicksort(快速排序)和merge sort(归并排序),选择 ...
- 二叉搜索树详解(Java实现)
1.二叉搜索树定义 二叉搜索树,是指一棵空树或者具有下列性质的二叉树: 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值: 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根 ...
- Java AtomicInteger类的使用方法详解_java - JAVA
文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 首先看两段代码,一段是Integer的,一段是AtomicInteger的,为以下: public class Samp ...
随机推荐
- windows nvm 切换node版本后,npm找不到
前言 在 windows 使用 nvm,管理 node 版本时,nvm install 14.21.3 后,发现在指定 node 版本的 node_modules 文件夹中没有对应的 npm 包,这时 ...
- gorm中使用乐观锁
乐观锁简介 乐观锁(又称乐观并发控制)是一种常见的数据库并发控制策略. 乐观并发控制多数用于数据竞争(data race)不大.冲突较少的环境中,这种环境中,偶尔回滚事务的成本会低于读取数据时锁定数据 ...
- Laravel 配置连接多个数据库以及如何使用
目录 配置连接 配置 .env 文件 配置 \config\database.php 文件 使用 Schema Query Eloquent 配置连接 配置 .env 文件 /* 这部分是默认的数据库 ...
- 通过C#转换图片到PDF文档
将图片(JPG.PNG)转换为PDF文件可以帮助我们更好地保存和分享图片.此外,PDF文件还具有强大的安全特性,将图片转换为PDF后,我们可以通过设置密码来文件内容不被泄露.本文将介绍如何将JPG/P ...
- Django实战项目-学习任务系统-发送邮件通知
接着上期代码内容,继续完善优化系统功能. 本次增加发送邮件通知功能,学习任务系统发布的任务,需要及时通知到学生用户知晓. 由于目前智能手机普及,人人都离不开手机,所以手机端接收通知信息更加及时有效. ...
- 通过 openpyxl 操作 excel 表格
博客地址:https://www.cnblogs.com/zylyehuo/ STEP1: 导入相关库 import os from openpyxl import load_workbook STE ...
- 阿里云服务器中Linux下centos7.6安装mysql8.0.11
1.下载安装 MySQL最新下载地址:https://dev.mysql.com/downloads/mysql/ 选择的是Linux 64位通用的二级制版本,这样不在需要进行编译安装,系统安装依赖 ...
- apisix~ApisixPluginConfig的使用
1. ApisixPluginConfig 的作用 插件配置复用:将插件配置定义为独立的资源,供多个路由或服务引用. 解耦插件与路由:修改插件配置时,只需更新 ApisixPluginConfig,无 ...
- 什么是 Write-Ahead Logging (WAL) 技术?它的优点是什么?MySQL 中是否用到了 WAL?
什么是 Write-Ahead Logging (WAL) 技术? Write-Ahead Logging (WAL) 是一种用于数据库系统的日志记录技术,它要求在对数据库进行任何修改之前,所有的修改 ...
- EF core番外——EF core 输出生成的SQL 到控制台
----------------版权声明:本文为CSDN博主「爱睡觉的程序员」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.cs ...