Pre
概述
在数字化时代,网络通信的安全性是必须关注的重要问题之一。非对称加密算法作为现代密码学的重要组成部分,为保护通信的隐私提供了一种可靠的解决方案。
什么是非对称加密算法?
非对称加密算法,又称为公钥加密算法,是一种密码学中的重要概念。它与传统的对称加密算法不同,需要一对密钥:公钥和私钥。这对密钥之间存在着特殊的数学关系,但无法通过公钥推导出私钥,从而保证了通信的安全性。
如何工作?
当发送方A希望将数据发送给接收方B时,A可以使用B的公钥对数据进行加密,得到密文。只有拥有对应私钥的B才能解密这个密文。同样地,B也可以使用A的公钥加密数据,只有A持有私钥才能解密。这种加密和解密使用不同的密钥的特点,使得非对称加密算法成为了保护通信隐私的重要工具。
示例:RSA算法
RSA算法是非对称加密算法中最常见的一种,它利用了大数分解的数学难题,保证了通信的安全性。在RSA算法中,公钥是公开的,私钥是保密的。发送方使用接收方的公钥对数据进行加密,而接收方使用自己的私钥进行解密,从而实现了安全的通信。
特点和优势
- 加密和解密使用不同的密钥,提高了通信的安全性。
- 如果使用私钥加密,只能使用公钥解密;反之亦然。
- 非对称加密算法安全性高,但处理数据速度较慢。
ECC:另一种非对称加密算法
除了RSA算法,还有一种备受关注的非对称加密算法,即椭圆曲线密码学(ECC)。ECC利用了椭圆曲线上的数学难题,相比RSA算法,它能够以更短的密钥长度实现相当于甚至更高的安全级别,同时在资源受限的环境下拥有更好的性能表现。
Code
生成公钥和私钥
package com.artisan;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
/**
* @author 小工匠
* @version 1.0
*/
public class RsaKeyPair {
public static void main(String[] args) throws Exception {
// 指定加密算法为RSA
String algorithm = "RSA";
// 创建密钥对生成器对象
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 生成RSA密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 获取生成的私钥
PrivateKey privateKey = keyPair.getPrivate();
// 获取生成的公钥
PublicKey publicKey = keyPair.getPublic();
// 获取私钥的编码字节数组
byte[] privateKeyEncoded = privateKey.getEncoded();
// 获取公钥的编码字节数组
byte[] publicKeyEncoded = publicKey.getEncoded();
// 对公私钥的编码字节数组进行Base64编码
String privateKeyString = Base64.encode(privateKeyEncoded);
String publicKeyString = Base64.encode(publicKeyEncoded);
// 打印私钥的Base64编码字符串
System.out.println(privateKeyString);
System.out.println("----------------------------------");
// 打印公钥的Base64编码字符串
System.out.println(publicKeyString);
}
}
使用RSA算法生成一个密钥对,并将私钥和公钥进行Base64编码后打印出来了。
私钥加密
package com.artisan;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import javax.crypto.Cipher;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
/**
* @author 小工匠
* @version 1.0
* @mark: 显示代码,改变世界
*/
public class PrivateKeyEnc {
public static void main(String[] args) throws Exception {
String input = "小工匠的IT生活";
// 指定加密算法为RSA
String algorithm = "RSA";
// 创建密钥对生成器对象
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 生成RSA密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 获取生成的私钥
PrivateKey privateKey = keyPair.getPrivate();
// 获取生成的公钥
PublicKey publicKey = keyPair.getPublic();
// 获取私钥的编码字节数组
byte[] privateKeyEncoded = privateKey.getEncoded();
// 获取公钥的编码字节数组
byte[] publicKeyEncoded = publicKey.getEncoded();
// 对公私钥的编码字节数组进行Base64编码
String privateKeyString = Base64.encode(privateKeyEncoded);
String publicKeyString = Base64.encode(publicKeyEncoded);
// 打印生成的密钥对
System.out.println("私钥(Base64编码): " + privateKeyString);
System.out.println("公钥(Base64编码): " + publicKeyString);
// 创建加密对象,参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
// 初始化加密对象
// 第一个参数:加密模式
// 第二个参数:使用私钥进行加密
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
// 使用私钥加密输入的字符串
byte[] encryptedBytes = cipher.doFinal(input.getBytes());
// 对加密后的字节数组进行Base64编码,并打印
System.out.println("加密后的字符串(Base64编码): " + Base64.encode(encryptedBytes));
}
}
私钥加密私钥解密 ( 行不通 )
在上面的代码上追加
// 私钥进行解密 (错误的演示)
cipher.init(Cipher.DECRYPT_MODE,privateKey);
// 对密文进行解密,不需要使用base64,因为原文不会乱码
byte[] bytes1 = cipher.doFinal(encryptedBytes);
System.out.println(new String(bytes1));
私钥加密公钥解密
将上述代码的 私钥解密,换成使用公钥解密
// 公钥进行解密
cipher.init(Cipher.DECRYPT_MODE,publicKey);
// 对密文进行解密,不需要使用base64,因为原文不会乱码
byte[] bytes1 = cipher.doFinal(encryptedBytes);
System.out.println("解密后的字符串: " + new String(bytes1));
公钥加密和公钥解密 (行不通)
保存公钥和私钥
生成RSA非对称加密算法的密钥对,并将生成的公钥和私钥保存在本地文件中。
package com.artisan;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.nio.charset.Charset;
import java.security.*;
public class KeyPairOperate {
public static void main(String[] args) throws Exception {
// 加密算法
String algorithm = "RSA";
// 生成密钥对并保存在本地文件中
generateKeyToFile(algorithm, "a.pub", "a.pri");
}
/**
* 生成密钥对并保存在本地文件中
*
* @param algorithm : 算法
* @param pubPath : 公钥保存路径
* @param priPath : 私钥保存路径
* @throws Exception
*/
private static void generateKeyToFile(String algorithm, String pubPath, String priPath) throws Exception {
// 获取密钥对生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 获取密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 获取公钥
PublicKey publicKey = keyPair.getPublic();
// 获取私钥
PrivateKey privateKey = keyPair.getPrivate();
// 获取byte数组
byte[] publicKeyEncoded = publicKey.getEncoded();
byte[] privateKeyEncoded = privateKey.getEncoded();
// 进行Base64编码
String publicKeyString = Base64.encode(publicKeyEncoded);
String privateKeyString = Base64.encode(privateKeyEncoded);
// 保存文件
FileUtils.writeStringToFile(new File(pubPath), publicKeyString, Charset.forName("UTF-8"));
FileUtils.writeStringToFile(new File(priPath), privateKeyString, Charset.forName("UTF-8"));
}
}
读取私钥
// 读取私钥
PrivateKey privateKey = readPrivateKeyFromFile(algorithm, "a.pri");
byte[] encoded = privateKey.getEncoded();
String privateContent = Base64.encode(encoded);
System.out.println("私钥内容:" + privateContent);
/**
* @param algorithm
* @param filePath
* @return
* @throws Exception
*/
private static PrivateKey readPrivateKeyFromFile(String algorithm, String filePath) throws Exception {
// 从文件中读取私钥字符串
String privateKeyString = FileUtils.readFileToString(new File(filePath), StandardCharsets.UTF_8);
// 进行Base64解码
byte[] privateKeyEncoded = Base64.decode(privateKeyString);
// 获取密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
// 构建密钥规范 进行Base64解码
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.decode(privateKeyString));
return keyFactory.generatePrivate(spec);
}
看下生成的文件中的内容:
读取公钥
// 读取公钥
PublicKey publicKey = readPublicKeyFromFile(algorithm, "a.pub");
byte[] publicKeyEncoded = publicKey.getEncoded();
String publicContent = Base64.encode(publicKeyEncoded);
System.out.println("公钥内容:" + publicContent);
/**
* @param algorithm
* @param filePath
* @return
* @throws Exception
*/
private static PublicKey readPublicKeyFromFile(String algorithm, String filePath) throws Exception {
// 将文件内容转为字符串
String publicKeyString = FileUtils.readFileToString(new File(filePath), StandardCharsets.UTF_8);
// 获取密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
// 构建密钥规范 进行Base64解码
X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.decode(publicKeyString));
// 生成公钥
return keyFactory.generatePublic(spec);
}
使用读取的公钥加密,私钥解密
// 使用公钥私钥实现加解密
String text = "小工匠的IT生活";
System.out.println("原文:" + text);
String enc = encryptRSA(algorithm, publicKey, text);
System.out.println("公钥加密后的数据:" + enc);
String plainText = decryptRSA(algorithm, privateKey, enc);
System.out.println("私钥解密后的数据:" + plainText);
/**
* 解密数据
*
* @param algorithm : 算法
* @param encrypted : 密文
* @param key : 密钥
* @return : 原文
* @throws Exception
*/
public static String decryptRSA(String algorithm, Key key, String encrypted) throws Exception {
// 创建加密对象
// 参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
// 私钥进行解密
cipher.init(Cipher.DECRYPT_MODE, key);
// 由于密文进行了Base64编码, 在这里需要进行解码
byte[] decode = Base64.decode(encrypted);
// 对密文进行解密,不需要使用base64,因为原文不会乱码
byte[] bytes1 = cipher.doFinal(decode);
return new String(bytes1);
}
/**
* 使用密钥加密数据
*
* @param algorithm : 算法
* @param input : 原文
* @param key : 密钥
* @return : 密文
* @throws Exception
*/
public static String encryptRSA(String algorithm, Key key, String input) throws Exception {
// 创建加密对象
// 参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
// 初始化加密
// 第一个参数:加密的模式
// 第二个参数:使用私钥进行加密
cipher.init(Cipher.ENCRYPT_MODE, key);
// 私钥加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 对密文进行Base64编码
return Base64.encode(bytes);
}
Source
package com.artisan;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.apache.commons.io.FileUtils;
import javax.crypto.Cipher;
import java.io.File;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* @author 小工匠
* @version 1.0
* @mark: show me the code , change the world
*/
public class KeyPairOperate {
public static void main(String[] args) throws Exception {
// 加密算法
String algorithm = "RSA";
// 生成密钥对并保存在本地文件中
generateKeyToFile(algorithm, "a.pub", "a.pri");
// 读取私钥
PrivateKey privateKey = readPrivateKeyFromFile(algorithm, "a.pri");
byte[] encoded = privateKey.getEncoded();
String privateContent = Base64.encode(encoded);
System.out.println("私钥内容:" + privateContent);
// 读取公钥
PublicKey publicKey = readPublicKeyFromFile(algorithm, "a.pub");
byte[] publicKeyEncoded = publicKey.getEncoded();
String publicContent = Base64.encode(publicKeyEncoded);
System.out.println("公钥内容:" + publicContent);
// 使用公钥私钥实现加解密
String text = "小工匠的IT生活";
System.out.println("原文:" + text);
String enc = encryptRSA(algorithm, publicKey, text);
System.out.println("公钥加密后的数据:" + enc);
String plainText = decryptRSA(algorithm, privateKey, enc);
System.out.println("私钥解密后的数据:" + plainText);
}
/**
* 解密数据
*
* @param algorithm : 算法
* @param encrypted : 密文
* @param key : 密钥
* @return : 原文
* @throws Exception
*/
public static String decryptRSA(String algorithm, Key key, String encrypted) throws Exception {
// 创建加密对象
// 参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
// 私钥进行解密
cipher.init(Cipher.DECRYPT_MODE, key);
// 由于密文进行了Base64编码, 在这里需要进行解码
byte[] decode = Base64.decode(encrypted);
// 对密文进行解密,不需要使用base64,因为原文不会乱码
byte[] bytes1 = cipher.doFinal(decode);
return new String(bytes1);
}
/**
* 使用密钥加密数据
*
* @param algorithm : 算法
* @param input : 原文
* @param key : 密钥
* @return : 密文
* @throws Exception
*/
public static String encryptRSA(String algorithm, Key key, String input) throws Exception {
// 创建加密对象
// 参数表示加密算法
Cipher cipher = Cipher.getInstance(algorithm);
// 初始化加密
// 第一个参数:加密的模式
// 第二个参数:使用私钥进行加密
cipher.init(Cipher.ENCRYPT_MODE, key);
// 私钥加密
byte[] bytes = cipher.doFinal(input.getBytes());
// 对密文进行Base64编码
return Base64.encode(bytes);
}
/**
* @param algorithm
* @param filePath
* @return
* @throws Exception
*/
private static PublicKey readPublicKeyFromFile(String algorithm, String filePath) throws Exception {
// 将文件内容转为字符串
String publicKeyString = FileUtils.readFileToString(new File(filePath), StandardCharsets.UTF_8);
// 获取密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
// 构建密钥规范 进行Base64解码
X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.decode(publicKeyString));
// 生成公钥
return keyFactory.generatePublic(spec);
}
/**
* @param algorithm
* @param filePath
* @return
* @throws Exception
*/
private static PrivateKey readPrivateKeyFromFile(String algorithm, String filePath) throws Exception {
// 从文件中读取私钥字符串
String privateKeyString = FileUtils.readFileToString(new File(filePath), StandardCharsets.UTF_8);
// 进行Base64解码
byte[] privateKeyEncoded = Base64.decode(privateKeyString);
// 获取密钥工厂
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
// 构建密钥规范 进行Base64解码
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.decode(privateKeyString));
return keyFactory.generatePrivate(spec);
}
/**
* 生成密钥对并保存在本地文件中
*
* @param algorithm : 算法
* @param pubPath : 公钥保存路径
* @param priPath : 私钥保存路径
* @throws Exception
*/
private static void generateKeyToFile(String algorithm, String pubPath, String priPath) throws Exception {
// 获取密钥对生成器
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
// 获取密钥对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 获取公钥
PublicKey publicKey = keyPair.getPublic();
// 获取私钥
PrivateKey privateKey = keyPair.getPrivate();
// 获取byte数组
byte[] publicKeyEncoded = publicKey.getEncoded();
byte[] privateKeyEncoded = privateKey.getEncoded();
// 进行Base64编码
String publicKeyString = Base64.encode(publicKeyEncoded);
String privateKeyString = Base64.encode(privateKeyEncoded);
// 保存文件
FileUtils.writeStringToFile(new File(pubPath), publicKeyString, Charset.forName("UTF-8"));
FileUtils.writeStringToFile(new File(priPath), privateKeyString, Charset.forName("UTF-8"));
}
}
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。