Java中,编码和加密是两个不同的概率,分别作用于不同的目标。我们在日常开发中偶尔也会用到关于这两个东西,比如对数据加密,账号密码进行加密等情况,下面我围绕Java的编码和加密进行相关介绍和讲解。
Java编码
编码作为Java中将数据转换为另一种格式的过程,通常是用于数据的传输和层次,用以确保数据的可读性和完整性。
在Java中我们常见的编码类型有以下几种:
- UTF-8
- 是一种可变长度的Unicode编码,几乎可以表示所有的字符。
- 使用1到4个字节来表示不同的字符,对应ASCII字符,使用一个字节表示,对于其他字符,使用多个字节表示。
- 是互联网上最常用的字符编码,也是我们Java中默认的字符编码。
String str = "Hello, 世界!";
byte[] utf8Bytes = str.getBytes(StandardCharsets.UTF_8);
String utf8String = new String(utf8Bytes, StandardCharsets.UTF_8);
System.out.println("UTF-8 Encoded String: " + utf8String);
- ISO-8859-1:
- ISO-8859-1(也称为Latin-1)是一种单字节编码,它可以表示西欧语言中的大多数字符。
- ISO-8859-1编码使用1个字节表示一个字符,可以表示ASCII字符和一些扩展字符。
String str = "Hello, World!";
byte[] isoBytes = str.getBytes(StandardCharsets.ISO_8859_1);
String isoString = new String(isoBytes, StandardCharsets.ISO_8859_1);
System.out.println("ISO-8859-1 Encoded String: " + isoString);
-
GBK:
- GBK是一种中文字符集编码,它是GB2312的扩展,可以表示繁体中文和一些生僻字。
- GBK编码使用2个字节表示一个字符。
String str = "你好,世界!";
byte[] gbkBytes = str.getBytes("GBK");
String gbkString = new String(gbkBytes, "GBK");
System.out.println("GBK Encoded String: " + gbkString);
-
Big5:
- Big5是一种繁体中文字符集编码,主要用于台湾地区。
- Big5编码使用2个字节表示一个字符。
-
UTF-16:
- UTF-16是一种固定长度的Unicode编码,它使用2个字节表示一个字符。
- UTF-16可以表示几乎所有的Unicode字符,包括辅助平面字符。
-
Base64 编码:
- Base64编码是一种将二进制数据转换为可打印字符的编码方式。
- 在Java中,可以使用Base64编码来处理二进制数据的转换和传输。
import java.util.Base64;
String str = "Hello, World!";
String encodedString = Base64.getEncoder().encodeToString(str.getBytes());
System.out.println("Base64 Encoded String: " + encodedString);
byte[] decodedBytes = Base64.getDecoder().decode(encodedString);
String decodedString = new String(decodedBytes);
System.out.println("Base64 Decoded String: " + decodedString);
在Java中,可以使用java.nio.charset.Charset
类来获取和操作不同的字符编码。例如,使用Charset.forName("UTF-8")
可以获取UTF-8编码的Charset对象。
Base64
这里我重点讲解一下Base64编码,原因在于其在许多场景被广泛使用,例如在电子邮件中传输二进制附件,在HTTP中请求中传输二进制数据,如图片和文件,在URL中传输二进制数据,避免特殊字符等问题,还有在数据库中存储二进制数据。
Base64 并不是加密解密算法,尽管我们有时也听到使用 Base64 来加密解密的说法,但这里所说的加密与解密实际是指编码(encode) 和解码(decode) 的过程,其变换是非常简单的,仅仅能够避免信息被直接识别。
Base64 算法主要是将给定的字符以字符编码(如 ASCII 码,UTF-8 码)对应的十进制数为基准,做编码操作:
- 将给定的字符串以字符为单位,转换为对应的字符编码。
- 将获得字符编码转换为二进制
- 对二进制码做分组转换,每 3 个字节为一组,转换为每 4 个 6 位二进制位一组(不足 6 位时低位补 0)。这是一个分组变化的过程,3 个 8 位二进制码和 4 个 6 位二进制码的长度都是 24 位(38 = 46 = 24)。
- 对获得的 4-6 二进制码补位,向 6 位二进制码添加 2 位高位 0,组成 4 个 8 位二进制码。
- 对获得的 4-8 二进制码转换为十进制码。
- 将获得的十进制码转换为 Base64 字符表中对应的字符。
特点
- 字符集:Base64编码使用64个字符来表示二进制数据,包括大小写字母、数字和两个特殊字符(+和/)。
- 可打印性:Base64编码的结果是由可打印字符组成的字符串,可以在文本环境中直接使用。
- 固定长度:每3个字节的二进制数据被编码为4个字符,因此Base64编码后的字符串长度总是原始数据长度的4/3倍(不足4的倍数时会进行填充)。
- 数据完整性:Base64编码不会改变原始数据的内容,只是将其表示为不同的形式。
- 不加密:Base64编码只是一种编码方式,不提供数据的加密功能。
在Java中,可以使用java.util.Base64
类来进行Base64编码和解码操作。Base64类提供了静态方法来进行编码和解码,例如Base64.getEncoder().encodeToString(byte[])
用于编码,Base64.getDecoder().decode(String)
用于解码。
示例:
import java.util.Base64;
public class Base64Demo {
public static void main(String[] args) throws Exception{
// Base64编码示例
String originalString = "Hello, World!";
String encodedString = Base64.getEncoder().encodeToString(originalString.getBytes());
System.out.println("Base64编码后的秘钥: " + encodedString);
String deCodedString = new String(Base64.getDecoder().decode(encodedString));
System.out.println("Base64解码后的秘钥: " + deCodedString);
}
}
Java的消息摘要
在Java中,消息摘要(Message Digest)是一种用于生成数据摘要的算法。它将任意长度的数据作为输入,并生成一个固定长度的摘要(哈希值)。消息摘要常用于验证数据的完整性和一致性,以及实现密码存储和数字签名等功能。
消息摘要是一个唯一对应一个消息或文本的固定程度的值,他由一个单向hash加密函数对消息进行作用而产生,如果消息中途改变,那么消息的接收着对收到消息的新产生的摘要和原摘要比较,就可以知道消息是否被改变了,故而消息摘要保证了消息的完整性。消息摘要采用单向的hash函数将需要加密的明文“摘要”成一串密文,这一串密文亦称为数字指纹,有固定的长度,且不同的明文摘要成密文,其结果总是不同的。而同样的明文其摘要必定是一致。这样这串摘要便可成为验证明文是否是真身的指纹了。
消息摘要的特点:
- 固定长度:无论输入数据的长度如何,消息摘要生成的摘要长度是固定的。
- 唯一性:不同的输入数据通常会生成不同的摘要,即使输入数据只有微小的变化。
- 不可逆性:从摘要无法推导出原始数据,即无法通过摘要逆向还原出原始数据。
- 散列冲突:不同的输入数据可能会生成相同的摘要,这称为散列冲突,但是发生散列冲突的概率非常低。
消息摘要可以解决的问题:
- 数据完整性验证:通过比较原始数据的摘要和接收到的数据的摘要,可以验证数据在传输过程中是否被篡改。
- 密码存储:可以将用户密码的摘要存储在数据库中,而不是明文存储密码。当用户登录时,将输入的密码进行摘要计算,并与存储的摘要进行比较,以验证密码的正确性。
- 数字签名:使用私钥对数据的摘要进行签名,然后使用公钥验证签名的有效性。这可以确保数据的来源和完整性。
- 数据索引:可以使用消息摘要作为数据的唯一标识,用于快速查找和索引数据。
常见的消息摘要类型:
-
MD5:MD5(Message Digest Algorithm 5)是一种广泛使用的消息摘要算法,生成的摘要长度为128位。然而,由于MD5存在安全性问题,不再推荐用于密码存储和安全性要求较高的场景。
-
SHA-1:SHA-1(Secure Hash Algorithm 1)是一种生成160位摘要的算法。然而,由于SHA-1存在碰撞攻击的风险,已经不再被广泛使用。
-
SHA-256:SHA-256是SHA-2(Secure Hash Algorithm 2)系列中的一种算法,生成的摘要长度为256位。SHA-256是目前广泛使用的安全摘要算法之一,提供较高的安全性。
-
SHA-512:SHA-512是SHA-2系列中的一种算法,生成的摘要长度为512位。相比于SHA-256,SHA-512提供更高的安全性,但计算速度较慢 。
-
SHA-3:SHA-3是最新的SHA系列算法,它提供了与SHA-2相似的安全性,但具有不同的内部结构。SHA-3算法有多个变体,如SHA-3-224、SHA-3-256、SHA-3-384和SHA-3-512,根据需要选择适当的摘要长度。
SHA-256 消息摘要示例:
public class MessageDigestDemo {
public static void main(String[] args) throws NoSuchAlgorithmException {
String originalString = "Hello, World!";
// 创建MessageDigest对象并指定算法
MessageDigest digest = MessageDigest.getInstance("SHA-256");
// 计算消息摘要
byte[] digestBytes = digest.digest(originalString.getBytes());
// 将摘要转换为十六进制字符串
StringBuilder hexString = new StringBuilder();
for (byte b : digestBytes) {
// String hex = String.format("%02x", b); 这样和下述等同
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
System.out.println("SHA-256 消息摘要结果: " + hexString.toString());
}
}
out : SHA-256 消息摘要结果: dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
综合示例:
public class MessageDigestDemoPlus {
public static byte[] encode(byte[] input, Type type) throws Exception {
// 根据类型,初始化消息摘要对象
MessageDigest md5Digest = MessageDigest.getInstance(type.getName());
// 更新要计算的内容
md5Digest.update(input);
// 完成哈希计算,返回摘要
return md5Digest.digest();
}
public static byte[] encodeWithBase64(byte[] input, Type type) throws Exception {
return Base64.getUrlEncoder().encode(encode(input, type));
}
public static String encodeWithBase64String(byte[] input, Type type) throws Exception {
return Base64.getUrlEncoder().encodeToString(encode(input, type));
}
public enum Type {
MD5("MD5"),
SHA1("SHA1"),
SHA256("SHA-256"),
SHA384("SHA-384"),
SHA512("SHA-512");
private String name;
Type(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
public static void main(String[] args) throws Exception {
String msg = "Hello World!";
System.out.println("MD5: " + encodeWithBase64String(msg.getBytes(), Type.MD5));
System.out.println("SHA1: " + encodeWithBase64String(msg.getBytes(), Type.SHA1));
System.out.println("SHA256: " + encodeWithBase64String(msg.getBytes(), Type.SHA256));
System.out.println("SHA384: " + encodeWithBase64String(msg.getBytes(), Type.SHA384));
System.out.println("SHA512: " + encodeWithBase64String(msg.getBytes(), Type.SHA512));
}
}
MD5: 7Qdih1MuhjZehB6Sv8UNjA==
SHA1: Lve95gjOVATpfV8EL5X4nxwjKHE=
SHA256: f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk=
SHA384: v9dsDrvQBv7lg0EFR8GIewKSvnbVgtlsJC0qeScj4_1v0GH51c_RO4-WE1jmrbpK
SHA512: hhhE1nBOhXP-w02WfiC8_vPUJM9IvgTm3AjyvVjHKXQzcQFerYkcw88cnTS0kmS1EHUbH_nlN5N7xGtdb_TsyA==
数字签名
Java的数字签名是一种用于验证数据完整性和身份认证的机制。它使用非对称加密算法来生成和验证数字签名,以确保数据的来源和完整性。数字签名算法是非对称加密算法和消息摘要算法的结合体。
数字签名算法的特点:
- 数据完整性:数字签名可以验证数据是否在传输过程中被篡改。
- 身份认证:数字签名可以验证数据的来源,确保数据是由特定的发送者生成的。
- 不可伪造:数字签名使用私钥进行加密,只有持有私钥的发送者才能生成有效的签名。
- 不可篡改:数字签名是基于非对称加密算法的,即使对签名进行修改,也无法通过验证。
数字签名过程:
- 生成秘钥对:使用非对称加密算法(如:RSA)生成一对密钥,包括公钥和私钥。
- 签名:使用私钥对要签名的数据进行加密,生成数字签名。
- 验证:使用公钥对签名和原始数据进行解密,然后比较解密后的数据和原始数据是否一致。
RSA示例:
public class DigitalSignatureDemo {
public static void main(String[] args) throws Exception {
String originalData = "Hello, World!";
// 生成密钥对
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
// 签名
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(originalData.getBytes());
byte[] signatureBytes = signature.sign();
// 验证
Signature verification = Signature.getInstance("SHA256withRSA");
verification.initVerify(publicKey);
verification.update(originalData.getBytes());
boolean isVerified = verification.verify(signatureBytes);
System.out.println("数字签名校验结果: " + isVerified);
}
}
DSA示例:
public class DsaSignatureDemo {
public static final String KEY_ALGORITHM = "DSA";
public static final String SIGN_ALGORITHM = "SHA1withDSA";
/**
* DSA密钥长度默认1024位。 密钥长度必须是64的整数倍,范围在512~1024之间
*/
private static final int KEY_SIZE = 1024;
private KeyPair keyPair;
public DsaSignatureDemo() throws Exception {
this.keyPair = initKey();
}
private KeyPair initKey() throws Exception {
// 初始化密钥对生成器
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(DsaSignatureDemo.KEY_ALGORITHM);
// 实例化密钥对生成器
keyPairGen.initialize(KEY_SIZE);
// 实例化密钥对
return keyPairGen.genKeyPair();
}
public byte[] signature(byte[] data, byte[] privateKey) throws Exception {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey key = keyFactory.generatePrivate(keySpec);
Signature signature = Signature.getInstance(SIGN_ALGORITHM);
signature.initSign(key);
signature.update(data);
return signature.sign();
}
public byte[] getPrivateKey() {
return keyPair.getPrivate().getEncoded();
}
public boolean verify(byte[] data, byte[] publicKey, byte[] sign) throws Exception {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey key = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(SIGN_ALGORITHM);
signature.initVerify(key);
signature.update(data);
return signature.verify(sign);
}
public byte[] getPublicKey() {
return keyPair.getPublic().getEncoded();
}
public static void main(String[] args) throws Exception {
String msg = "Hello,World";
DsaSignatureDemo dsa = new DsaSignatureDemo();
byte[] sign = dsa.signature(msg.getBytes(), dsa.getPrivateKey());
boolean flag = dsa.verify(msg.getBytes(), dsa.getPublicKey(), sign);
String result = flag ? "数字签名匹配" : "数字签名不匹配";
System.out.println("数字签名:" + Base64.getUrlEncoder().encodeToString(sign));
System.out.println("验证结果:" + result);
}
}
output:
数字签名:MC0CFQCNkbjPsCQr2t6bwuq9qbpdsXFPdAIUHKCsA0ktND4vBTMYO3jl6Mkad_Q=
验证结果:数字签名匹配
Java的加密
加密是通过使用密钥将数据转换为另一种形式的过程,以保护数据的安全性和隐私性。Java提供了许多加密算法和工具,如对称加密算法(如AES、DES)、非对称加密算法(如RSA)、哈希函数(如MD5、SHA)等。对称加密算法使用相同的密钥进行加密和解密,适用于对数据进行加密保护。非对称加密算法使用公钥加密、私钥解密的方式,用于安全地传输密钥和数字签名等场景。哈希函数用于将任意长度的数据转换为固定长度的哈希值,常用于数据完整性校验和密码存储。
对称加密
对称加密算法主要是DES、3DES、AES、IDEA、RC2、RC4、RC5和Blowfish等。
对称加密算法是应用的比较早的一种加密算法,技术来说相对成熟,在对称加密算法中,数据发送方将明文(也就是原始数据)和加密密钥一起经过特殊算法处理后,是其变为复杂的加密密文发送出去,收信方收到密文后,如果他需要获取原文信息,那就得使用加密后的密钥及相同的算法的逆方法对密文进行解密,和数据签名不一样,这个是可逆的,在对称加密算法中,使用的秘钥只有一个,发收信方度需要使用这个密钥对数据进行加密和解密。
对称加密的优缺点
优点:
- 速度快:对称加密算法通常比非对称加密算法更快,因为加密和解密使用相同的密钥和算法。
- 适用于大量数据:对称加密算法适用于加密大量数据,因为它们的加密和解密过程相对较快。
- 实现简单:对称加密算法的实现相对简单,易于理解和使用。
缺点:
- 密钥管理:对称加密算法需要在通信双方之间共享密钥,因此需要安全地管理和分发密钥。
- 密钥数量:对称加密算法的密钥数量较多,当通信双方增加时,密钥的数量也会增加,密钥管理变得更加复杂。
- 安全性:对称加密算法的安全性依赖于密钥的保密性,如果密钥被泄露,加密数据的安全性将受到威胁。
- 密钥交换:在通信开始之前,通信双方需要安全地交换密钥,以确保密钥的机密性和完整性。
原理:
-
密钥生成:在通信双方之间,首先需要生成一个共享的对称密钥。这个密钥需要保密,并且只有通信双方知道。
-
加密过程:发送方使用对称密钥将原始数据进行加密。加密算法将原始数据和对称密钥作为输入,生成密文作为输出。
-
传输密文:发送方将加密后的密文发送给接收方。由于密文是经过加密的,第三方无法直接获取原始数据。
-
解密过程:接收方使用相同的对称密钥对密文进行解密。解密算法将密文和对称密钥作为输入,生成原始数据作为输出。
对称加密的关键在于对称密钥的保密性。只有发送方和接收方知道对称密钥,才能进行加密和解密操作。因此,对称加密算法的安全性依赖于对密钥的保护和管理。
常见的对称加密算法有AES(Advanced Encryption Standard)、DES(Data Encryption Standard)和3DES(Triple Data Encryption Algorithm)等。这些算法使用相同的密钥进行加密和解密,但具体的加密算法和密钥长度可能会有所不同。
例如甲方和乙方作为通信双方,当双方在消息传递之前已经确定好了加密算法,那么他们需要完成一次消息传递就学要经过以下步骤:
对称加密的工作模式:
电子密码本模式(Electronic Codebook, ECB):
- ECB模式将明文分成固定大小的数据块,然后对每个数据块进行独立的加密。
- 相同的明文块将始终加密为相同的密文块,因此ECB模式不适用于加密需要保持机密性和完整性的数据。
密码分组链接模式(Cipher Block Chaining, CBC):
- CBC模式使用前一个密文块作为当前明文块的输入,然后对结果进行加密。
- CBC模式引入了初始向量(Initialization Vector, IV)来增加随机性和安全性。
- CBC模式是常用的对称加密工作模式,提供了较好的安全性和机密性。
密码反馈模式(Cipher Feedback, CFB):
- CFB模式将前一个密文块作为加密器的输入,然后对结果进行加密,再与明文块进行异或运算。
- CFB模式可以处理任意长度的数据,但对错误传输敏感。
输出反馈模式(Output Feedback, OFB):
- OFB模式将前一个密文块作为加密器的输入,然后对结果进行加密,再与明文块进行异或运算。
- OFB模式可以处理任意长度的数据,且对错误传输不敏感。
计数器模式(Counter, CTR):
- CTR模式使用一个计数器和密钥生成一个密钥流,然后将密钥流与明文进行异或运算。
- CTR模式可以并行加密和解密数据,适用于高速数据加密。
对称加密的填充方法
PKCS#5/PKCS#7填充:
- PKCS#5(密码学标准#5)和PKCS#7是填充方法的标准化规范。
- 这两种填充方法在大多数情况下是等效的,它们使用字节填充数据块,填充的字节值等于需要填充的字节数。
- 例如,如果需要填充3个字节,则填充的字节值为0x03,如果需要填充8个字节,则填充的字节值为0x08。
假设我们有一个数据块大小为8字节(64位),需要进行加密。原始数据长度为10字节,不是数据块的整数倍,因此需要进行填充。
原始数据:0x48 0x65 0x6C 0x6C 0x6F 0x20 0x57 0x6F 0x72 0x6C
填充前的数据块:0x48 0x65 0x6C 0x6C 0x6F 0x20 0x57 0x6F
PKCS#5/PKCS#7填充:在数据块的末尾添加6个字节,每个字节的值为0x06。
填充后的数据块:0x48 0x65 0x6C 0x6C 0x6F 0x20 0x57 0x6F 0x06 0x06 0x06 0x06 0x06 0x06
Zero Padding填充:
- Zero Padding填充方法将数据块的剩余空间填充为0。
- 这种填充方法简单且易于实现,但在某些情况下可能会引起歧义。
例如
假设我们有一个数据块大小为16字节(128位),需要进行加密。原始数据长度为11字节,不是数据块的整数倍,因此需要进行填充。
原始数据:0x48 0x65 0x6C 0x6C 0x6F 0x20 0x57 0x6F 0x72 0x6C 0x64
填充前的数据块:0x48 0x65 0x6C 0x6C 0x6F 0x20 0x57 0x6F 0x72 0x6C 0x64 ?? ?? ?? ?? ?? ?? ??
Zero Padding填充:在数据块的末尾添加5个字节,每个字节的值为0x00。
填充后的数据块:0x48 0x65 0x6C 0x6C 0x6F 0x20 0x57 0x6F 0x72 0x6C 0x64 0x00 0x00 0x00 0x00 0x00
ISO/IEC 7816-4填充:
- ISO/IEC 7816-4是用于智能卡的标准,定义了一种填充方法。
- 这种填充方法在数据块的末尾添加一个字节0x80,然后用0x00填充剩余的字节。
原始数据:
0x48 0x65 0x6C 0x6C 0x6F 0x21
填充前的数据块:0x48 0x65 0x6C 0x6C 0x6F 0x21 ?? ??
添加填充字节0x80:0x48 0x65 0x6C 0x6C 0x6F 0x21 0x80 ??
用0x00填充剩余字节:0x48 0x65 0x6C 0x6C 0x6F 0x21 0x80 0x00
示例:
public class DESExample {
public static void main(String[] args) throws Exception {
String originalString = "Hello, World!";
// 创建DES密钥
String keyString = "01234567"; // 8字节的密钥
byte[] keyBytes = keyString.getBytes(StandardCharsets.UTF_8);
DESKeySpec desKeySpec = new DESKeySpec(keyBytes);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
// 创建DES加密器
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// 加密
byte[] encryptedBytes = cipher.doFinal(originalString.getBytes(StandardCharsets.UTF_8));
String encryptedString = Base64.getEncoder().encodeToString(encryptedBytes);
System.out.println("Encrypted String: " + encryptedString);
// 创建DES解密器
cipher.init(Cipher.DECRYPT_MODE, secretKey);
// 解密
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedString));
String decryptedString = new String(decryptedBytes, StandardCharsets.UTF_8);
System.out.println("Decrypted String: " + decryptedString);
}
}
output:
Encrypted String: NoOAnzv8SVfhQKHpy4zFaA==
Decrypted String: Hello, World!
在这个例子中,我们使用DES算法对字符串"Hello, World!"进行加密和解密。首先,我们创建了一个8字节的DES密钥,然后使用密钥创建了加密器和解密器。我们使用加密器对原始字符串进行加密,并将加密后的结果进行Base64编码以便于显示和传输。然后,我们使用解密器对加密后的字符串进行解密,并将解密后的结果转换为字符串进行显示。
补充: 口令加密
在Java中,口令加密(Password-based Encryption, PBE)是一种特殊的对称加密方式,它使用用户提供的口令(密码)来生成加密密钥。与常规的对称加密不同,口令加密使用用户的口令作为密钥,而不是使用随机生成的密钥。
口令加密与常规的对称加密之间的主要区别如下:
-
密钥生成:对称加密使用随机生成的密钥,而口令加密使用用户提供的口令作为密钥。用户输入的口令通过一系列的算法转换为密钥。
-
密钥的安全性:对称加密的密钥通常是随机生成的,具有足够的长度和复杂性,以提供较高的安全性。而口令加密的密钥是由用户提供的,其安全性取决于用户选择的口令的强度。
-
密钥管理:对称加密的密钥可以通过安全的密钥交换协议进行传输和存储。而口令加密的密钥是用户提供的,需要安全地存储和管理,以防止未经授权的访问。
-
使用场景:对称加密适用于需要高效加密和解密大量数据的场景,而口令加密适用于需要使用用户提供的口令进行加密和解密的场景,如用户密码存储。
流程:
- 口令生成密钥:用户输入口令作为输入,通过一系列的算法转换为加密算法所需的密钥。这个过程通常包括口令转换、盐值生成和迭代次数等步骤。
- 加密器初始化:使用生成的密钥,初始化加密器。加密器的初始化参数可能包括加密算法、工作模式、填充方式、盐值和迭代次数等。
- 加密:将原始数据作为输入,使用初始化的加密器对数据进行加密。加密器将原始数据和密钥作为输入,生成加密后的密文作为输出。
- 密文传输或存储:将加密后的密文传输给接收方或存储在某个位置。由于密文是经过加密的,第三方无法直接获取原始数据。
- 解密器初始化:接收方使用相同的口令生成密钥,并使用初始化参数初始化解密器。
- 解密:使用初始化的解密器,对接收到的密文进行解密。解密器将密文和密钥作为输入,生成解密后的原始数据作为输出。
- 获取原始数据:接收方获取解密后的原始数据,可以进行进一步的处理或显示。
案例:
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.nio.charset.StandardCharsets;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;
import java.util.Base64;
public class PBEEncryptionExample {
public static void main(String[] args) throws Exception {
String originalString = "Hello, World!";
String password = "MySecretPassword";
// 创建口令加密参数
byte[] salt = {0x01, 0x23, 0x45, 0x67, (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF};
int iterationCount = 1000;
// 创建口令加密密钥
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, iterationCount);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey secretKey = keyFactory.generateSecret(keySpec);
// 创建口令加密器
Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES");
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec);
// 加密
byte[] encryptedBytes = cipher.doFinal(originalString.getBytes(StandardCharsets.UTF_8));
String encryptedString = Base64.getEncoder().encodeToString(encryptedBytes);
System.out.println("Encrypted String: " + encryptedString);
// 创建口令解密器
cipher.init(Cipher.DECRYPT_MODE, secretKey, paramSpec);
// 解密
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedString));
String decryptedString = new String(decryptedBytes, StandardCharsets.UTF_8);
System.out.println("Decrypted String: " + decryptedString);
}
}
非对称加密
非对称加密是一种加密方式,使用一对密钥(公钥和私钥)来加密和解密数据。公钥用于加密数据,而私钥用于解密数据。这种加密方式通常用于安全通信,如SSL/TLS协议中的数据传输。常见的非对称加密算法包括RSA、DSA和ECC。这些算法的安全性建立在数学难题的基础上,如大素数分解和椭圆曲线离散对数问题。
优缺点
优点
- 安全性高:公钥用于加密数据,私钥用于解密数据,保护了数据的安全性。
- 方便密钥交换:不需要事先共享密钥,可以通过公钥加密传输密钥。
- 数字签名:私钥用于签名数据,公钥用于验证签名,可以确保数据的完整性和真实性。
缺点
- 计算复杂度高:相比对称加密,非对称加密的计算量更大,效率较低。
- 加密速度慢:由于复杂的数学运算,非对称加密的加密速度比对称加密慢。
- 密钥管理困难:需要安全地管理公钥和私钥,避免泄露和被篡改。
非对称加密算法实现机密信息交换的基本过程是:甲方生成一对密钥并将其中的一把作为公用密钥向其它方公开;得到该公用密钥的乙方使用该密钥对机密信息进行加密后再发送给甲方;甲方再用自己保存的另一把专用密钥对加密后的信息进行解密。
非对称加密的流程
非对称加密的整个流程在Java中通常涉及到生成密钥对、加密和解密数据的过程。以下是详细描述:
生成密钥对:
// 生成密钥对
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048); // 指定密钥长度
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
使用公钥加密数据:
// 使用公钥加密数据
byte[] data = "Hello, World!".getBytes();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedData = cipher.doFinal(data);
使用私钥解密数据:
// 使用私钥解密数据
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedData = cipher.doFinal(encryptedData);
String decryptedText = new String(decryptedData);
System.out.println("解密后的数据:" + decryptedText);
示例:
import javax.crypto.Cipher;
import java.security.*;
public class AsymmetricEncryptionExample {
public static void main(String[] args) throws Exception {
// 生成密钥对
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// 加密数据
String originalText = "Hello, World!";
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedData = cipher.doFinal(originalText.getBytes());
// 解密数据
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedData = cipher.doFinal(encryptedData);
String decryptedText = new String(decryptedData);
// 输出结果
System.out.println("原始数据:" + originalText);
System.out.println("加密后的数据:" + new String(encryptedData));
System.out.println("解密后的数据:" + decryptedText);
}
}
标签:摘要,加密,String,数字签名,Base64,密钥,Java,byte From: https://blog.csdn.net/qq_45922256/article/details/136871014原始数据:Hello, World!
加密后的数据:wP*Ev�[�N���K�g(u��E.H�WKaTeX parse error: Unexpected character: ' ' at position 1: ̲Μ�3[�`��p):� …���iGs��G���Ɓg�dJX��oBrټ%*v���Euܗ�Sl,�Lg5�w}-�8�x,4(�c k�T3�����+鋌SW�dyy�x�
��Bp���|۠w� H�&V��e�6¬�q���N�
解密后的数据:Hello, World!