密码协议学习笔记(1.5):几种常用的非对称密码体系

RSA密码体系:

RSA密码体系是一种依赖于依赖于大数质因数分解的难解性的密码体系.

RSA加密算法:

参与者:

  • 私钥持有者Alice
  • 公钥持有者Bob

运行步骤:

  1. Alice选取两个大质数$p,q$(需要使用Miller-Rabin算法判定其是否为质数),计算$n=pq$,其欧拉函数$\varphi(n)=(p-1)(q-1)$
  2. Alice选取大质数$e\in(\max(p,q),\varphi(n))$,这保证了$gcd(e,\varphi(n))=1$
  3. Alice(使用扩展欧几里得算法或欧拉定理)计算出$d$,使得$d\cdot e = k \cdot \varphi(n)+1$,其中$k$是任意的整数,这意味着,$d=e^{-1} \mod \varphi(n)$
  4. Alice将$pk=(n,e)$作为公钥发送给Bob,将$sk=(p,q,d)$作为私钥自己保留
  5. 若Bob要给Alice发送信息(明文)$m$,用以下加密(encryption)算法将其加密为密文$c$:$$c=Enc(pk,m)=m^e \mod n$$
  6. Alice收到密文$c$后,用以下解密(decryption)算法还原出明文$m$:$$Dec(sk,c)=c^d \mod n$$.

正确性:

至于为什么$c^d=m \mod n$,证明要根据密文$m$是否与模数$n$互质,考虑如下两种情况:

当$m,n$互质时,

$$ \begin{aligned} c^d = & m^{ed} \\ = & m^{k\varphi(n)+1} \qquad \text{($k$为整数,由于第3步的构造)} \\ = & m \cdot (m^{\varphi(n)})^k \\ = & m \cdot (1)^k \mod n \qquad \text{(欧拉定理)}\\ = & m \end{aligned} $$

当$m,n$不互质时,欧拉定理失效,由于$n=pq$,因此$m$要么是$p$的倍数,要么是$q$的倍数

不妨记$m=kp$,$k$为整数,且$m,q$互质(否则$m$会是$q$的倍数,同时是$p,q$的倍数,就是$n$的倍数,此时证明是显然的),那么,此时可应用以$q$为模数的欧拉定理

$$\begin{aligned} x^{q-1}=& 1 \mod q \\ x^{k(p-1)(q-1)} = & 1 \mod q \qquad \text{(等式两端同时做$k(p-1)$次方)} \\ x^{k(p-1)(q-1)} = & iq + 1 \text{(将同余式改写成恒等式)} \end{aligned}$$

重新观察解密过程

$$ \begin{aligned} c^d = & m^{ed} \\ = & m^{k\varphi(n)+1} \qquad \text{($k$为整数,由于第3步的构造)} \\ = & m \cdot m^{k(p-1)(q-1)} \\ = & (iq+1)m \qquad (x^{k(p-1)(q-1)} = iq + 1) \\ = & mq+m \\ = & kpq+m \qquad (m=kp) \\ = &m \mod n \end{aligned} $$

综上,

$$c^d = m \mod n$$

安全性:

Alice的私钥$sk=(p,q,d)$不会上传到网络上,因此理论上无法被攻击者截获,攻击者若截获了加密消息$c$,同时已知公开信息$pk=(n,e)$时,由于大整数分解质因数问题的难解性,攻击者很难知道$n=pq$中的$p,q$到底是多少,也就不知道$e=d^{-1} \mod (p-1)(q-1)$是多少.

代码:

def divi(a): #分解质因数
    i=2
    ans=[]
    while(i*i<a):
        while(a%i==0):
            ans.append(i)
            a=a//i
        i=i+1
    if(a>1):
        ans.append(a)
    return ans

def gcd(a,b): #辗转相除法
    if(b == 0):
        return a
    else:
        return gcd(b,a%b)
    
def exgcd(a,b): #扩展欧几里得算法
    if(b == 0):
        x = 1
        y = 0
        return a,x,y
    ans,t,x = exgcd(b,a%b)
    y = t-a//b*x
    return ans,x,y

def qpow(base,exp,mod): #快速幂
    ans = 1
    while(exp>0):
        if(exp%2 == 1):
            ans = ans*base%mod
        exp = exp//2
        base = base*base%mod
    return ans

def rev(a,mod,type ='not_prime'):
    if(type =='prime'): #费马小定理求逆元
        return qpow(a,mod-2,mod)
    else: #辗转相除法求逆元
        g,x,y = exgcd(a,mod)
        if(g!= 1):
            return -1 #逆元不存在
        else:
            return (x+mod)%mod

p = 97
q = 103 #实际上这两个大整数是随机生成并通过Miller-Rabin算法判定的
print("Gen()->pk,sk")
print('p =',p,end=' = ')
print(*divi(p),sep='*')
print('q =',q,end=' = ')
print(*divi(q),sep='*')
g,x,y = exgcd(p,q)
n = p*q
print('n = pq =',n)
φ = (p-1)*(q-1)
print('φ = (p-1)(q-1) =',φ)
e = 197
print('e =',e,'\ngcd(z,e) =',gcd(φ,e))
d = rev(e,φ,type ='not_prime')
print('''d = e^{-1}(mod φ) =''',d)
print('pk:(n =',n,',e =',e,')')
print('sk:(n =',n,',d =',d,')')

print('\n')
print("Enc(pk,m)->c")
m = 114
print('m =',m)
c = qpow(m,e,n)
print('''c = m^e mod n =''',c)

print('\n')
print('Dec(c,sk)->m')
m1 = qpow(c,d,n)
print('''m = c^d mod n =''',m1)

运行结果:

Gen()->pk,sk
p = 97 = 97
q = 103 = 103
e = 197
gcd(z,e) = 1
d = e^{-1}(mod φ) = 845
pk:(n = 9991 ,e = 197 )
sk:(n = 9991 ,d = 845 )


Enc(pk,m)->c
m = 114
c = m^e mod n = 7731


Dec(c,sk)->m
m = c^d mod n = 114

RSA数字签名算法:

RSA密码体系还可以用于数字签名.

参与者:

  • 私钥持有者Alice
  • 公钥持有者Bob

运行步骤:

  1. Alice选取两个大质数$p,q$(需要使用Miller-Rabin算法判定其是否为质数),计算$n=pq$,其欧拉函数$\varphi(n)=(p-1)(q-1)$
  2. Alice选取大质数$e\in(\max(p,q),\varphi(n))$,这保证了$gcd(e,\varphi(n))=1$
  3. Alice(使用扩展欧几里得算法或欧拉定理)计算出$d$,使得$d\cdot e = k \cdot \varphi(n)+1$,其中$k$是任意的整数,这意味着,$d=e^{-1} \mod \varphi(n)$
  4. Alice将$pk=(n,e)$作为公钥发送给Bob,将$sk=(p,q,d)$作为私钥自己保留
  5. 若Alice要对一段信息$m$进行签名,用以下签名(signature)算法为其签名$s$:$$s=Sign(sk,m)=m^d \mod n$$
  6. Bob收到$(m,s)$后,用以下验证(verify)算法验算签名是否有效$$Ver(pk,m,s): s^e==m? 1 : 0$$.

正确性:

同上,略.

安全性:

同上,攻击者要伪造签名,就需要在仅知道公钥$pk=(n,e)$的情况下,计算出$d=e^{-1} \mod (p-1)(q-1)$,需要进行分解质因数$n=pq$,这在计算复杂度上是不可行的.

代码:

def divi(a): #分解质因数
    i=2
    ans=[]
    while(i*i<a):
        while(a%i==0):
            ans.append(i)
            a=a//i
        i=i+1
    if(a>1):
        ans.append(a)
    return ans

def gcd(a,b): #辗转相除法
    if(b == 0):
        return a
    else:
        return gcd(b,a%b)
    
def exgcd(a,b): #扩展欧几里得算法
    if(b == 0):
        x = 1
        y = 0
        return a,x,y
    ans,t,x = exgcd(b,a%b)
    y = t-a//b*x
    return ans,x,y

def qpow(base,exp,mod): #快速幂
    ans = 1
    while(exp>0):
        if(exp%2 == 1):
            ans = ans*base%mod
        exp = exp//2
        base = base*base%mod
    return ans

def rev(a,mod,type ='not_prime'):
    if(type =='prime'): #费马小定理求逆元
        return qpow(a,mod-2,mod)
    else: #辗转相除法求逆元
        g,x,y = exgcd(a,mod)
        if(g!= 1):
            return -1 #逆元不存在
        else:
            return (x+mod)%mod

p = 97
q = 103 #实际上这两个大整数是随机生成并通过Miller-Rabin算法判定的
print("Gen()->pk,sk")
print('p =',p,end=' = ')
print(*divi(p),sep='*')
print('q =',q,end=' = ')
print(*divi(q),sep='*')
g,x,y = exgcd(p,q)
n = p*q
print('n = pq =',n)
φ = (p-1)*(q-1)
print('φ = (p-1)(q-1) =',φ)
e = 197
print('e =',e,'\ngcd(z,e) =',gcd(φ,e))
d = rev(e,φ,type ='not_prime')
print('''d = e^{-1}(mod φ) =''',d)
print('pk:(n =',n,',e =',e,')')
print('sk:(n =',n,',d =',d,')')

print('\n')
print("Sign(sk,m)->s")
m = 114
print('m =',m)
s = qpow(m,e,n)
print('''s = m^e mod n =''',s)

print('\n')
print('''Ver(pk,s,m)->{Valid,Invalid}''')
m1 = qpow(s,d,n)
print('''s^d mod n =''',m1)
if (m1==m):
    print("Valid")
else:
    print("Invalid")
    

运行结果:

Gen()->pk,sk
p = 97 = 97
q = 103 = 103
n = pq = 9991
φ = (p-1)(q-1) = 9792
e = 197
gcd(z,e) = 1
d = e^{-1}(mod φ) = 845
pk:(n = 9991 ,e = 197 )
sk:(n = 9991 ,d = 845 )


Sign(sk,m)->s
m = 114
s = m^e mod n = 7731


Ver(pk,s,m)->{1,0}
s^d mod n = 114
Valid

离散对数密码体系(ElGamal):

ElGamal密码体系是一种利用离散对数难解性问题的密码体系

ElGamal加密算法:

参与者:

  • 私钥持有者Alice
  • 公钥持有者Bob

运行步骤:

  1. Alice选取大质数$p$,整数$g\in[0,p-1]$,整数$\alpha \in [0,p-2]$,计算$\beta=g^{\alpha} \mod p$
  2. Alice将$pk=(p,g,\beta)$作为公钥发送给Bob,将$sk=(p,\alpha)$作为私钥自己保留
  3. 若Bob要给Alice发送信息(明文)$m$,需要先随机生成一个$k\in[0,p-1]$,然后用以下加密(encryption)算法将其加密为密文$c$:$$c=Enc(pk,m,k)=(r,s)$$其中$r=g^k \mod p$,   $s=m\beta^k \mod p$
  4. Alice收到密文$c=(r,s)$后,用以下解密(decryption)算法还原出明文$m$:$$Dec(sk,c)=s(r^{\alpha})^{-1} \mod n$$.

正确性:

$$\begin{aligned}
sr^{-\alpha}=&m\beta^k \cdot (g^k)^{-\alpha} \qquad (\text{加密过程},r=g^k \mod p, s=m\beta^k \mod p)\\
            =&m \cdot (g^{\alpha})^k \cdot (g^k)^{-\alpha} \qquad (\text{密钥生成过程},\beta=g^{\alpha}\mod p)\\
            =&m\cdot g^{\alpha k}\cdot g^{-\alpha k}\\
            =&m
\end{aligned}$$

安全性:

攻击者在截获密文$c=(r,s)$,并已知公钥$pk=(p,g,\beta)$时,要还原密文,就需要知道$\alpha$是多少,这就需要在已知$p,g,\beta$的情况下求解$$\beta=g^{\alpha} \mod p$$中的$\alpha$是多少,该等式可记为如下等价形式$$\alpha=\log_g\beta \mod p$$称为离散对数问题,这在计算复杂度上是难以求解的.

代码:

import time #用于生成随机数
def divi(a): #分解质因数
    i=2
    ans=[]
    while(i*i<a):
        while(a%i==0):
            ans.append(i)
            a=a//i
        i=i+1
    if(a>1):
        ans.append(a)
    return ans

def gcd(a,b): #辗转相除法
    if(b == 0):
        return a
    else:
        return gcd(b,a%b)
    
def exgcd(a,b): #扩展欧几里得算法
    if(b == 0):
        x = 1
        y = 0
        return a,x,y
    ans,t,x = exgcd(b,a%b)
    y = t-a//b*x
    return ans,x,y

def qpow(base,exp,mod): #快速幂
    ans = 1
    while(exp>0):
        if(exp%2 == 1):
            ans = ans*base%mod
        exp = exp//2
        base = base*base%mod
    return ans

def rev(a,mod,type ='not_prime'):
    if(type =='prime'): #费马小定理求逆元
        return qpow(a,mod-2,mod)
    else: #辗转相除法求逆元
        g,x,y = exgcd(a,mod)
        if(g!= 1):
            return -1 #逆元不存在
        else:
            return (x+mod)%mod


print("Gen()->pk,sk")
p=97
print('p =',p,end=' = ')
print(*divi(p),sep='*')
g=5
print('g =',g,', g in [0,p-1]')
α=8
print('α =',α,', α in [0,p-2]')
β=qpow(g,α,p)
print('''β = g^α mod p =''',β)
print('pk:(p =',p,',g =',g,',β =',β,')')
print('sk:(p =',p,',α =',α,')')

print('\n')
print("Enc(pk,m)->c")
m = 66
print('m =',m)
k=int(time.time())%(p-1)
print('随机生成 k in [0,p-2], k =',k)
r=qpow(g,k,p)
s=m*qpow(β,k,p) % p
print('r=g^k mod p =',r)
print('s= m*β^k mod p =',s)
print('c=(r,s)')

print('\n')
print("Dec(sk,c)->m")
m1=s*rev(qpow(r,α,p),p,type='prime')%p
print('''m=s*(r^α)^{-1} mod p =''',m1)

运行结果:

Gen()->pk,sk
p = 97 = 97
g = 5 , g in [0,p-1]
α = 8 , α in [0,p-2]
β = g^α mod p = 6
pk:(p = 97 ,g = 5 ,β = 6 )
sk:(p = 97 ,α = 8 )


Enc(pk,m)->c
m = 66
随机生成 k in [0,p-2], k = 3
r=g^k mod p = 28
s= m*β^k mod p = 94
c=(r,s)


Dec(sk,c)->m
m=s*(r^α)^{-1} mod p = 66

ElGamal数字签名算法:

参与者:

  • 私钥持有者Alice
  • 公钥持有者Bob

运行步骤:

  1. Alice选取大质数$p$,整数$g\in[0,p-1]$,整数$\alpha \in [0,p-2]$,计算$\beta=g^{\alpha} \mod p$
  2. Alice将$pk=(p,g,β)$作为公钥发送给Bob,将$sk=(p,α)$作为私钥自己保留
  3. 若Alice要对一段信息$m$进行签名,首先在$[1,p-2]$中随机选取一个与$p-1$互质的数$k$,然后用以下签名(signature)算法为其签名$sig$:$$sig=Sign(sk,m,k)=(r,s)$$ 其中 $$r=g^k \mod p$$ $$s=(m-\alpha r) k^{-1} \mod (p-1)$$
  4. Bob收到$(m,sig)$后,用以下验证(verify)算法验算签名是否有效$$Ver(pk,m,sig): \beta^r r^s==g^m? 1 : 0$$.

特别注意,这里Alice在计算$k$的逆元时,要计算的是模数为$(p-1)$时的逆元,而非模数为$p$时的逆元,否则:

$$g^{k\cdot (k^{-1}(\mod p))}=g^{wp+1}=g\cdot (g^p)^w (w\in Z)$$

然而当$p$为质数时,根据欧拉定理,$g^p=g \neq 1 \mod p$

正确性:

$$\begin{aligned}
\beta^rr^s=&(g^{\alpha})^r(g^k)^s\\
          =&g^{\alpha r} g^{k (m-r\alpha)k^{-1}} \qquad (\text{由签名过程,}s=(m-\alpha r)k^{-1})\\
          =&g^{\alpha r+(m-\alpha r)}\\
          =&g^m
\end{aligned}$$

安全性:

同上,攻击者若要伪造签名,就需要就需要知道$\alpha$是多少,就要求解离散对数问题$\alpha=\log_g\beta \mod p$.

代码:

import time #用于生成随机数
def divi(a): #分解质因数
    i=2
    ans=[]
    while(i*i<a):
        while(a%i==0):
            ans.append(i)
            a=a//i
        i=i+1
    if(a>1):
        ans.append(a)
    return ans

def gcd(a,b): #辗转相除法
    if(b == 0):
        return a
    else:
        return gcd(b,a%b)
    
def exgcd(a,b): #扩展欧几里得算法
    if(b == 0):
        x = 1
        y = 0
        return a,x,y
    ans,t,x = exgcd(b,a%b)
    y = t-a//b*x
    return ans,x,y

def qpow(base,exp,mod): #快速幂
    ans = 1
    while(exp>0):
        if(exp%2 == 1):
            ans = ans*base%mod
        exp = exp//2
        base = base*base%mod
    return ans

def rev(a,mod,type ='not_prime'):
    if(type =='prime'): #费马小定理求逆元
        return qpow(a,mod-2,mod)
    else: #辗转相除法求逆元
        g,x,y = exgcd(a,mod)
        if(g!= 1):
            return -1 #逆元不存在
        else:
            return (x+mod)%mod


print("Gen()->pk,sk")
p=97
print('p =',p,end=' = ')
print(*divi(p),sep='*')
g=5
print('g =',g,', g in [0,p-1]')
α=8
print('α =',α,', α in [0,p-2]')
β=qpow(g,α,p)
print('''β = g^α mod p =''',β)
print('pk:(p =',p,',g =',g,',β =',β,')')
print('sk:(p =',p,',α =',α,')')

print('\n')
print("Sign(pk,m)->c")
m = 66
print('m =',m)
k=0
while(gcd(k,p-1)!=1):
    k=int(time.time()*1000)%(p-1)
print('随机生成与(p-1)互质的 k in [0,p-2], k =',k,'gcd(p-1,k) =',gcd(p-1,k))
r=qpow(g,k,p)
print('r=g^k mod p =',r)
s=(m-r*α)*rev(k,p-1,type='not_prime')%(p-1)
print('''s= (m-ra)k^{-1} mod (p-1) =''',s)
print('sig=(r,s)')

print('\n')
print('''Ver(pk,sig)->{0,1}''')
m1=qpow(β,r,p)*qpow(r,s,p)%p
m2=qpow(g,m,p)

print('''β^r·r^s mod p =''',m1)
print('''g^m mod p =''',m2)
if (m1==m2):
    print("Valid")
else:
    print("Invalid")

运行结果:

Gen()->pk,sk
p = 97 = 97
g = 5 , g in [0,p-1]
α = 8 , α in [0,p-2]
β = g^α mod p = 6
pk:(p = 97 ,g = 5 ,β = 6 )
sk:(p = 97 ,α = 8 )


Sign(pk,m)->c
m = 66
随机生成与(p-1)互质的 k in [0,p-2], k = 41 gcd(p-1,k) = 1
r=g^k mod p = 80
s= (m-ra)k^{-1} mod (p-1) = 82
sig=(r,s)


Ver(pk,sig)->{0,1}
β^r·r^s mod p = 70
g^m mod p = 70
Valid

椭圆曲线上定义的交换群:

尝试以最简洁最直接的方式说明椭圆曲线的定义

由一个质数$p$作为模数,两个整数$a,b$作为参数,当$4a^3+27b^2 \mod p \neq 0$时,可以定义一个椭圆曲线$y^2=x^3+ax+b \mod p$上的交换群$E_p(a,b)=(G\cup \{O\},+)$

$G$为椭圆曲线上的实点,$O$为无穷远处的虚点,同时充当着该群上的零元.

遍历$x\in[0,p-1]$,计算$z=x^3+ax+b$,然后计算模平方根$y^2=z\mod 11$(若存在)(计算方法参考素性检验问题和模平方根问题 - Isakovsky - 博客园 (cnblogs.com)),即可得到群上的所有实点.

群上的运算$+$的定义如下,设$P=(x_1,y_1),Q=(x_2,y_2)\in G$:

  1. $O+O=O$
  2. $P+O=P$
  3. 若$x_1=x_2,y_1+y_2=p$,则$P+Q=O$
  4. 否则,$P+Q=S=(x_3,y_3)$,$x_3,y_3$由以下规则确定: $$x_3=\lambda^2-x_1-x_2 \mod p$$ $$y_3=\lambda(x_1-x_3)-y_1 \mod p$$ 其中 $$\lambda=\left\{\begin{matrix} \frac{y_2-y_1}{x_2-x_1} \mod p ,& x_1\neq x_2 \\ \frac{3x_1^2+a}{2y_1} \mod p ,& x_1=x_2 \end{matrix}\right.$$

类似地,群上的数乘运算$kP$定义为$k$个$P$相加

示例代码($E_{11}(1,6)$下定义的交换群):

def divi(a): #分解质因数
    i=2
    ans=[]
    while(i*i<a):
        while(a%i==0):
            ans.append(i)
            a=a//i
        i=i+1
    if(a>1):
        ans.append(a)
    return ans

def gcd(a,b): #辗转相除法
    if(b == 0):
        return a
    else:
        return gcd(b,a%b)
    
def exgcd(a,b): #扩展欧几里得算法
    if(b == 0):
        x = 1
        y = 0
        return a,x,y
    ans,t,x = exgcd(b,a%b)
    y = t-a//b*x
    return ans,x,y

def qpow(base,exp,mod): #快速幂
    ans = 1
    while(exp>0):
        if(exp%2 == 1):
            ans = ans*base%mod
        exp = exp//2
        base = base*base%mod
    return ans

def rev(a,mod,type ='not_prime'):
    if(type =='prime'): #费马小定理求逆元
        return qpow(a,mod-2,mod)
    else: #辗转相除法求逆元
        g,x,y = exgcd(a,mod)
        if(g!= 1):
            return -1 #逆元不存在
        else:
            return (x+mod)%mod
        

def ECCAdd(p1,p2,p,a,b): #椭圆曲线上的加法
    if(p1=='inf'):
        return p2
    elif(p2=='inf'):
        return p1
    # 无穷远点定义为零元
    x1,y1=p1
    x2,y2=p2
    if(x1==x2 and y1+y2==p):
        return 'inf'
    # 定义关于y轴对称两点(在模意义下实际上是相加得p)互为加法逆元,相加得零元

    if(x1==x2 and y1==y2):
        λ=(3*x1*x1+a)*rev(2*y1,p,type='prime')%p # 两点不重合,过此两点做一条直线
    else:
        λ=(y2-y1)*rev((x2-x1+p)%p,p,type='prime')%p # 两点重合,则取其与曲线的切线

    x3=(λ*λ-x1-x2+p)%p
    y3=(λ*(x1-x3)-y1+p)%p
    # 定义这条直线与曲线的另一个交点为群上加法的结果

    return (x3,y3)


p=11 #定义模数
a=1
b=6 #当4a^3+27b^2 ≠0 mod p 时,可定义一个交换群
print('p =',p,'a =',a,'b =',b)
if(4*a*a*a+27*b*b%p==0):
    print("无法构成交换群")
    exit()
else:
    print("可以构成交换群,群上的元素有:")
    for x in range(p):
        z=(x*x*x+a*x+b)%p
        for y in range(p):
            if(y*y%p==z):
                print((x,y))
    print('inf')
    print('请输入第一个点:')
    p1=input()
    if(p1!='inf'):
        p1=tuple(p1.split(','))
        p1=(int(p1[0][1:]),int(p1[1][:-1]))
        if((p1[0]*p1[0]*p1[0]+a*p1[0]+b)%p!=(p1[1]*p1[1])%p):
            print('该点不在椭圆曲线上!')
            exit()
    print('请输入第二个点:')
    p2=input()
    if(p2!='inf'):
        p2=tuple(p2.split(','))
        p2=(int(p2[0][1:]),int(p2[1][:-1]))
        if((p2[0]*p2[0]*p2[0]+a*p2[0]+b)%p!=(p2[1]*p2[1])%p):
            print('该点不在椭圆曲线上!')
            exit()
    print(p1,'+',p2,'=',ECCAdd(p1,p2,p,a,b))

运行结果:

p = 11 a = 1 b = 6
可以构成交换群,群上的元素有:
(2, 4)
(2, 7)
(3, 5)
(3, 6)
(5, 9)
(7, 2)
(7, 9)
(8, 3)
(8, 8)
(10, 2)
(10, 9)
inf

输入: 
inf
inf
输出:
inf

输入:
(7,2)
inf
输出:
(7,2)

输入:
(5,2)
(5,9)
输出:
inf

输入:
(8,3)
(8,3)
输出:
(7,9)

输入:
(8,3)
(7,2)
输出:
(8,8)

椭圆曲线上的ElGamal密码体系:

椭圆曲线上的离散对数问题也是难解的,由此可构造出此种密码体系并确保其安全.

椭圆曲线密码体系上的明文和密文都是椭圆曲线上的点,因此在使用该密码体系之前需要构造一个整数与点之间的双射.

参与者:

  • 私钥持有者Alice
  • 公钥持有者Bob

运行步骤:

  1. 定义椭圆曲线上点的个数$|G \cup \{O\}| =n$,Alice选取点$P \in G$,再选取$x\in[2,n-1]$,计算$Q=xP$,将$Q$作为公钥发送给Bob,$x$作为私钥保留
  2. 若Bob要给Alice发送信息$P_m \in G \cup \{O\}$,需要先随机生成一个$k\in[1,n-1]$,然后用以下加密(encryption)算法将其加密为密文$c=Enc(pk,k,P_m)=(C_1,C_2)$:$$C_1=kP$$ $$C_2=P_m+kQ$$
  3. Alice收到密文$c=(C_1,C_2)$后,用以下解密(decryption)算法还原出明文$P_m$:$$Dec(sk,c)=C_2 - xC_1$$.

正确性:

$$\begin{aligned}
C_2-xC_1=&(P_m+kQ)-x(kP) \\
        =&(P_m+k(xP))-x(kP) \\
        =&P_m
\end{aligned}$$

安全性:

攻击者要想从$c=(C_1,C_2)$计算出$P_m$,就必须知道$k$,而要从$P$和$kP$中计算出$k$将面临求解椭圆曲线上的离散对数问题.

代码:

import time
def divi(a): #分解质因数
    i=2
    ans=[]
    while(i*i<a):
        while(a%i==0):
            ans.append(i)
            a=a//i
        i=i+1
    if(a>1):
        ans.append(a)
    return ans

def gcd(a,b): #辗转相除法
    if(b == 0):
        return a
    else:
        return gcd(b,a%b)
    
def exgcd(a,b): #扩展欧几里得算法
    if(b == 0):
        x = 1
        y = 0
        return a,x,y
    ans,t,x = exgcd(b,a%b)
    y = t-a//b*x
    return ans,x,y

def qpow(base,exp,mod): #快速幂
    ans = 1
    while(exp>0):
        if(exp%2 == 1):
            ans = ans*base%mod
        exp = exp//2
        base = base*base%mod
    return ans

def rev(a,mod,type ='not_prime'):
    if(type =='prime'): #费马小定理求逆元
        return qpow(a,mod-2,mod)
    else: #辗转相除法求逆元
        g,x,y = exgcd(a,mod)
        if(g!= 1):
            return -1 #逆元不存在
        else:
            return (x+mod)%mod
        

def ECCAdd(p1,p2,p,a,b): #椭圆曲线上的加法
    if(p1=='inf'):
        return p2
    elif(p2=='inf'):
        return p1
    # 无穷远点定义为零元
    x1,y1=p1
    x2,y2=p2
    if(x1==x2 and y1+y2==p):
        return 'inf'
    # 定义关于y轴对称两点(在模意义下实际上是相加得p)互为加法逆元,相加得零元

    if(x1==x2 and y1==y2):
        λ=(3*x1*x1+a)*rev(2*y1,p,type='prime')%p # 两点不重合,过此两点做一条直线
    else:
        λ=(y2-y1)*rev((x2-x1+p)%p,p,type='prime')%p # 两点重合,则取其与曲线的切线

    x3=(λ*λ-x1-x2+p)%p
    y3=(λ*(x1-x3)-y1+p)%p
    # 定义这条直线与曲线的另一个交点为群上加法的结果
    return (x3,y3)

def EccMinus(p1,p2,p,a,b): #做减法就是加上加法逆元
    if(p2=='inf'):
        return p1
    else:
        return ECCAdd(p1,(p2[0],p-p2[1]),p,a,b)


def ECCMult(k,base,p,a,b): #椭圆曲线上的数乘(龟速乘)
    ans = 'inf'
    while(k>0):
        if(k%2 == 1):
            ans = ECCAdd(ans,base,p,a,b)
        k = k//2
        base = ECCAdd(base,base,p,a,b)
    return ans



p=11 #定义模数
a=1
b=6 #当4a^3+27b^2 ≠0 mod p 时,可定义一个交换群
print('p =',p,'a =',a,'b =',b)
if(4*a*a*a+27*b*b%p==0):
    print("无法构成交换群")
    exit()
else:
    print("可以构成交换群,群上的元素有:")
n=0 #统计椭圆曲线上的元素个数
for x in range(p):
    z=(x*x*x+a*x+b)%p
    for y in range(p):
        if(y*y%p==z):
            print((x,y))
            n=n+1
print('inf')
n=n+1 #这个值同时也是(除零元O以外的)所有的点的阶数
# 即,使得 nP=O 最小的正整数n

print("Gen()->pk,sk")
P=(5,2)
x=6
Q=ECCMult(x,P,p,a,b)
print('P =',P)
print('x =',x)
print('Q = x * P =',Q)
print('pk:(Q =',Q,')')
print('sk:(x =',x,')')

print('\n')
print("Enc(pk,m)->c")
m = (8,8)
print('m =',m)
k=int(time.time()*1000)%(n-1)+1
print('k =',k)
c1=ECCMult(k,P,p,a,b)
c2=ECCAdd(m,ECCMult(k,Q,p,a,b),p,a,b)
print('c1 = kP =',c1)
print('c1 = m + kQ =',c1)
print('c = ( c1 =',c1,'c2 =',c2,')')

print('\n')
print('Dec(c,sk)->m')
m1 = EccMinus(c2,ECCMult(x,c1,p,a,b),p,a,b)
print('''m = c2 - x * c1 =''',m1)

运行结果:

p = 11 a = 1 b = 6
可以构成交换群,群上的元素有:
(2, 4)
(2, 7)
(3, 5)
(3, 6)
(5, 2)
(5, 9)
(7, 2)
(7, 9)
(8, 3)
(8, 8)
(10, 2)
(10, 9)
inf
Gen()->pk,sk
P = (5, 2)
x = 6
Q = x * P = (2, 4)
pk:(Q = (2, 4) )
sk:(x = 6 )


Enc(pk,m)->c
m = (8, 8)
k = 10
c1 = kP = (7, 2)
c1 = m + kQ = (7, 2)
c = ( c1 = (7, 2) c2 = inf )


Dec(c,sk)->m
m = c2 - x * c1 = (8, 8)

(注:若待加密的消息过长(二进制值超过了模数),可将其二进制形式进行分块,然后参与加密运算)

(若待签名的消息过长,可直接对其哈希值进行签名)

posted @ 2023-09-03 17:30  Isakovsky  阅读(35)  评论(0编辑  收藏  举报