其实就是一次课堂作业,写一下过程
Paillier算法的原理很简单,如图所示
首先是建立一个新环境
我使用anaconda建立Python的新环境,直接运行prompt输入 conda create –n python27_env (环境名) python=2.7 以创建 python 2.7 环境
conda create -n Paillier python=2.7
用到的库有gmpy2,time,binascii,其中只有gmpy2需要下载,在虚拟环境下直接
conda install gmpy2
其余两个库可直接引入
其次是写Paillier库
这个库里面包含初始化(init)、计算函数(L)、密钥生成(keygen)、加密和解密函数构成的。
直接贴代码
import gmpy2 as gy
import time
import binascii
#gmpy2为对大整数运算的库函数,包含初始化、求最大公因数、求逆元、判断奇偶数素数合数取余开方等运算
class Paillier(object):
def __init__(self, pubKey=None, privateKey=None):
self.pubKey = pubKey
self.privateKey = privateKey
def __L__(self, x, n):
res = (x - 1) // n
return res
def gen_prime(self, rs):
p = gy.mpz_urandomb(rs, 1024)
while not gy.is_prime(p):
p += 1
return p
def keygen(self):
while True:
rs = gy.random_state(int(time.time()))
p = self.gen_prime(rs)
q = self.gen_prime(rs)
n = p * q
lmd = (p - 1) * (q - 1)
if gy.gcd(n, lmd) == 1:
break
g = n + 1
mu = gy.invert(lmd, n)
self.pubKey = [n, g]
self.privateKey = [lmd, mu]
def encrypt(self, plaintext):
m = gy.mpz(plaintext) # Ensure plaintext is converted to mpz
n, g = self.pubKey
r = gy.mpz_random(gy.random_state(int(time.time())), n)
while gy.gcd(n, r) != 1:
r += 1
ciphertext = (gy.powmod(g, m, n ** 2) * gy.powmod(r, n, n ** 2)) % (n ** 2)
return ciphertext
def decrypt(self, ciphertext):
n, _ = self.pubKey
lmd, mu = self.privateKey
m = self.__L__(gy.powmod(ciphertext, lmd, n ** 2), n)
plaintext = m*mu %n
return plaintext
def add(self, ciphertext1, ciphertext2):
n, _ = self.pubKey
result = (ciphertext1 * ciphertext2) % (n ** 2)
return result
这样一个简单的Paillier库就完成了,我们进行一个测试
新建test.py:
import Paillier
pai = Paillier.Paillier()
pai.keygen()
pubKey = pai.pubKey
print("密钥已生成")
plaintext1 = input("请输入第一个明文: ")
plaintext2 = input("请输入第二个明文: ")
print("公钥:",pubKey)
ciphertext1 = pai.encrypt(plaintext1)
ciphertext2 = pai.encrypt(plaintext2)
print("第一个明文加密结果", ciphertext1)
print("第二个明文加密结果", ciphertext2)
detext1=pai.decrypt(ciphertext1)
detext2=pai.decrypt(ciphertext2)
print("第一个密文解密结果:",detext1)
print("第二个密文解密结果:",detext2)
re=pai.add(ciphertext1, ciphertext2)
print("两密文相乘结果为:",re)
deciphertext = pai.decrypt(re)
print("密文相乘解密得到明文: ", deciphertext)
Paillier算法具有加法同态性,即密文相乘(模n²)后解密的结果为明文相加的结果,例如输入111与222,输出密文相乘解密得到的明文为333
经过测试后可以看出我们的算法是可行的,那么进行匿名投票算法的编写
新建ElectronicVoting.py
在这里,我们规定每位投票者有且仅有一票,即最多投一票,可以选择不投,但是由于票数均为零对结果无影响我们这里不予考虑,假设每位投票者的票均会投出。
代码如下:
import Paillier
print("规定每位投票者只有一票且必须投出,投票者将每位候选者的票数加密后发送给统计者,统计者进行相乘,而后公布者进行解密")
x=input("请输入候选者人数数量")
y=input("请输入投票者人数数量")
n=eval(y)
pai = Paillier.Paillier()
pai.keygen()
pubKey = pai.pubKey
s=[]
for i in range(0,n):
print("请第",i+1,"名投票者为候选者投票")
l=[]
for j in range(0,eval(x)):
print("请为第",j+1,"名投票者投票:")
c=input()
l.append(pai.encrypt(c))#将每位投票者对每位候选人的票数加密后存储到列表中
s.append(l)
q=[]
for i in range(0,eval(x)):
re=1
for j in range(0,n):
re = (re * s[j][i]) % (pai.pubKey[0] ** 2)#利用加法同态性进行票数相乘计算
print("第",i+1,"位候选者获得了",pai.decrypt(re),"张选票")
q.append(pai.decrypt((re)))
max_votes = max(q)
winner_index = q.index(max_votes) + 1 # 候选者编号从1开始
print("\n获得最多票数的候选者是第", winner_index, "位,共获得", max_votes, "张选票。")
即可得到结果,结果如图
总结
在这段代码中能够完成对于票数的匿名统计,但是还存在一些问题,比如每位投票者加密使用的公钥都是一样的,这样也许某位投票者能够根据自己结果加密出来的密文推断出他人投票的结果,后续还需改进
感谢大家收看