ElGamal加密算法:离散对数难题的安全基石

一、ElGamal算法概述

ElGamal加密算法是1985年由Taher Elgamal提出的基于离散对数问题 的非对称加密算法,与RSA不同,它直接建立在Diffie-Hellman密钥交换协议之上。ElGamal具有概率加密特性(同一明文每次加密产生不同密文),使其在安全性上具有独特优势,广泛应用于PGP、GnuPG等安全系统中。

核心特性

特性 描述
安全性基础 离散对数难题
加密特性 概率加密(随机性)
密钥结构 公钥=(p, g, h),私钥=x
应用场景 安全通信、数字签名、电子投票

密钥生成 选择大素数p 选择生成元g 选择私钥x 计算公钥h=g^x mod p 加密 选择随机数k 计算c1=g^k mod p 计算c2=m*h^k mod p 解密 计算共享密钥s=c1^x mod p 计算m=c2*s^-1 mod p

二、ElGamal算法原理

1. 密钥生成

复制代码
1. 选择大素数 p
2. 选择生成元 g ∈ Zₚ*
3. 选择私钥 x(1 < x < p-1)
4. 计算公钥 h = g^x mod p

2. 加密过程(分组处理)

复制代码
对于明文分组 m(0 ≤ m < p):
1. 随机选择整数 k(1 < k < p-1)
2. 计算 c₁ = g^k mod p
3. 计算共享密钥 s = h^k mod p
4. 计算 c₂ = m · s mod p
5. 输出密文 (c₁, c₂)

3. 解密过程

复制代码
1. 计算共享密钥 s = c₁^x mod p
2. 计算 s 的模逆元 s^{-1} mod p
3. 恢复明文 m = c₂ · s^{-1} mod p

三、Java实现

java 复制代码
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ElGamalAlgorithm {

    private static final int PRIME_BITS = 1024;
    private BigInteger p;
    private BigInteger g;
    private BigInteger x;
    private BigInteger h;
    private int blockSize;

    public void generateKeys() {
        SecureRandom random = new SecureRandom();
        p = BigInteger.probablePrime(PRIME_BITS, random);
        g = findGenerator(p, random);

        BigInteger pMinusTwo = p.subtract(BigInteger.TWO);
        x = new BigInteger(PRIME_BITS - 1, random)
                .mod(pMinusTwo)
                .add(BigInteger.ONE);

        h = g.modPow(x, p);
        blockSize = (p.bitLength() - 1) / 8 - 1; // 留出空间确保m < p
    }

    private BigInteger findGenerator(BigInteger p, SecureRandom random) {
        BigInteger g;
        do {
            g = new BigInteger(p.bitLength(), random)
                    .mod(p.subtract(BigInteger.ONE))
                    .add(BigInteger.ONE);
        } while (!isGenerator(g, p));
        return g;
    }

    private boolean isGenerator(BigInteger g, BigInteger p) {
        return g.compareTo(BigInteger.ONE) > 0 &&
                g.compareTo(p) < 0;
    }

    public List<BigInteger[]> encrypt(byte[] plaintext) {
        SecureRandom random = new SecureRandom();
        List<BigInteger[]> ciphertext = new ArrayList<>();

        for (int i = 0; i < plaintext.length; i += blockSize) {
            int length = Math.min(blockSize, plaintext.length - i);
            byte[] block = Arrays.copyOfRange(plaintext, i, i + length);

            // 确保值为正数且小于p
            BigInteger m = new BigInteger(1, block);
            if (m.compareTo(p) >= 0) {
                throw new IllegalArgumentException("明文分组太大,请使用更小的blockSize");
            }

            ciphertext.add(encryptBlock(m, random));
        }

        return ciphertext;
    }

    private BigInteger[] encryptBlock(BigInteger m, SecureRandom random) {
        BigInteger k;
        do {
            k = new BigInteger(p.bitLength(), random);
        } while (k.compareTo(BigInteger.ONE) <= 0 ||
                k.compareTo(p.subtract(BigInteger.ONE)) >= 0);

        BigInteger c1 = g.modPow(k, p);
        BigInteger s = h.modPow(k, p);
        BigInteger c2 = m.multiply(s).mod(p);

        return new BigInteger[]{c1, c2};
    }

    public byte[] decrypt(List<BigInteger[]> ciphertext) {
        ByteArrayOutputStream output = new ByteArrayOutputStream();

        for (BigInteger[] block : ciphertext) {
            BigInteger m = decryptBlock(block[0], block[1]);
            byte[] bytes = m.toByteArray();

            // 处理可能的前导零
            if (bytes[0] == 0) {
                output.write(bytes, 1, bytes.length - 1);
            } else {
                output.write(bytes, 0, bytes.length);
            }
        }

        return output.toByteArray();
    }

    private BigInteger decryptBlock(BigInteger c1, BigInteger c2) {
        BigInteger s = c1.modPow(x, p);
        BigInteger sInv = s.modInverse(p);
        return c2.multiply(sInv).mod(p);
    }


    public static void main(String[] args) {
        ElGamalAlgorithm elgamal = new ElGamalAlgorithm();
        elgamal.generateKeys();

        System.out.println("公钥(p): " + elgamal.p.toString(16).substring(0, 20) + "...");
        System.out.println("公钥(g): " + elgamal.g.toString(16).substring(0, 20) + "...");
        System.out.println("公钥(h): " + elgamal.h.toString(16).substring(0, 20) + "...");
        System.out.println("分组大小: " + elgamal.blockSize + " 字节");

        String longText = "ElGamal加密算法是一种基于离散对数问题的非对称加密算法," +
                "由Taher Elgamal于1985年提出。" +
                "同一明文每次加密产生不同的密文,增强安全性。";

        System.out.println("\n测试文本: " + longText);

        byte[] plaintext = longText.getBytes(StandardCharsets.UTF_8);
        List<BigInteger[]> ciphertext = elgamal.encrypt(plaintext);
        System.out.println("分组数量: " + ciphertext.size());

        byte[] decrypted = elgamal.decrypt(ciphertext);
        String decryptedText = new String(decrypted, StandardCharsets.UTF_8);
        System.out.println("解密文本: " + decryptedText);
        System.out.println("解密是否成功: " + longText.equals(decryptedText));
    }
}

四、ElGamal安全性分析

1. 安全优势与局限

优势 局限
基于离散对数难题 密文膨胀(2-4倍)
概率加密特性 计算开销大
可证明安全性 需大素数参数
支持同态运算 需安全随机数

2. 已知攻击与防御

攻击类型 防御措施
小子群攻击 使用安全素数
随机数重用 每次加密使用新随机数
CCA攻击 使用OAEP填充
量子计算威胁 迁移到后量子密码

五、Java标准库实现

java 复制代码
import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.*;
import java.util.Base64;

public class ElGamalWithJCE {
    
    /**
     * 生成ElGamal密钥对
     * @param keySize 密钥大小(1024+)
     * @return 密钥对
     */
    public static KeyPair generateKeyPair(int keySize) throws Exception {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("ElGamal");
        keyGen.initialize(keySize);
        return keyGen.generateKeyPair();
    }
    
    /**
     * 使用公钥加密
     * @param publicKey 公钥
     * @param data 待加密数据
     * @return 加密结果
     */
    public static byte[] encrypt(PublicKey publicKey, byte[] data) throws Exception {
        Cipher cipher = Cipher.getInstance("ElGamal/None/OAEPWithSHA-256AndMGF1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }
    
    /**
     * 使用私钥解密
     * @param privateKey 私钥
     * @param encrypted 加密数据
     * @return 解密结果
     */
    public static byte[] decrypt(PrivateKey privateKey, byte[] encrypted) throws Exception {
        Cipher cipher = Cipher.getInstance("ElGamal/None/OAEPWithSHA-256AndMGF1Padding");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(encrypted);
    }
    
    /**
     * 获取公钥参数
     */
    public static String encodePublicKey(PublicKey publicKey) {
        ElGamalPublicKey elgKey = (ElGamalPublicKey) publicKey;
        return "p=" + elgKey.getParams().getP().toString(16) + 
               "&g=" + elgKey.getParams().getG().toString(16) + 
               "&y=" + elgKey.getY().toString(16);
    }
    
    public static void main(String[] args) throws Exception {
        // 1. 生成密钥对
        KeyPair keyPair = generateKeyPair(2048);
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();
        
        System.out.println("公钥参数: " + encodePublicKey(publicKey));
        
        // 2. 加密测试
        String message = "JCE实现ElGamal加密";
        byte[] encrypted = encrypt(publicKey, message.getBytes());
        System.out.println("加密结果 (Base64): " + Base64.getEncoder().encodeToString(encrypted));
        
        // 3. 解密测试
        byte[] decrypted = decrypt(privateKey, encrypted);
        System.out.println("解密结果: " + new String(decrypted));
        
        // 4. 性能对比
        long start = System.nanoTime();
        encrypt(publicKey, message.getBytes());
        long time = System.nanoTime() - start;
        System.out.println("加密时间: " + time / 1000 + " μs");
    }
}

六、ElGamal优化与发展

1. 性能优化技术

技术 效果 实现方式
预计算 加速加密 预计算gk和hk
中国剩余定理 加速解密 使用p-1因子分解
并行处理 提高吞吐量 多核分组处理
硬件加速 10-100倍提升 专用模幂运算器

2. 变体算法比较

算法 特点 适用场景
标准ElGamal 基础实现 通用加密
椭圆曲线ElGamal 密钥更短 移动设备
阈值ElGamal 分布式解密 安全多方计算
同态ElGamal 支持密文运算 隐私计算

3. 后量子时代发展

ElGamal 基于格的密码 多元密码 同态加密 CRYSTALS-Kyber Rainbow签名 TFHE方案

总结

ElGamal加密算法作为离散对数难题的经典实现,具有以下核心价值:

  1. 安全性强:基于数学难题,具有可证明安全性
  2. 概率加密:抵御选择明文攻击(CPA)
  3. 功能丰富:支持加密、签名、零知识证明
  4. 同态特性:乘法同态(m₁m₂的密文 = E(m₁) × E(m₂))
相关推荐
Java陈序员1 分钟前
再见 Navicat!一款开源的 Web 数据库管理工具!
java·react.js·docker
知其然亦知其所以然17 分钟前
RAG 结果太水?用 RRF + Reranker 重排,效果翻倍提升!
java·后端·llm
SimonKing19 分钟前
吊打面试官系列:Spring为什么不推荐使用字段依赖注入?
java·后端·架构
lubiii_25 分钟前
SQL手工测试(MySQL数据库)
数据库·mysql·web安全·网络安全
魔镜魔镜_谁是世界上最漂亮的小仙女26 分钟前
java-集合
java·后端·程序员
真实的菜28 分钟前
消息队列高级特性与原理:解锁分布式系统的底层逻辑
java
若水不如远方29 分钟前
java范型
java
凌辰揽月31 分钟前
Web后端基础(基础知识)
java·开发语言·前端·数据库·学习·算法
lifallen37 分钟前
深入浅出 Arrays.sort(DualPivotQuicksort):如何结合快排、归并、堆排序和插入排序
java·开发语言·数据结构·算法·排序算法
长安不见39 分钟前
背景知识: 理解LimitLatch背后的AQS
java