一、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加密算法作为离散对数难题的经典实现,具有以下核心价值:
- 安全性强:基于数学难题,具有可证明安全性
- 概率加密:抵御选择明文攻击(CPA)
- 功能丰富:支持加密、签名、零知识证明
- 同态特性:乘法同态(m₁m₂的密文 = E(m₁) × E(m₂))