加密与签名技术之非对称加密算法

162 阅读13分钟

概述

非对称加密算法使用一对密钥:公钥用于加密,私钥用于解密。这种算法解决了密钥分发问题,但计算开销较大。

目录

  1. RSA
  2. ECC (椭圆曲线密码学)
  3. ElGamal
  4. SM2 (国密算法)
  5. Diffie-Hellman 密钥交换
  6. 性能对比

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填充)
状态广泛使用,标准算法

应用场景

  1. HTTPS/TLS:证书签名和密钥交换
  2. 数字签名:代码签名、文档签名
  3. 邮件加密:PGP/GPG
  4. SSH:身份认证
  5. 区块链:某些加密货币
  6. 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位极高安全
secp256k1256位3072位比特币

应用场景

  1. TLS/HTTPS:现代浏览器支持
  2. 移动设备:资源受限场景
  3. 区块链:比特币、以太坊
  4. IoT设备:低功耗设备
  5. 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流行)
状态较少使用

应用场景

  1. PGP/GPG:某些配置中使用
  2. 学术研究:密码学教学
  3. 特定合规要求

性能影响

  • 加密速度:中等
  • 密文扩展:是明文的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
状态国密标准,国内广泛使用

应用场景

  1. 政府系统:符合国密要求
  2. 金融行业:国内银行、支付
  3. 信创项目:国产化替代
  4. 密码合规:需要国密认证

性能影响

  • 密钥生成:快(与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) 是一种密钥交换协议,允许双方在不安全的信道上协商共享密钥。

算法流程:

  1. 双方协商公开参数 (p, g)
  2. Alice选择私钥a,计算 A = g^a mod p,发送给Bob
  3. Bob选择私钥b,计算 B = g^b mod p,发送给Alice
  4. Alice计算 s = B^a mod p
  5. Bob计算 s = A^b mod p
  6. 双方得到相同的共享密钥 s

技术规格

类型密钥长度安全性
DH2048位中等
ECDH256位高(等价3072位DH)

应用场景

  1. TLS/HTTPS:密钥交换
  2. VPN:IPSec、OpenVPN
  3. SSH:密钥交换
  4. 即时通讯:安全聊天

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-256256位⭐⭐⭐⭐⭐
RSA-20482048位⭐⭐⭐⭐
RSA-30723072位很慢很慢很慢很慢⭐⭐⭐
ECC P-384384位⭐⭐⭐⭐⭐
SM2256位⭐⭐⭐⭐

性能优化建议

  1. 密钥交换:优先使用ECDH而非传统DH
  2. 移动设备:使用ECC而非RSA
  3. 高性能场景:使用ECC
  4. 兼容性要求:RSA更广泛支持
  5. 混合加密:非对称加密用于密钥交换,对称加密用于数据加密

总结

推荐选择

现代应用:

  • 首选:ECC P-256(性能与安全性最佳平衡)
  • 备选:RSA-2048(兼容性要求)

高安全需求:

  • 首选:ECC P-384 或 RSA-3072

国密合规:

  • SM2

密钥交换:

  • 首选:ECDH
  • 备选:传统DH(性能较差)

选择决策树

需要非对称加密?
├─ 需要国密合规?→ SM2
├─ 性能优先/移动设备?→ ECC P-256
├─ 兼容性优先?→ RSA-2048
└─ 极高安全需求?→ ECC P-384 或 RSA-4096