1. 前言
ELGamal密碼是除了RSA之外最有代表性的公開密鑰密碼之一,它的安全性建立在離散對數問題的困難性之上,是一種公認安全的公鑰密碼。
我們再來回顧下離散對數問題:
設p為素數,若存在一個正整數α,使得α、α2、...、αp-1關於模p互不同余,則稱α為模p的一個原根。於是有如下運算:α的冪乘運算:y=αx(mod p),1≤x≤p-1,α的對數運算:x=logαy,1≤y≤p-1
只要p足夠大,求解離散對數問題時相當復雜的。離散對數問題具有較好的單向性。
2. ELGamal加解密算法
1.隨機地選擇一個大素數p,且要求p-1有大素數因子,將p公開。
2.選擇一個模p的原根α,並將α公開。
3.隨機地選擇一個整數d(1<d<p-1)作為私鑰,並對d保密。
4.計算公鑰y=α^d(mod p),並將y公開。
加密
1.隨機地選取一個整數k(1<k<p-1)。
2.計算U=y^k(mod p)、C1=α^k(mod p)、C2=UM(mod p)。
3.取(C1,C2)作為密文。
解密
1.計算V=C1^d(mod p)。
2.計算M=C2V^-1(mod p)。
正確性證明
3. ELGamal算法細節
實現ELGamal算法,需要實現以下幾個部分:
1.對大數的素數判定;(RSA算法學習過)
2.判斷原根;
3.模指運算;(RSA算法學習過)
4.模逆運算。(RSA算法學習過)
判斷原根
已知a和m互素,如果d是滿足a^d=1(mod m)的最小正整數,則稱d為a模m的階,記為d=σm(a)。由於a和m互素,根據歐拉定理可知a^φ(m)=1(mod m),由此可以得到σm(a) | φ(m)。
若a是m的原根,則σm(a)=φ(m)。
根據上述兩點,推出逆否命題:如果∃d | φ(m)且d≠φ(m),使得a^d=1(mod m),則a不是模m的原根。所以判斷a是否為模m的原根,最快的方法就是判斷φ(m)的每一個因子d是否使得a^d=1(mod m)。如果滿足ad=1(mod m)的d=φ(m),則a是模m的原根。
eg:判斷2是不是模11的原根,φ(11)=10。10的因子有1、2、5、10,所以:
2(mod 11)=2
2^2(mod 11)=4
2^5(mod 11)=10
2^10(mod 11)=1
因此,2是模11的原根。
4. ELGamal密碼的安全性
由於ELGamal密碼的安全性建立在GF(p)上離散對數的困難性之上,而目前尚無求解GF(p)上離散對數的有效算法,所以在p足夠大時ELGamal密碼是安全的。理想情況下p為強素數,p-1=2q,q為大素數。
為了安全加密所使用的k必須是一次性的。如果長期使用同一個k加密的話,就可能被攻擊者獲取,從而根據V=U=y^k(mod p),M=C2V^-1(mod p)而得到明文。另外,使用同一個k加密不同的明文M和M',則由於
如果攻擊者知道M,則很容易求出M'。此外,k選取時還要保證U=y^k(mod p)≠1。
5. 基於ELGamal的java源碼的實現
ELGamal源碼:
import java.math.BigInteger;
import java.util.Random;
public class ELGamal {
public BigInteger p, alpha, y;
private BigInteger d;
public ELGamal() {
do {
//1. 隨機地選擇一個大素數p,且要求p-1有大素數因子,將p公開。
p = BigInteger.probablePrime(100, new Random());
} while (p.subtract(BigInteger.ONE).divide(new BigInteger("2")).isProbablePrime(100));
do {
//2.選擇一個模p的原根α,並將α公開。
alpha = new BigInteger(100, new Random());
} while (! isOrigin(alpha, p));
do {
//3.隨機地選擇一個整數d(1<d<p-1)作為私鑰,並對d保密。
d = new BigInteger(100, new Random());
} while (d.compareTo(BigInteger.ONE) != 1 || d.compareTo(p.subtract(BigInteger.ONE)) != -1);
//4.計算公鑰y=α^d(mod p),並將y公開。
y = alpha.modPow(d, p);
}
public ELGamal(BigInteger p, BigInteger alpha, BigInteger d) {
this.p = p;
this.alpha = alpha;
this.d = d;
y = alpha.modPow(d, p);
}
/**
* 加密
* 隨機地選取一個整數k(1<k<p-1)
* 計算U=yk(mod p)、C1=αk(mod p)、C2=UM(mod p)
* 取(C1,C2)作為密文
* @param M
* @return
*/
BigInteger[] encrypt(BigInteger M) {
BigInteger[] C = new BigInteger[2];
BigInteger k, U;
do {
do {
k = new BigInteger(100, new Random());
} while (k.compareTo(BigInteger.ONE) != 1 || k.compareTo(p.subtract(BigInteger.ONE)) != -1);
U = y.modPow(k, p);
} while (U.intValue() != 1);
C[0] = alpha.modPow(k, p);
C[1] = U.multiply(M).mod(p);
return C;
}
/**
* 加密
* @param M
* @param k
* @return
*/
BigInteger[] encrypt(BigInteger M, BigInteger k) {
BigInteger[] C = new BigInteger[2];
BigInteger U = y.modPow(k, p);
C[0] = alpha.modPow(k, p);
C[1] = U.multiply(M).mod(p);
return C;
}
/**
* 解密
* 計算V=C1^d(mod p);
* 計算M=C2V^-1(mod p)
* @param C
* @return
*/
BigInteger decrypt(BigInteger[] C) {
BigInteger V = C[0].modPow(d, p);
BigInteger M = C[1].multiply(V.modPow(new BigInteger("-1"), p)).mod(p);
return M;
}
/**
* 判斷a是否為模m的原根,其中m為素數
* @param a
* @param m
* @return
*/
static boolean isOrigin(BigInteger a, BigInteger m) {
if (a.gcd(m).intValue() != 1) return false;
BigInteger i = new BigInteger("2");
while (i.compareTo(m.subtract(BigInteger.ONE)) == -1) {
if (m.mod(i).intValue() == 0) {
if (a.modPow(i, m).intValue() == 1)
return false;
while (m.mod(i).intValue() == 0)
m = m.divide(i);
}
i = i.add(BigInteger.ONE);
}
return true;
}
public BigInteger getD() {
return d;
}
}
測試代碼:
import java.math.BigInteger;
public class TestELGamal {
public static void main(String[] args) {
BigInteger p = new BigInteger("2579");
BigInteger alpha = new BigInteger("2");
BigInteger d = new BigInteger("765");
ELGamal elgamal = new ELGamal(p, alpha, d);
System.out.println("p=" + elgamal.p);
System.out.println("α=" + elgamal.alpha);
System.out.println("d=" + elgamal.getD());
System.out.println("y=" + elgamal.y);
BigInteger M = new BigInteger("1299");
BigInteger k = new BigInteger("853");
BigInteger[] C = elgamal.encrypt(M, k);
System.out.println("明文:M=" + M);
System.out.println("k=" + k);
System.out.println("密文:(C1, C2)=(" + C[0] + ", " + C[1] + ")");
BigInteger decryptM=elgamal.decrypt(C);
System.out.println("解密:decryptM=" + decryptM);
}
}
測試結果:
更多代碼請參考:https://github.com/Anapodoton/Encryption/tree/master/ELGamal