目录
- 数据加密
- 介绍
- 使用场景
- 密码学历史
- 古代密码学
- 凯撒密码
- 例子
- 特点
- 维吉尼亚密码
- 原理
- 例子
- 特点
- 现代密码学
- 介绍
- 现代密码学的加密算法分类
- 哈希算法
- 优点
- 缺点
- 代码示例【封装写法】
- 对称加密算法
- 对称加密算法的加密过程
- 解密过程
- 对称加密算法的优点:
- 对称加密算法的缺点:
- 代码示例【封装写法】
- 非对称密钥加密
- RSA算法
- 非对称加密算法的特点
- 代码示例
- 数字签名算法
- 详细解释:
- 优点
- 缺点
- 代码示例
- 解释
- 密钥交换算法
- 密钥交换算法d额两个主要目标
- 密钥交换算法方式
- Diffie-Hellman密钥交换算法
- 具体过程
- 椭圆曲线Diffie-Hellman密钥交换算法(ECDH)
- 密钥交换算法的优点
- 密钥交换算法的局限性:
- 代码示例
- Diffie-Hellman算法
- 公钥证书
- 公钥证书内容
- 使用公钥证书进行身份验证和加密通信的过程
- 优点
- 代码示例
数据加密
介绍
- 数据加密指的是将数据转换为密文,使得非授权人员无法理解和访问数据的过程。
- 数据加密主要通过使用算法和密钥对原始数据进行转换,从而使其变得不可读或难以理解。
- 只有拥有正确密钥的授权人员才能解密和访问数据。
使用场景
- 包括保护个人隐私
- 保护商业机密
- 确保数据的完整性和安全传输等
密码学历史
密码学是一门研究如何保护信息安全的学科。它的历史可以追溯到几千年前的古代。
古代密码学
古代人们已经开始使用各种加密技术来保护重要信息。以下是一些古代的密码学方法:
- 凯撒密码(Caesar cipher):最早记录于古罗马时期,通过将字母按照一定规律进行替换,实现简单的加密。
- 维吉尼亚密码(Vigenère cipher):16世纪时由法国人布利斯·德·维吉尼亚提出的多表密码,通过使用不同的密码表对明文进行加密。
凯撒密码
- 凯撒密码(Caesar cipher)是最早的已知密码算法之一,发明于古罗马时代。它是一种简单的替换密码,通过将字母按照一定规律进行替换来实现加密。
- 具体来说,凯撒密码采用了一种位移(shift)的方法。加密时,将明文中的每个字母按照一个固定的位移值进行移动,生成密文。解密时,将密文中的每个字母反向移动相同的位移值,就可以恢复出原始的明文。
例子
- 将明文 “HELLO” 使用凯撒密码进行加密,假设位移值为3,则将字母按照顺序位移3位得到密文 “KHOOR”。
- 解密时将密文字母反向位移3位,则可以得到原始的明文 “HELLO”。
上面是移位法,还有单表替换法和多表替换法。
- 单表替换:原文和密文使用的是同一张表
- 例如:abcde ------> sdfvs
- 原文cdd----->密文:fvv
- 多表替换:表示有多张表,原文和密文进行对比
- 表单1:abcde-sdfvs、表单2:abcde-tyfgs、表单3:abcde-bdfhr
- 原文:cdd
- 密钥:332
- 密文:fhg
特点
- 凯撒密码的算法非常简单,容易理解和实现。
- 然而,它的安全性非常低,因为密钥空间很小,只有26种可能的位移值,可以通过简单的穷举攻击破解。
- 因此,凯撒密码在现代密码学中并不常用,但它具有教育和历史意义,是了解密码学基础的重要起点。
维吉尼亚密码
维吉尼亚密码(Vigenère cipher)是一种多表替换密码,由布鲁托·维吉尼亚(Blaise de Vigenère)在16世纪发明。相比凯撒密码,维吉尼亚密码更加复杂和安全。
原理
- 维吉尼亚密码使用了一个关键词(key)作为密钥,用于确定每个字母的位移值。
- 关键词中的每个字母对应一个数值,将这个数值与明文中的字母相加(取模26),得到密文中对应字母的值。
- 如果关键词长度小于明文长度,关键词将循环使用直到加密完整的明文。
例子
例如,使用关键词 “KEY” 对明文 “HELLO” 进行加密。
- 首先将关键词扩展到与明文相同的长度,得到 “KEYKE”。
- 然后将明文的每个字母与关键词相应位置的字母相加,并对26取模运算,得到密文 “RIJVS”。
- 解密时,使用相同的关键词和相反的运算,将密文中的字母与关键词对应位置的字母相减,并对26取模运算,得到原始的明文。
特点
- 维吉尼亚密码相对于凯撒密码更加安全,因为每个字母的位移值不再恒定,而是根据关键词的每个字母决定。
- 这使得破解更加困难,需要更复杂的分析方法。
- 然而,维吉尼亚密码仍然可以通过一些密码分析技术进行破解,特别是在关键词较短的情况下。
- 在现代密码学中,维吉尼亚密码通常作为基础算法进行改进或组合使用。
现代密码学
介绍
现代密码学的发展主要集中在20世纪。以下是一些重要的里程碑:
- Enigma密码机:在第一次世界大战和第二次世界大战期间,纳粹德国使用的一种电机械密码机,被认为是最复杂和最具挑战性的密码系统之一。
- 数据加密标准(Data Encryption Standard,DES):在20世纪70年代中期,美国国家标准局(NBS)委托IBM开发的一种对称密钥加密算法。
- 公钥密码学:在1970年代末和1980年代初,由美国的Whitfield Diffie和Martin Hellman提出了公钥密码学概念,为密码学领域带来了重大突破。公钥密码学使用一对密钥,其中一个是公开的(公钥),另一个是私密的(私钥)。
现代密码学涉及了许多复杂的算法和协议,如RSA、AES、SHA等。随着计算机技术的发展,密码学在信息安全领域扮演着非常重要的角色,保护着个人隐私和敏感数据的安全。
现代密码学的加密算法分类
现代密码学中常用的加密算法主要包括:
- 哈希算法:将任意长度的数据映射为固定长度的哈希值,不可逆的算法,常见的有MD5、SHA-1、SHA-256等。
- 对称加密算法:使用同一个密钥进行加密和解密的算法,常见的有DES、3DES、AES等。
- 非对称加密算法:使用一对密钥进行加密和解密的算法,常见的有RSA、Diffie-Hellman、ECC等。
- 数字签名算法:使用非对称加密算法和哈希算法进行数字签名和验证,常见的有RSA、DSA等。
- 密钥交换算法:用于安全地交换密钥的算法,常见的有Diffie-Hellman、ECDH等。
- 公钥证书:用于身份验证和加密通信的数字证书,常见的有X.509证书。
这些加密算法可以应用于各种场景,如数据加密、身份验证、数字签名、安全通信等。
哈希算法
哈希算法(Hash algorithm)是一种将任意长度的数据映射为固定长度哈希值的算法。
优点
- 固定长度输出:哈希算法的输出通常为固定长度的哈希值。无论输入数据的大小是多少,哈希值的长度都是固定的。
- 不可逆性:给定一个哈希值,很难通过逆推计算出原始的输入数据。哈希算法是单向的,只能从输入计算出哈希值,而不能从哈希值还原出输入。
- 高效性:哈希算法的计算速度通常很快,对于任意长度的输入数据,都能够在短时间内计算出对应的哈希值。
- 碰撞概率很低:碰撞指的是两个不同的输入数据通过哈希算法得到了相同的哈希值。好的哈希算法应该具有非常低的碰撞概率,即使输入数据稍有变化,得到的哈希值也应该是完全不同的。
缺点
- 不可逆性:虽然哈希算法的不可逆性是一种优点,可以保护数据的安全性,但有时候在特定场景下需要逆向操作。例如,对于密码找回功能,用户忘记密码时,无法通过哈希值逆向计算出原始密码。
- 碰撞攻击:虽然优秀的哈希算法具有非常低的碰撞概率,但并非完全不可能发生碰撞。碰撞是指两个不同的输入数据通过哈希算法得到了相同的哈希值。虽然发生碰撞的概率非常低,但在密码学中,攻击者可能会专门构造数据,以达到碰撞的目的,从而破坏哈希算法的安全性。
- 预映射攻击:预映射攻击是指攻击者在预先计算出一些已知输入数据的哈希值,然后将这些哈希值与目标哈希值进行比较,以获得目标输入数据的可能值。这种攻击方法可以绕过对抗碰撞攻击的措施。
- 依赖输入数据:哈希算法的输出结果依赖于输入数据。即使输入数据发生了微小的变化,得到的哈希值也会完全不同。这可能会导致在某些情况下,进行数据比较或查找时出现困难。
代码示例【封装写法】
package com.kgc.main;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @author: zjl
* @datetime: 2024/6/1
* @desc: 复兴Java,我辈义不容辞
*/
public class HashEncryptionDemo {
// 哈希算法
public enum HashAlgorithm {
MD5, SHA1, SHA256
}
// 哈希加密
public static String hash(String plaintext, HashAlgorithm algorithm) throws NoSuchAlgorithmException {
// 创建哈希算法的实例
MessageDigest md = MessageDigest.getInstance(algorithm.toString());
// 计算哈希值
byte[] hashedBytes = md.digest(plaintext.getBytes(StandardCharsets.UTF_8));
// 将哈希值转换为十六进制字符串
StringBuilder sb = new StringBuilder();
for (byte b : hashedBytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
public static void main(String[] args) {
try {
String plaintext = "123456";
HashAlgorithm algorithm = HashAlgorithm.SHA256;
// 哈希加密
String hashedText = hash(plaintext, algorithm);
System.out.println("Hashed Text: " + hashedText);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
解密
一些简单的密码完全可以直接破解:https://www.cmd5.com/
对称加密算法
- 对称加密算法是一种使用相同的密钥对明文进行加密和解密的算法。
- 具体而言,对称加密算法通过一种数学运算将明文转换为密文,然后通过相同的数学运算将密文转换回明文。
- 这种密钥对称的特性使得对称加密算法在数据传输和存储过程中非常高效。
对称加密算法的加密过程
- 首先,选取一个密钥。对称加密算法使用的密钥长度通常为128位或256位,较长的密钥长度会增加加密的安全性。
- 将明文划分为合适的块,并应用算法将每个块转换为相应的密文块。对称加密算法通常使用的算法有DES、AES等。
- 密文块通过网络或存储介质传输或保存。
解密过程
与加密过程正好相反:
- 接收到密文块后,使用相同的密钥和相反的算法将密文块转换为相应的明文块。
- 将明文块组合起来,就可以得到完整的明文。
对称加密算法的优点:
- 加密解密速度快。由于使用相同的密钥进行加密解密,所以运算速度很快。
- 实现简单。对称加密算法的实现较为简单,计算机硬件支持较好。
对称加密算法的缺点:
- 密钥管理困难。加密和解密的双方需要共享密钥,并且需要保证密钥的安全性,如果密钥泄露,所有的信息都会暴露。
- 不适合大规模网络通信。在大规模网络通信中,由于需要共享密钥,所以密钥的分发和管理会非常复杂。
- 缺乏不可抵御的密码。对称加密算法的密钥是对其安全性的关键,如果密钥被破解,那么加密也就没有任何意义了。
代码示例【封装写法】
package com.kgc.main;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.Base64;
/**
* @author: zjl
* @datetime: 2024/6/1
* @desc: 复兴Java,我辈义不容辞
*/
public class SymmetricEncryptionDemo {
// 加密算法
public enum EncryptionAlgorithm {
AES, DES, DESede
}
// 加密密码
public static String encrypt(String plaintext, String password, EncryptionAlgorithm algorithm) throws Exception {
// 创建加密算法的实例
Cipher cipher = Cipher.getInstance(algorithm.toString());
// 创建密钥对象
Key key = new SecretKeySpec(password.getBytes(), algorithm.toString());
// 设置为加密模式
cipher.init(Cipher.ENCRYPT_MODE, key);
// 进行加密计算
byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes());
// 使用Base64进行编码
return Base64.getEncoder().encodeToString(encryptedBytes);
}
// 解密密码
public static String decrypt(String encryptedText, String password, EncryptionAlgorithm algorithm) throws Exception {
// 创建解密算法的实例
Cipher cipher = Cipher.getInstance(algorithm.toString());
// 创建密钥对象
Key key = new SecretKeySpec(password.getBytes(), algorithm.toString());
// 设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, key);
// 进行解密计算
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedText);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
// 将解密后的字节数组转换为字符串
return new String(decryptedBytes);
}
public static void main(String[] args) {
try {
String plaintext = "syhy_2YH";
String password = "ThisIsTheEncryptionKey";
EncryptionAlgorithm algorithm = EncryptionAlgorithm.AES;
// 加密
String encryptedText = encrypt(plaintext, password, algorithm);
System.out.println("Encrypted Text: " + encryptedText);
// 解密
String decryptedText = decrypt(encryptedText, password, algorithm);
System.out.println("Decrypted Text: " + decryptedText);
} catch (Exception e) {
e.printStackTrace();
}
}
}
非对称密钥加密
- 非对称加密算法使用一对密钥来进行加密和解密操作,其中一个密钥称为公钥,另一个密钥称为私钥。
- 公钥可以公开,而私钥必须保密。
- 使用公钥加密的数据只能使用私钥解密,而使用私钥加密的数据只能使用公钥解密。
RSA算法
非对称加密算法的一个常见例子是RSA算法。RSA算法的加密过程如下:
- 选择两个不同的质数p和q,并计算出它们的乘积n。
- 计算n的欧拉函数φ(n) = (p-1)(q-1)。
- 选择一个与φ(n)互质的整数e(通常为65537),作为公钥的一部分。
- 计算e的模φ(n)的逆元d,作为私钥的一部分。
- 公钥是(n, e),私钥是(n, d)。
- 加密时,将明文m使用公式c = m^e mod n进行加密。
- 解密时,将密文c使用公式m = c^d mod n进行解密,得到原始的明文。
非对称加密算法的特点
- 安全性好:由于公钥是公开的,攻击者无法通过公钥推导出私钥,从而保证了数据的安全性。
- 适合密钥交换:非对称加密算法可以用于安全地交换密钥。例如,Alice可以使用Bob的公钥加密一条消息,只有Bob能够使用自己的私钥解密该消息。
- 加密速度较慢:相对于对称加密算法,非对称加密算法的加密速度较慢。因此,通常在对称加密算法中使用非对称加密算法来交换密钥,然后使用对称加密算法进行快速加密。
- 密钥长度较长:为了保证足够的安全性,非对称加密算法的密钥长度通常需要比对称加密算法的密钥长度长。
- 尽管非对称加密算法的加密速度较慢和密钥长度较长,但其安全性优势使得它在许多安全通信和加密应用中得到了广泛应用。
代码示例
package com.kgc.main;
import javax.crypto.*;
import java.security.*;
import java.util.Base64;
/**
* @author: zjl
* @datetime: 2024/6/1
* @desc: 复兴Java,我辈义不容辞
*/
public class AsymmetricEncryptionDemo {
private static final String RSA = "RSA";
private static final String DSA = "DSA";
private static final String EC = "EC";
private static final int KEY_SIZE = 2048;
public static void main(String[] args) throws Exception {
String plainText = "syhy_2YH";
// 生成密钥对
KeyPair keyPair = generateKeyPair(RSA);
// 使用公钥加密
byte[] encryptedData = encrypt(plainText, keyPair.getPublic(), RSA);
//一般情况下公钥和私钥会先计算出来然后保存起来
// 使用私钥解密
String decryptedText = decrypt(encryptedData, keyPair.getPrivate(), RSA);
System.out.println("Encrypted Data: " + Base64.getEncoder().encodeToString(encryptedData));
System.out.println("Decrypted Text: " + decryptedText);
}
public static KeyPair generateKeyPair(String algorithm) throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
keyPairGenerator.initialize(KEY_SIZE);
return keyPairGenerator.generateKeyPair();
}
public static byte[] encrypt(String plainText, PublicKey publicKey, String algorithm)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, IllegalBlockSizeException, BadPaddingException {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(plainText.getBytes());
}
public static String decrypt(byte[] encryptedData, PrivateKey privateKey, String algorithm)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedData = cipher.doFinal(encryptedData);
return new String(decryptedData);
}
}
数字签名算法
- 数字签名是一种用于验证和保护数据完整性以及身份认证的加密技术。
- 它涉及使用私钥对数据进行加密来创建数字签名,并使用相应的公钥对数字签名进行验证。
详细解释:
-
非对称密钥算法:数字签名使用非对称密钥算法,也称为公钥密码算法。这些算法使用两个密钥:一个私钥用于签名生成,另一个公钥用于验证签名。
-
加密和解密过程:数字签名首先对原始数据进行加密。加密过程使用私钥,它将原始数据的哈希值加密生成数字签名。数字签名本质上是原始数据的加密摘要。
-
验证过程:验证数字签名涉及使用相应的公钥和原始数据来解密签名。解密后的结果应与原始数据的哈希值相匹配。如果匹配成功,则表示数字签名是有效的,并且数据的完整性未被篡改。
-
私钥的保密性:私钥用于生成数字签名,并且必须保持机密,只有拥有私钥的实体才能对数据进行签名。私钥的机密性很重要,否则如果私钥泄露,攻击者可以伪造数字签名来进行欺骗。
-
公钥的分发:公钥用于验证数字签名,可以公开分发给需要验证数据完整性的人。任何人都可以使用公钥来验证数字签名,以确保数据的完整性。
-
数据完整性和身份验证:数字签名提供了数据完整性和身份验证。通过验证数字签名,接收方可以确信数据在传输过程中未被篡改,并且可以确定发送方的身份。
-
常用的数字签名算法:常见的数字签名算法包括RSA (Rivest-Shamir-Adleman)、DSA (Digital Signature Algorithm)和ECDSA (Elliptic Curve Digital Signature Algorithm)等。
优点
-
数据完整性:数字签名可以确保数据在传输或存储过程中未被篡改。接收方可以使用公钥来验证数字签名,如果签名验证成功,则可以确定数据的完整性。
-
身份认证:数字签名可以验证数据发送方的身份。通过验证数字签名,接收方可以确信数据来自于指定的发送方。
-
防止抵赖:数字签名可以防止发送方在数据被接收方验证之后否认其发送的数据。由于数字签名是基于私钥生成的,发送方无法否认自己生成的数字签名。
-
安全性:使用非对称密钥算法进行数字签名可以提供更高的安全性。私钥的保密性保证了数字签名的真实性,而公钥的公开性使得任何人都可以验证签名。
缺点
- 复杂性:数字签名的实现相对复杂,涉及到使用私钥加密和公钥验证的过程。正确地生成和验证数字签名需要具备相关的加密算法和密钥管理技术。
- 密钥管理:数字签名需要使用配对的公钥和私钥进行加密和验证。因此,密钥的生成、分发、存储和更新需要进行有效的管理,以防止私钥泄露和公钥被篡改。
- 性能开销:与对称密钥加密相比,非对称密钥算法具有较大的计算和存储开销。生成和验证数字签名可能需要更多的计算资源和存储空间。
代码示例
package com.kgc.main;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
/**
* @author: zjl
* @datetime: 2024/6/1
* @desc: 复兴Java,我辈义不容辞
*/
public class DigitalSignatureDemo {
public static void main(String[] args) {
try {
String message = "syhy_2YH";
KeyPair keyPair = generateKeyPair();
// 使用私钥对消息进行签名
byte[] signature = sign(message.getBytes(), keyPair.getPrivate());
// 使用公钥对签名进行验证
boolean isValid = verify(message.getBytes(), signature, keyPair.getPublic());
System.out.println("Signature is valid: " + isValid);
} catch (Exception e) {
e.printStackTrace();
}
}
// 生成RSA密钥对
public static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
return keyGen.generateKeyPair();
}
// 使用私钥对消息进行签名
public static byte[] sign(byte[] message, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(message);
return signature.sign();
}
// 使用公钥对签名进行验证
public static boolean verify(byte[] message, byte[] signature, PublicKey publicKey) throws Exception {
Signature verifier = Signature.getInstance("SHA256withRSA");
verifier.initVerify(publicKey);
verifier.update(message);
return verifier.verify(signature);
}
// 将字节数组转换为十六进制字符串
public static String bytesToHex(byte[] bytes) {
StringBuilder result = new StringBuilder();
for (byte b : bytes) {
result.append(String.format("%02x", b));
}
return result.toString();
}
// 将十六进制字符串转换为字节数组
public static byte[] hexToBytes(String hex) {
byte[] bytes = new byte[hex.length() / 2];
for (int i = 0; i < hex.length(); i += 2) {
bytes[i / 2] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
}
return bytes;
}
}
解释
- generateKeyPair方法用于生成RSA密钥对。
- sign方法使用私钥对消息进行签名,
- verify方法使用公钥对签名进行验证。
- bytesToHex和hexToBytes方法用于在字节数组和十六进制字符串之间进行转换。
也可以根据需要选择不同的数字签名算法,例如MD5、SHA-1或SHA-256,并使用相应的签名算法来初始化Signature对象。同样也可以使用不同的密钥长度来生成RSA密钥对。
密钥交换算法
- 密钥交换算法(Key Exchange Algorithm)是一种用于在通信双方之间安全地交换密钥的方法。
- 密钥交换算法的目的是确保通信双方能够协商出一个共享的密钥,以便在后续的通信中使用对称密钥加密算法进行加密和解密。
密钥交换算法d额两个主要目标
- 保证通信双方之间的密钥协商过程是安全的
- 确保协商出的密钥对第三方是不可预测的
密钥交换算法方式
常见的密钥交换算法包括Diffie-Hellman密钥交换算法和椭圆曲线Diffie-Hellman密钥交换算法(ECDH)。
Diffie-Hellman密钥交换算法
- 是一种基于数论的密钥交换协议。
- 它允许两个通信双方在不共享密钥的情况下协商出一个共享密钥。
具体过程
- 通信双方首先要共同选择并公开一个大素数p和一个原根g。
- 每一方选择一个私密的随机数(私钥),并使用p和g计算出对应的公开值(公钥)。
- 每一方将自己的公钥发送给对方。
- 每一方使用对方发送的公钥和自己的私钥计算出一个共享的秘密值。
- 通信双方最终得到的共享秘密值就是双方协商出的对称密钥。
椭圆曲线Diffie-Hellman密钥交换算法(ECDH)
- 与Diffie-Hellman密钥交换算法类似,但使用的是椭圆曲线加密算法。
- 椭圆曲线加密算法具有更高的安全性和更短的密钥长度,因此在实际应用中更常用。
密钥交换算法的优点
- 安全性:密钥交换算法确保密钥协商过程的安全性,使得通信双方只有在彼此验证对方的身份之后才能协商出共享密钥。
- 多方通信:密钥交换算法可以支持多个通信双方之间的密钥协商,而不需要对每对通信双方都进行一对一的密钥交换。
- 密钥长度:密钥交换算法通常使用较短的密钥长度,这样可以节省资源并提高加密和解密的效率。
密钥交换算法的局限性:
- 中间人攻击:如果双方无法验证对方的公钥的真实性,存在中间人攻击的风险,攻击者可以伪装成通信双方并获取密钥。
- 密钥管理:密钥交换算法只负责协商出共享密钥,对于密钥的生成、存储和管理,通信双方仍然需要采取适当的安全措施。
- 密钥更新:由于密钥交换算法只负责协商出初始密钥,双方需要定期更新密钥以维护通信的安全性。
因此,在使用密钥交换算法时,需要综合考虑安全性和性能,并采取合适的安全措施来保护密钥交换过程和协商出的密钥。
代码示例
Diffie-Hellman算法
package com.kgc.main;
import javax.crypto.KeyAgreement;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
/**
* @author: zjl
* @datetime: 2024/6/1
* @desc: 复兴Java,我辈义不容辞
*/
public class DiffieHellmanDemo {
public static void main(String[] args) throws Exception {
// 生成Diffie-Hellman公私密钥对
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DiffieHellman");
KeyPair keyPairA = keyPairGenerator.generateKeyPair();
KeyPair keyPairB = keyPairGenerator.generateKeyPair();
// 获取Diffie-Hellman参数
DHPublicKey publicKeyA = (DHPublicKey) keyPairA.getPublic();
DHPublicKey publicKeyB = (DHPublicKey) keyPairB.getPublic();
DHParameterSpec paramSpec = publicKeyA.getParams();
// 初始化KeyAgreement对象
KeyAgreement keyAgreementA = KeyAgreement.getInstance("DiffieHellman");
keyAgreementA.init(keyPairA.getPrivate(), paramSpec);
KeyAgreement keyAgreementB = KeyAgreement.getInstance("DiffieHellman");
keyAgreementB.init(keyPairB.getPrivate(), paramSpec);
// 计算本地的共享密钥
keyAgreementA.doPhase(publicKeyB, true);
keyAgreementB.doPhase(publicKeyA, true);
// 生成双方的共享密钥
byte[] secretKeyA = keyAgreementA.generateSecret();
byte[] secretKeyB = keyAgreementB.generateSecret();
// 验证双方的共享密钥是否一致
System.out.println("双方的共享密钥是否一致:" + java.util.Arrays.equals(secretKeyA, secretKeyB));
}
}
公钥证书
- 公钥证书是一种数字证书,用于验证公钥的身份和完整性。
- 它是由权威的证书颁发机构(CA)签发的,包含了公钥和与该公钥相关的其他信息。
- 公钥证书基于公钥基础设施(PKI)体系,并被广泛用于加密通信和身份验证领域。
公钥证书内容
一个公钥证书通常包含以下信息:
-
证书持有者的身份信息:证书中会包含证书持有者(也称为主体)的身份信息,如名称、电子邮件地址或组织名称等。这些信息可以帮助其他人或系统确认证书持有者的身份。
-
证书持有者的公钥:证书中包含了证书持有者的公钥。公钥是一对密钥中的一个,用于加密和验证数字签名。公钥可被其他人使用来加密数据或验证数字签名。
-
证书颁发机构的签名:证书颁发机构会对证书进行数字签名,以确认证书的真实性和完整性。签名是通过使用证书颁发机构的私钥对证书的摘要进行加密生成的,其他人可以使用证书颁发机构的公钥验证签名的有效性。
-
证书有效期:证书中包含了证书的有效期限,通常包括起始日期和截止日期。证书在有效期内是可信的,超过有效期后应该不再使用。
使用公钥证书进行身份验证和加密通信的过程
-
当一个实体需要验证证书持有者的身份时,它会获取证书并从证书中提取公钥。
-
实体使用证书颁发机构的公钥来验证证书的签名。如果签名有效,那么证书被认为是有效的。
-
实体使用证书持有者的公钥来进行加密或验证数字签名。
优点
- 公钥证书的主要优点是能够提供身份验证和加密通信的安全性。
- 通过使用权威的证书颁发机构签发的证书,可以确保证书持有者的身份是可信的,并且公钥是完整和未篡改的。
- 这使得公钥证书成为一种重要的工具,用于确保安全的网络通信和身份验证。
代码示例
依赖
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.70</version> <!-- 使用最新版本 -->
</dependency>
根据你的需求调整日期、DN(Distinguished Name)和其他证书属性。
package com.kgc.main;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.GregorianCalendar;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
/**
* @author: zjl
* @datetime: 2024/6/1
* @desc: 复兴Java,我辈义不容辞
*/
public class CertificateExample {
public static void main(String[] args) throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
// 生成RSA密钥对
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", "BC");
keyGen.initialize(2048);
KeyPair keyPair = keyGen.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
// 创建X509证书
X500Name dnName = new X500Name("CN=Test User, O=Your Organization, L=City, ST=State, C=Country");
Date startDate = new GregorianCalendar(2022, 1, 1).getTime();
Date endDate = new GregorianCalendar(2024, 12, 31).getTime();
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(dnName, BigInteger.ONE, startDate, endDate, dnName, publicKey);
ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").build(privateKey);
X509CertificateHolder certificate = certBuilder.build(signer);
// 将证书保存到文件
saveCertificateToFile(certificate, "test.crt");
// 读取并验证证书
Certificate loadedCert = loadCertificateFromFile("test.crt");
if (loadedCert instanceof X509Certificate) {
X509Certificate x509Cert = (X509Certificate) loadedCert;
x509Cert.checkValidity();
System.out.println("Certificate is valid.");
} else {
System.out.println("Loaded certificate is not an X.509 certificate.");
}
}
private static void saveCertificateToFile(X509CertificateHolder cert, String filename) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filename)) {
fos.write(cert.getEncoded());
}
}
private static Certificate loadCertificateFromFile(String filename) throws Exception {
try (FileInputStream fis = new FileInputStream(filename)) {
return CertificateFactory.getInstance("X.509").generateCertificate(fis);
}
}
}
- 先生成了一个RSA密钥对
- 然后创建了一个自签名的X.509证书,并将其保存到文件。
- 接着,它从文件中加载证书并验证其有效性。