簡述:ElGamal公鑰密碼體制是由 T.ElGamal於 1985年提出的,直到現在仍然是一個安全效能良好的公鑰密碼體制。該演算法既能用於資料加密也能用於數字簽名,其安全性依賴於計算有限域上離散對數這一難題。下面詳細介紹該演算法。

1.背景

ElGamal公鑰密碼體制是由 T.ElGamal於 1985年提出的,與 Diffie-Hellman金鑰分配體制密切相關。ElGamal密碼體系應用於一些技術標準中,如數字簽名標準(DSS)和 S/MIME電子郵件標準。直到現在仍然是一個安全效能良好的公鑰密碼體制。該演算法既能用於資料加密也能用於數字簽名,其安全性依賴於計算有限域上離散對數這一難題。Elgamal加密方案可以視為DHKE協議的拓展,無容置疑,其安全性也是基於離散函式問題和Diffie_Hellman問題的難度。

在對稱金鑰加解密,在他們進行任何加密之前,雙方必須要擁有一個祕密的key。一直以來,金鑰的分發一直存在很多問題,因為金鑰分配涉及到面對面的見面,可信中介的使用,或者是通過一個加密的渠道傳送key。通過加密渠道傳送key取決於這個加密渠道的安全性。Diffie-Hellman金鑰交換方案提供了實際中金鑰分配問題的解決方案,即它允許雙方通過不安全的通道進行交流,得到一個共同金鑰。該實驗使用python3.8實現了Elgamal加密演算法和數字簽名。

2.主要演算法

2.1

Diffie-Hellman金鑰交換演算法

圖一.DH金鑰交換演算法流程圖

1.Alice和Bob交換了各自公鑰後,計算出公共金鑰(DH演算法),就可以用同一個金鑰進行加密和解密。
2.Diffie-Hellman金鑰交換的安全性建立在下述事實之上:求關於素數的模素數冪運算相對容易,而計算離散對數卻非常困難;對於大素數,求離散對數被認為是不可行的。

3.例:輸入一個質數,作為Alice和Bob的公鑰:7

取原根為: 3

Alice隨機產生的私鑰: 3

A計算出的公鑰YA,傳遞給B: 6

Bob隨機產生的私鑰: 1

B計算出的公鑰YB,傳遞給A: 3

A獲取的共享金鑰: 6

B獲取的共享金鑰: 6

2.2

Elgamal加密的基本原理

該加密演算法可以分為以下幾個步驟:

1.選取一個大素數q和q的一個原根a。

2.Alice將選取一個私鑰Xa,計算Ya=a^Xa mod q.{q,a,Ya}將作為公鑰傳遞給Bob

3.Bob獲取公鑰後,選取自己的私鑰Xb,計算:

K=Ya^Xb mod q,

C1=a^Xb mod q.

K是雙方的公共金鑰,用來加解密明文M。密文C2=M*K mod q.

C1傳遞給Alice,是獲取K的媒介。

1.Alice獲取Bob的密文(C1,C2)後,開始解密。

K=C1^Xa mod q

M=C2*K-1 mod q

解密完成。

2.3

在有限域Zq中求取指定元素乘法逆元。

歐幾里得演算法用於求取整數a,b的最大公因數gcd(a,b),基於gcd(a,b)=gcd(b,a mod b)成立。拓展的歐幾里得演算法用於求取a*x+b*y=gcd(a,b)的整數解(x,y)。

實驗程式碼:

def extendgcd(a,b):            #返回一個集合:(x,y,d)滿足ax+by=d

if b == 0:

return (1,0,a)

else:

(x,y,d)=extendgcd(b,a%b)

x,y=y,(x-(a//b)*y)#x-(a//b)*y即為x mod y

return (x,y,d)

當演算法進行到最後一步,即a=d,b=0時,必然有(x,y)=(1,0)滿足a*1+b*0=d。此即為回溯的起始點。

考察回溯過程中上下兩層的關係。有:

a * x1 + b * y1 = gcd(a,b)
b * x2 + (a%b) * y2 = gcd(b,a%b)

# d= gcd(a,b) = gcd(b,a%b) 演算法中每一步返回值都滿足d=ax+by

得:a * x1 + b * y1 = b * x2 + (a%b) * y2

其中a%b換為a-(a/b)*b。
得:a * x1 + b * y1 = b * x2 + (a - (a / b) * b) * y2

=b * (x2 - (a / b) * y2) + a * y2

=a * y2 + b * (x2 - (a / b) * y2)

得到:

X1=y2, y1=(x2 - (a / b) * y2)

得到方程b * x2 + (a%b) * y2 = g(b,a%b) 的解x2, y2,就能得到方程a * x1 + b * y1 = g(a,b) 的解x1,y1。

在Elgamal演算法中,拓展歐幾里得演算法用於求取金鑰K的關於模φ(n)的逆元K0。

即令

K*K0 mod q=1,求K0。

K*x+q*y=gcd(K,q)=1#K與q互質

該式正是拓展歐幾里得演算法公式。求取x。

等式兩邊同時模q,得K*x mod φ(n)=1。

即x就是我們要求的K模q的逆元K0。

2.4Elgamal數字簽名的基本原理

1.生成金鑰

同ElGamal 加密方案一樣,ElGamal 數字簽名方案的基本元素是素數q和α,其中α是q的原根。使用者Alice選取私鑰Xa,1<Xa<q-1。計算Ya=a^Xa mod q,公鑰為{q, a, Ya}

2.簽名與驗證

數字簽名的步驟:

1.Alice選取隨機金鑰K,K滿足1<K<q-1,且gcd(K,q-1)=1。

2.計算r=a^K mod q.

3.計算K-1 mod q-1.

4.計算s=(m-Xa*r)*K-1 mod q-1.

5.返回簽名(r,s)

簽名驗證步驟:

Bob現在收到了Alice的訊息m,公鑰(q, a, Ya)和簽名(r,s)。

1.計算V1=a^m mod q.

2.計算V2=(Ya^r)*(S1^S2) mod q.

若V1=V2,說明該簽名合法,訊息確是Alice所發。

3.演算法實現

點選檢視程式碼

點選檢視程式碼
'''
實現環境:python 3.8
使用庫: random
'''
#3.1加密演算法實現:
import random as rd
def sushu(num):#判斷素數
judge=1
for x in range(2,num):
if num%x==0:
judge=0
return False
if judge==1:
return True
def getsushu():#生成大質數
while True:
x=rd.randint(50000,100000)
if sushu(x)==True:
return x
def fun0(a,b):#輾轉相除法
if a%b==0:
return b
else:
return fun0(b,a%b)
def extendgcd(a,b):#返回一個集合:(x,y,d)滿足ax+by=d
if b == 0:
return (1,0,a)
else:
(x,y,d)=extendgcd(b,a%b)
x,y=y,(x-(a//b)*y)#x-(a//b)*y即為x mod y
return (x,y,d) def Ord(n):#求原根
result=[]
for x in range(1,n):
if pow(x,n-1,n)==1:
result.append(x)
for i in range(1,n-1):
if pow(x,i,n)==1:
result.remove(x)
break
if len(result)!=0:
return result def get_KPB(q,a):
XA=rd.randint(2,q-2)
YA=pow(a,XA,q)
return {'q':q,'a':a,'YA':YA},XA
def encrypt(text,KAB):
k=rd.randint(2,KAB['q'])
K=pow(KAB['YA'],k,KAB['q'])#一次性金鑰,用完即毀
C1=pow(KAB['a'],k,KAB['q'])
C2=pow(K*text,1,KAB['q'])
return [C1,C2]
def decrypt(miwen,XA,q):
C1=miwen[0]
C2=miwen[1]
K=pow(C1,XA,q)
K0=extendgcd(K,q)[0] if extendgcd(K,q)[0]>0 else extendgcd(K,q)[0]+q
M=pow(C2*K0,1,q)
return M
def change0(text):#字串轉utf-8
return list(map(ord,text))
def change1(alist):#utf-8轉字串
result = ''
for x in alist:
result += chr(x)
return result if __name__ == '__main__':
q=getsushu()
print('隨機生成的素數為:',q)
a=Ord(q)[0]
print('選取的本原根為:',a)
KAB,XA=get_KPB(q,a)
print('生成的公鑰為:',KAB)
text=input('輸入待加密明文:')
temp=change0(text)
mlist=[]
for x in temp:
miwen=encrypt(x,KAB)
mlist.append(miwen)
print('密文列表:',mlist)
wlist=[]
for x in mlist:
wlist.append(decrypt(x,XA,q))
print('解密後明文:',change1(wlist)) #3.2數字簽名實現:
import elgamal加密 as el
import random as rd def get_KPB(q,a):
XA=rd.randint(2,q-2)
print('XA(d)=',XA)
YA=pow(a,XA,q)
return {'q':q,'a':a,'YA':YA},XA
#q,a,YA是公鑰,XA是私鑰 def sig(text,KAB,XA):
ke=0
while True:
k = rd.randint(2,KAB['q']- 2)
if el.fun0(k,(KAB['q']-1))==1:
ke=k
break r=pow(KAB['a'],ke,KAB['q'])
ke0 = el.extendgcd(ke,KAB['q']-1)[0] if el.extendgcd(ke,KAB['q']-1)[0]>0 else el.extendgcd(ke,KAB['q']-1)[0] +(KAB['q']-1)
s=pow((text-XA*r)*ke0,1,(KAB['q']-1))
return [r,s] def ver(text,sign,KAB):
r=sign[0]
s=sign[1]
t=pow((KAB['YA']**r)*(r**s),1,KAB['q'])
print('對每一段簽名進行驗證:t=',t,end='')
print('; a**m mod q=',pow(KAB['a'],text,KAB['q']))
if t==pow(KAB['a'],text,KAB['q']):
return True
else:
return False if __name__ == '__main__':
print('該程式實現Elgamal數字簽名:')
q=el.getsushu()
print('選取的大素數q為:',q)
a=el.Ord(q)[0]
print('選取的本原根a為:',a)
KAB,XA=get_KPB(q,a)
print('公鑰為:',KAB)
text=input('輸入待簽名原文:')
text=el.change0(text)
print('將原文轉為utf8編碼:',text)
sign=[]#簽名列表
for x in text:
sign.append(sig(x, KAB, XA))
print('簽名為:',sign) answer=False
for x in sign:
answer=ver(text[sign.index(x)],x,KAB)
print(answer)

3.1Elgamal加密演算法測試

隨機生成的素數為: 68993

選取的本原根為: 3

生成的公鑰為: {'q': 68993, 'a': 3, 'YA': 8845}

輸入待加密明文:nuistisouruniversity

密文列表: [[16888, 18105], [35921, 23717], [6908, 23122], [36150, 54825], [56786, 53807], [30866, 53053], [41182, 20569], [21660, 13487], [58789, 58315], [27149, 59733], [64748, 66959], [7238, 20154], [52728, 38058], [16732, 870], [41589, 7571], [30510, 39857], [35682, 6061], [44380, 53045], [26560, 7106], [6173, 39758]]

解密後明文: nuistisouruniversity

3.2Elgamal數字簽名測試

該程式實現Elgamal數字簽名:

選取的大素數q為: 71129

選取的本原根a為: 3

XA(d)= 69878

公鑰為: {'q': 71129, 'a': 3, 'YA': 39879}

輸入待簽名原文:nuistisouruniversity

將原文轉為utf8編碼: [110, 117, 105, 115, 116, 105, 115, 111, 117, 114, 117, 110, 105, 118, 101, 114, 115, 105, 116, 121]

簽名為: [[56796, 68014], [8258, 2747], [60548, 36193], [16302, 23267], [57639, 12898], [6678, 12877], [38448, 33657], [23709, 23941], [3104, 61397], [16734, 62], [70552, 25995], [53287, 11700], [48957, 30813], [57255, 56036], [10789, 44621], [45145, 21420], [16890, 4095], [11339, 6305], [45804, 56644], [26456, 11289]]

對每一段簽名進行驗證:t= 34761; a**m mod q= 34761

對每一段簽名進行驗證:t= 56535; a**m mod q= 56535

對每一段簽名進行驗證:t= 6290; a**m mod q= 6290

對每一段簽名進行驗證:t= 53701; a**m mod q= 53701

對每一段簽名進行驗證:t= 18845; a**m mod q= 18845

對每一段簽名進行驗證:t= 6290; a**m mod q= 6290

對每一段簽名進行驗證:t= 53701; a**m mod q= 53701

對每一段簽名進行驗證:t= 33154; a**m mod q= 33154

對每一段簽名進行驗證:t= 56535; a**m mod q= 56535

對每一段簽名進行驗證:t= 41610; a**m mod q= 41610

對每一段簽名進行驗證:t= 56535; a**m mod q= 56535

對每一段簽名進行驗證:t= 34761; a**m mod q= 34761

對每一段簽名進行驗證:t= 6290; a**m mod q= 6290

對每一段簽名進行驗證:t= 27347; a**m mod q= 27347

對每一段簽名進行驗證:t= 28178; a**m mod q= 28178

對每一段簽名進行驗證:t= 41610; a**m mod q= 41610

對每一段簽名進行驗證:t= 53701; a**m mod q= 53701

對每一段簽名進行驗證:t= 6290; a**m mod q= 6290

對每一段簽名進行驗證:t= 18845; a**m mod q= 18845

對每一段簽名進行驗證:t= 27079; a**m mod q= 27079

True

4.總結

Elgamal公鑰密碼體制既能用於資料加密也能用於數字簽名。其中的區別在於:資訊傳送方可以用公鑰加密,接收方用自己的私鑰解密。資訊傳送方用自己的私鑰為訊息簽名,接收方則用公鑰進行驗證。
ElGamal加密演算法產生的密文長度是明文的兩倍。使得通訊的資訊量增大。同時Elgamal加密演算法一個最大的特點是在加密過程中進行隨機方式的加密方法,這種方法有著很好的抗攻擊性。

待改進的地方還有很多,比如素數隨機產生的演算法,求原根的演算法等。