概述
非对称加密算法使用一对密钥:公钥用于加密,私钥用于解密。这种算法解决了密钥分发问题,但计算开销较大。
目录
RSA
原理
RSA基于大整数分解的困难性问题。使用公钥加密、私钥解密,或私钥签名、公钥验证。
核心数学原理:
- 选择两个大素数 p 和 q
- 计算 n = p × q
- 计算 φ(n) = (p-1) × (q-1)
- 选择公钥指数 e(通常65537)
- 计算私钥指数 d,使得 e × d ≡ 1 (mod φ(n))
- 公钥:(n, e),私钥:(n, d)
加密: c = m^e mod n 解密: m = c^d mod n
技术规格
| 属性 | 值 |
|---|---|
| 密钥长度 | 1024/2048/3072/4096位 |
| 安全级别 | 1024位已不安全,推荐2048位以上 |
| 签名长度 | 等于密钥长度 |
| 最大加密数据 | 密钥长度 - 11字节(PKCS1填充) |
| 状态 | 广泛使用,标准算法 |
应用场景
- HTTPS/TLS:证书签名和密钥交换
- 数字签名:代码签名、文档签名
- 邮件加密:PGP/GPG
- SSH:身份认证
- 区块链:某些加密货币
- API安全:JWT签名、OAuth
性能影响
- 加密/解密速度:慢(相比对称加密)
- 密钥生成时间:慢(生成大素数)
- 内存占用:高(大密钥)
- 吞吐量:低(不适合大数据)
- 典型使用:加密对称密钥,而非直接加密数据
Java实现示例
import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class RSAExample {
/**
* 生成RSA密钥对
*/
public static KeyPair generateKeyPair(int keySize) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(keySize); // 2048, 3072, 4096
return keyPairGenerator.generateKeyPair();
}
/**
* RSA公钥加密(适合小数据)
*/
public static String encrypt(String plaintext, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] plaintextBytes = plaintext.getBytes("UTF-8");
// RSA加密有数据长度限制,需要分块加密
int blockSize = getBlockSize(publicKey);
int offset = 0;
StringBuilder result = new StringBuilder();
while (offset < plaintextBytes.length) {
int length = Math.min(blockSize, plaintextBytes.length - offset);
byte[] block = new byte[length];
System.arraycopy(plaintextBytes, offset, block, 0, length);
byte[] encrypted = cipher.doFinal(block);
result.append(Base64.getEncoder().encodeToString(encrypted));
offset += length;
if (offset < plaintextBytes.length) {
result.append(":");
}
}
return result.toString();
}
/**
* RSA私钥解密
*/
public static String decrypt(String encryptedText, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
String[] blocks = encryptedText.split(":");
StringBuilder result = new StringBuilder();
for (String block : blocks) {
byte[] encrypted = Base64.getDecoder().decode(block);
byte[] decrypted = cipher.doFinal(encrypted);
result.append(new String(decrypted, "UTF-8"));
}
return result.toString();
}
/**
* 获取RSA加密块大小
*/
private static int getBlockSize(PublicKey publicKey) {
// PKCS1填充:密钥长度/8 - 11字节
// 2048位密钥:256 - 11 = 245字节
int keySize = ((java.security.interfaces.RSAPublicKey) publicKey).getModulus().bitLength();
return (keySize / 8) - 11;
}
/**
* RSA私钥签名
*/
public static String sign(String data, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(data.getBytes("UTF-8"));
byte[] signBytes = signature.sign();
return Base64.getEncoder().encodeToString(signBytes);
}
/**
* RSA公钥验证签名
*/
public static boolean verify(String data, String sign, PublicKey publicKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
signature.update(data.getBytes("UTF-8"));
byte[] signBytes = Base64.getDecoder().decode(sign);
return signature.verify(signBytes);
}
/**
* 密钥对序列化
*/
public static String serializePublicKey(PublicKey publicKey) {
return Base64.getEncoder().encodeToString(publicKey.getEncoded());
}
public static String serializePrivateKey(PrivateKey privateKey) {
return Base64.getEncoder().encodeToString(privateKey.getEncoded());
}
/**
* 从字符串反序列化公钥
*/
public static PublicKey deserializePublicKey(String keyStr) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(spec);
}
/**
* 从字符串反序列化私钥
*/
public static PrivateKey deserializePrivateKey(String keyStr) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(spec);
}
/**
* 混合加密:RSA加密对称密钥,对称密钥加密数据
*/
public static class HybridEncryption {
public static class EncryptedData {
public String encryptedKey; // RSA加密的对称密钥
public String encryptedData; // 对称加密的数据
public String algorithm; // 使用的对称算法
public EncryptedData(String encryptedKey, String encryptedData, String algorithm) {
this.encryptedKey = encryptedKey;
this.encryptedData = encryptedData;
this.algorithm = algorithm;
}
}
/**
* 混合加密:生成对称密钥,用RSA加密密钥,用对称密钥加密数据
*/
public static EncryptedData encrypt(String plaintext, PublicKey publicKey) throws Exception {
// 生成对称加密密钥
javax.crypto.KeyGenerator keyGen = javax.crypto.KeyGenerator.getInstance("AES");
keyGen.init(256);
javax.crypto.SecretKey aesKey = keyGen.generateKey();
// 用RSA加密对称密钥
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
rsaCipher.init(Cipher.ENCRYPT_MODE, publicKey);
String encryptedKey = Base64.getEncoder().encodeToString(
rsaCipher.doFinal(aesKey.getEncoded()));
// 用AES加密数据
Cipher aesCipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12];
java.security.SecureRandom random = new java.security.SecureRandom();
random.nextBytes(iv);
javax.crypto.spec.GCMParameterSpec gcmSpec =
new javax.crypto.spec.GCMParameterSpec(128, iv);
aesCipher.init(javax.crypto.Cipher.ENCRYPT_MODE, aesKey, gcmSpec);
byte[] encryptedDataBytes = aesCipher.doFinal(plaintext.getBytes("UTF-8"));
// 组合IV和密文
byte[] combined = new byte[12 + encryptedDataBytes.length];
System.arraycopy(iv, 0, combined, 0, 12);
System.arraycopy(encryptedDataBytes, 0, combined, 12, encryptedDataBytes.length);
return new EncryptedData(
encryptedKey,
Base64.getEncoder().encodeToString(combined),
"AES-256-GCM"
);
}
/**
* 混合解密
*/
public static String decrypt(EncryptedData encryptedData, PrivateKey privateKey) throws Exception {
// 用RSA解密对称密钥
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
rsaCipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] aesKeyBytes = rsaCipher.doFinal(
Base64.getDecoder().decode(encryptedData.encryptedKey));
javax.crypto.SecretKey aesKey = new javax.crypto.spec.SecretKeySpec(aesKeyBytes, "AES");
// 用AES解密数据
byte[] combined = Base64.getDecoder().decode(encryptedData.encryptedData);
byte[] iv = new byte[12];
System.arraycopy(combined, 0, iv, 0, 12);
byte[] ciphertext = new byte[combined.length - 12];
System.arraycopy(combined, 12, ciphertext, 0, ciphertext.length);
javax.crypto.Cipher aesCipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding");
javax.crypto.spec.GCMParameterSpec gcmSpec =
new javax.crypto.spec.GCMParameterSpec(128, iv);
aesCipher.init(javax.crypto.Cipher.DECRYPT_MODE, aesKey, gcmSpec);
return new String(aesCipher.doFinal(ciphertext), "UTF-8");
}
}
/**
* 完整示例
*/
public static void main(String[] args) throws Exception {
System.out.println("=== RSA密钥生成 ===");
KeyPair keyPair = generateKeyPair(2048);
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
System.out.println("公钥: " + serializePublicKey(publicKey).substring(0, 50) + "...");
System.out.println("私钥: " + serializePrivateKey(privateKey).substring(0, 50) + "...");
System.out.println("\n=== RSA直接加密(小数据) ===");
String original = "Hello, RSA!";
String encrypted = encrypt(original, publicKey);
System.out.println("原文: " + original);
System.out.println("加密: " + encrypted);
String decrypted = decrypt(encrypted, privateKey);
System.out.println("解密: " + decrypted);
System.out.println("\n=== RSA签名验证 ===");
String data = "需要签名的数据";
String signature = sign(data, privateKey);
System.out.println("数据: " + data);
System.out.println("签名: " + signature.substring(0, 50) + "...");
boolean isValid = verify(data, signature, publicKey);
System.out.println("验证结果: " + isValid);
System.out.println("\n=== 混合加密(推荐方式) ===");
String largeData = "这是一段较长的数据,使用混合加密方式,RSA加密对称密钥,对称密钥加密数据。";
HybridEncryption.EncryptedData hybrid = HybridEncryption.encrypt(largeData, publicKey);
System.out.println("加密的密钥: " + hybrid.encryptedKey.substring(0, 50) + "...");
System.out.println("加密的数据: " + hybrid.encryptedData.substring(0, 50) + "...");
String decryptedLarge = HybridEncryption.decrypt(hybrid, privateKey);
System.out.println("解密结果: " + decryptedLarge);
}
}
安全建议
✅ 推荐:
- 使用2048位或更长密钥(3072/4096位用于高安全)
- 密钥长度 ≥ 数据长度(实际使用混合加密)
- 使用OAEP填充而非PKCS1(更安全)
⚠️ 避免:
- 1024位密钥(已不安全)
- 直接加密大数据(使用混合加密)
- 硬编码密钥
ECC (椭圆曲线密码学)
原理
ECC基于椭圆曲线离散对数问题(ECDLP)。相比RSA,ECC可以用更短的密钥提供相同或更高的安全性。
核心优势:
- 密钥更短:256位ECC ≈ 3072位RSA的安全性
- 计算更快:密钥生成和运算更快
- 内存更小:密钥占用空间小
技术规格
| 曲线类型 | 密钥长度 | 等价RSA长度 | 用途 |
|---|---|---|---|
| P-256 (secp256r1) | 256位 | 3072位 | 广泛使用 |
| P-384 (secp384r1) | 384位 | 7680位 | 高安全 |
| P-521 (secp521r1) | 521位 | 15360位 | 极高安全 |
| secp256k1 | 256位 | 3072位 | 比特币 |
应用场景
- TLS/HTTPS:现代浏览器支持
- 移动设备:资源受限场景
- 区块链:比特币、以太坊
- IoT设备:低功耗设备
- VPN:某些VPN协议
性能影响
- 密钥生成速度:非常快(比RSA快10-100倍)
- 加密/解密速度:快(比RSA快)
- 内存占用:低(密钥小)
- CPU使用率:低
- 吞吐量:高于RSA
Java实现示例
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.KeyAgreement;
public class ECCExample {
/**
* 生成ECC密钥对(使用P-256曲线)
*/
public static KeyPair generateKeyPair(String curveName) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
// 常用曲线:secp256r1 (P-256), secp384r1 (P-384), secp521r1 (P-521)
ECGenParameterSpec ecSpec = new ECGenParameterSpec(curveName);
keyPairGenerator.initialize(ecSpec);
return keyPairGenerator.generateKeyPair();
}
/**
* ECDH密钥交换
*/
public static byte[] deriveSharedSecret(PrivateKey privateKey, PublicKey publicKey) throws Exception {
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
keyAgreement.init(privateKey);
keyAgreement.doPhase(publicKey, true);
return keyAgreement.generateSecret();
}
/**
* ECDSA签名
*/
public static String sign(String data, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withECDSA");
signature.initSign(privateKey);
signature.update(data.getBytes("UTF-8"));
byte[] signBytes = signature.sign();
return Base64.getEncoder().encodeToString(signBytes);
}
/**
* ECDSA验证
*/
public static boolean verify(String data, String sign, PublicKey publicKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withECDSA");
signature.initVerify(publicKey);
signature.update(data.getBytes("UTF-8"));
byte[] signBytes = Base64.getDecoder().decode(sign);
return signature.verify(signBytes);
}
/**
* 密钥序列化
*/
public static String serializePublicKey(PublicKey publicKey) {
return Base64.getEncoder().encodeToString(publicKey.getEncoded());
}
public static String serializePrivateKey(PrivateKey privateKey) {
return Base64.getEncoder().encodeToString(privateKey.getEncoded());
}
/**
* 从字符串反序列化
*/
public static PublicKey deserializePublicKey(String keyStr) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("EC");
return keyFactory.generatePublic(spec);
}
public static PrivateKey deserializePrivateKey(String keyStr) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("EC");
return keyFactory.generatePrivate(spec);
}
/**
* 使用ECC进行密钥交换和加密
*/
public static class ECCEncryption {
public static class EncryptedData {
public String encryptedKey;
public String encryptedData;
public EncryptedData(String encryptedKey, String encryptedData) {
this.encryptedKey = encryptedKey;
this.encryptedData = encryptedData;
}
}
/**
* 使用ECDH派生共享密钥,然后用AES加密数据
*/
public static EncryptedData encrypt(String plaintext, PublicKey peerPublicKey,
PrivateKey myPrivateKey) throws Exception {
// ECDH密钥交换
byte[] sharedSecret = deriveSharedSecret(myPrivateKey, peerPublicKey);
// 使用KDF从共享密钥派生AES密钥(简化示例,实际应使用HKDF)
javax.crypto.MessageDigest digest = javax.crypto.MessageDigest.getInstance("SHA-256");
byte[] aesKeyBytes = digest.digest(sharedSecret);
javax.crypto.SecretKey aesKey = new javax.crypto.spec.SecretKeySpec(aesKeyBytes, "AES");
// 用AES加密数据
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12];
java.security.SecureRandom random = new java.security.SecureRandom();
random.nextBytes(iv);
javax.crypto.spec.GCMParameterSpec gcmSpec =
new javax.crypto.spec.GCMParameterSpec(128, iv);
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, aesKey, gcmSpec);
byte[] encrypted = cipher.doFinal(plaintext.getBytes("UTF-8"));
// 组合IV和密文(这里简化,实际应传输公钥)
byte[] combined = new byte[12 + encrypted.length];
System.arraycopy(iv, 0, combined, 0, 12);
System.arraycopy(encrypted, 0, combined, 12, encrypted.length);
return new EncryptedData(
Base64.getEncoder().encodeToString(aesKeyBytes), // 实际应用中不应传输密钥
Base64.getEncoder().encodeToString(combined)
);
}
}
/**
* 完整示例
*/
public static void main(String[] args) throws Exception {
System.out.println("=== ECC密钥生成 ===");
KeyPair keyPair1 = generateKeyPair("secp256r1");
KeyPair keyPair2 = generateKeyPair("secp256r1");
System.out.println("密钥对1生成完成");
System.out.println("密钥对2生成完成");
System.out.println("\n=== ECDH密钥交换 ===");
byte[] sharedSecret1 = deriveSharedSecret(keyPair1.getPrivate(), keyPair2.getPublic());
byte[] sharedSecret2 = deriveSharedSecret(keyPair2.getPrivate(), keyPair1.getPublic());
// 双方应该得到相同的共享密钥
System.out.println("共享密钥1长度: " + sharedSecret1.length + " 字节");
System.out.println("共享密钥2长度: " + sharedSecret2.length + " 字节");
System.out.println("密钥是否相同: " + java.util.Arrays.equals(sharedSecret1, sharedSecret2));
System.out.println("\n=== ECDSA签名验证 ===");
String data = "需要签名的数据";
String signature = sign(data, keyPair1.getPrivate());
System.out.println("数据: " + data);
System.out.println("签名: " + signature.substring(0, 50) + "...");
boolean isValid = verify(data, signature, keyPair1.getPublic());
System.out.println("验证结果: " + isValid);
System.out.println("\n=== 性能对比 ===");
long start = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
generateKeyPair("secp256r1");
}
long end = System.currentTimeMillis();
System.out.println("生成100个ECC密钥对耗时: " + (end - start) + "ms");
System.out.println("\n=== 不同曲线对比 ===");
String[] curves = {"secp256r1", "secp384r1", "secp521r1"};
for (String curve : curves) {
long s = System.currentTimeMillis();
generateKeyPair(curve);
long e = System.currentTimeMillis();
System.out.println(curve + " 密钥生成耗时: " + (e - s) + "ms");
}
}
}
安全建议
✅ 推荐:
- 使用P-256(secp256r1)或更高
- 使用ECDSA进行签名
- 使用ECDH进行密钥交换
⚠️ 注意:
- 选择安全的曲线(避免弱曲线)
- secp256k1用于区块链,但其他场景建议secp256r1
ElGamal
原理
ElGamal是基于离散对数问题的公钥密码系统。可以用于加密和数字签名。
技术规格
| 属性 | 值 |
|---|---|
| 密钥长度 | 1024-4096位 |
| 密文扩展 | 明文大小的2倍 |
| 安全级别 | 中等(不如RSA/ECC流行) |
| 状态 | 较少使用 |
应用场景
- PGP/GPG:某些配置中使用
- 学术研究:密码学教学
- 特定合规要求
性能影响
- 加密速度:中等
- 密文扩展:是明文的2倍
- 内存占用:中等
- 使用率:较低
Java实现示例
// 注意:Java标准库不直接支持ElGamal
// 需要使用BouncyCastle等第三方库
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.Cipher;
import java.util.Base64;
public class ElGamalExample {
static {
Security.addProvider(new BouncyCastleProvider());
}
public static KeyPair generateKeyPair(int keySize) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ElGamal", "BC");
keyPairGenerator.initialize(keySize);
return keyPairGenerator.generateKeyPair();
}
public static String encrypt(String plaintext, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance("ElGamal/None/PKCS1Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encrypted = cipher.doFinal(plaintext.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(encrypted);
}
public static String decrypt(String encryptedText, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("ElGamal/None/PKCS1Padding", "BC");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(encryptedText));
return new String(decrypted, "UTF-8");
}
public static void main(String[] args) throws Exception {
System.out.println("⚠️ 注意: ElGamal使用较少,建议使用RSA或ECC");
KeyPair keyPair = generateKeyPair(1024);
String original = "测试数据";
String encrypted = encrypt(original, keyPair.getPublic());
System.out.println("加密后: " + encrypted);
String decrypted = decrypt(encrypted, keyPair.getPrivate());
System.out.println("解密后: " + decrypted);
}
}
SM2 (国密算法)
原理
SM2是基于椭圆曲线的公钥密码算法,属于中国国家密码标准。提供加密、签名和密钥交换功能。
技术规格
| 属性 | 值 |
|---|---|
| 密钥长度 | 256位 |
| 安全性 | 等价3072位RSA |
| 曲线 | sm2p256v1 |
| 状态 | 国密标准,国内广泛使用 |
应用场景
- 政府系统:符合国密要求
- 金融行业:国内银行、支付
- 信创项目:国产化替代
- 密码合规:需要国密认证
性能影响
- 密钥生成:快(与ECC相当)
- 加密/签名:快
- 内存占用:低
- CPU使用率:低
Java实现示例
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.Base64;
public class SM2Example {
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
* 生成SM2密钥对
*/
public static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC");
ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
keyPairGenerator.initialize(sm2Spec);
return keyPairGenerator.generateKeyPair();
}
/**
* SM2加密(需要使用专门的SM2加密算法,这里简化示例)
*/
public static String encrypt(String plaintext, PublicKey publicKey) throws Exception {
// SM2加密需要专门的实现
// 这里仅作示例,实际应使用专门的SM2加密库
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("SM2", "BC");
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, publicKey);
byte[] encrypted = cipher.doFinal(plaintext.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(encrypted);
}
/**
* SM2签名
*/
public static String sign(String data, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance("SM3withSM2", "BC");
signature.initSign(privateKey);
signature.update(data.getBytes("UTF-8"));
byte[] signBytes = signature.sign();
return Base64.getEncoder().encodeToString(signBytes);
}
/**
* SM2验证
*/
public static boolean verify(String data, String sign, PublicKey publicKey) throws Exception {
Signature signature = Signature.getInstance("SM3withSM2", "BC");
signature.initVerify(publicKey);
signature.update(data.getBytes("UTF-8"));
byte[] signBytes = Base64.getDecoder().decode(sign);
return signature.verify(signBytes);
}
public static void main(String[] args) throws Exception {
KeyPair keyPair = generateKeyPair();
System.out.println("SM2密钥对生成完成");
String data = "需要签名的数据";
String signature = sign(data, keyPair.getPrivate());
System.out.println("签名: " + signature.substring(0, 50) + "...");
boolean isValid = verify(data, signature, keyPair.getPublic());
System.out.println("验证结果: " + isValid);
}
}
Diffie-Hellman 密钥交换
原理
Diffie-Hellman (DH) 是一种密钥交换协议,允许双方在不安全的信道上协商共享密钥。
算法流程:
- 双方协商公开参数 (p, g)
- Alice选择私钥a,计算 A = g^a mod p,发送给Bob
- Bob选择私钥b,计算 B = g^b mod p,发送给Alice
- Alice计算 s = B^a mod p
- Bob计算 s = A^b mod p
- 双方得到相同的共享密钥 s
技术规格
| 类型 | 密钥长度 | 安全性 |
|---|---|---|
| DH | 2048位 | 中等 |
| ECDH | 256位 | 高(等价3072位DH) |
应用场景
- TLS/HTTPS:密钥交换
- VPN:IPSec、OpenVPN
- SSH:密钥交换
- 即时通讯:安全聊天
Java实现示例
import javax.crypto.KeyAgreement;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
public class DiffieHellmanExample {
/**
* 传统DH密钥交换
*/
public static class TraditionalDH {
public static KeyPair generateKeyPair(int keySize) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DH");
keyPairGenerator.initialize(keySize);
return keyPairGenerator.generateKeyPair();
}
public static byte[] deriveSharedSecret(PrivateKey privateKey, PublicKey publicKey)
throws Exception {
KeyAgreement keyAgreement = KeyAgreement.getInstance("DH");
keyAgreement.init(privateKey);
keyAgreement.doPhase(publicKey, true);
return keyAgreement.generateSecret();
}
}
/**
* ECDH密钥交换(推荐)
*/
public static class ECDH {
public static KeyPair generateKeyPair(String curveName) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec = new ECGenParameterSpec(curveName);
keyPairGenerator.initialize(ecSpec);
return keyPairGenerator.generateKeyPair();
}
public static byte[] deriveSharedSecret(PrivateKey privateKey, PublicKey publicKey)
throws Exception {
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
keyAgreement.init(privateKey);
keyAgreement.doPhase(publicKey, true);
return keyAgreement.generateSecret();
}
}
public static void main(String[] args) throws Exception {
System.out.println("=== 传统DH密钥交换 ===");
KeyPair aliceDH = TraditionalDH.generateKeyPair(2048);
KeyPair bobDH = TraditionalDH.generateKeyPair(2048);
byte[] sharedSecret1 = TraditionalDH.deriveSharedSecret(
aliceDH.getPrivate(), bobDH.getPublic());
byte[] sharedSecret2 = TraditionalDH.deriveSharedSecret(
bobDH.getPrivate(), aliceDH.getPublic());
System.out.println("共享密钥长度: " + sharedSecret1.length + " 字节");
System.out.println("密钥是否相同: " +
java.util.Arrays.equals(sharedSecret1, sharedSecret2));
System.out.println("\n=== ECDH密钥交换(推荐) ===");
KeyPair aliceECC = ECDH.generateKeyPair("secp256r1");
KeyPair bobECC = ECDH.generateKeyPair("secp256r1");
byte[] sharedSecret3 = ECDH.deriveSharedSecret(
aliceECC.getPrivate(), bobECC.getPublic());
byte[] sharedSecret4 = ECDH.deriveSharedSecret(
bobECC.getPrivate(), aliceECC.getPublic());
System.out.println("共享密钥长度: " + sharedSecret3.length + " 字节");
System.out.println("密钥是否相同: " +
java.util.Arrays.equals(sharedSecret3, sharedSecret4));
// 性能对比
System.out.println("\n=== 性能对比 ===");
long start1 = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
TraditionalDH.generateKeyPair(2048);
}
long end1 = System.currentTimeMillis();
System.out.println("传统DH (10次): " + (end1 - start1) + "ms");
long start2 = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
ECDH.generateKeyPair("secp256r1");
}
long end2 = System.currentTimeMillis();
System.out.println("ECDH (10次): " + (end2 - start2) + "ms");
}
}
性能对比
算法性能对比表
| 算法 | 密钥长度 | 密钥生成 | 加密速度 | 解密速度 | 签名速度 | 验证速度 | 推荐度 |
|---|---|---|---|---|---|---|---|
| ECC P-256 | 256位 | 快 | 快 | 快 | 快 | 快 | ⭐⭐⭐⭐⭐ |
| RSA-2048 | 2048位 | 慢 | 慢 | 慢 | 慢 | 快 | ⭐⭐⭐⭐ |
| RSA-3072 | 3072位 | 很慢 | 很慢 | 很慢 | 很慢 | 慢 | ⭐⭐⭐ |
| ECC P-384 | 384位 | 快 | 快 | 快 | 快 | 快 | ⭐⭐⭐⭐⭐ |
| SM2 | 256位 | 快 | 快 | 快 | 快 | 快 | ⭐⭐⭐⭐ |
性能优化建议
- 密钥交换:优先使用ECDH而非传统DH
- 移动设备:使用ECC而非RSA
- 高性能场景:使用ECC
- 兼容性要求:RSA更广泛支持
- 混合加密:非对称加密用于密钥交换,对称加密用于数据加密
总结
推荐选择
现代应用:
- 首选:ECC P-256(性能与安全性最佳平衡)
- 备选:RSA-2048(兼容性要求)
高安全需求:
- 首选:ECC P-384 或 RSA-3072
国密合规:
- SM2
密钥交换:
- 首选:ECDH
- 备选:传统DH(性能较差)
选择决策树
需要非对称加密?
├─ 需要国密合规?→ SM2
├─ 性能优先/移动设备?→ ECC P-256
├─ 兼容性优先?→ RSA-2048
└─ 极高安全需求?→ ECC P-384 或 RSA-4096