不经意传输实验
该协议的安全性在于步骤 3) 中,服务器端 S 无法知晓客户端 C 的选择的随机数 r , 从而对客户端 C 的最终选择也就无从知晓。
不经意传输协议可用于隐私信息检索(Private Information Retrieval – PIR,也叫匿踪查询)是安全多方计算中很实用的一项技术,用来保护用户的查询隐私。其目的是保证用户向服务器(数据源方)提交查询请求时,在用户查询信息不被泄漏的条件下完成查询。即整个查询过程中服务器不知道用户具体查询信息及查询出的数据项。
2. 实验环境
申请资源:2台ECS
操作系统:Window 10 及以上
预装:AnaConda
3. 实验步骤1-配置编程环境
配置编程环境(资源环境如果较卡顿,可以打开任务管理器将占据CPU资源过大的进程关掉)
1)申请实验资源后,“云产品资源”选项卡下将显示已申请:云服务器ECS-1 和云服务器ECS-2 。
2)本地打开系统的“远程桌面”,复制云服务器ECS-1 中“弹性IP”对应的 IP 地址到远程桌面连接的“计算机©:”处,点击“连接”。
3)弹出“输入你的凭据”窗口,将云服务器ECS-1 中的“用户名”和“密码”复制到窗口,确认连接到远程计算机。
4)连接到云服务器ECS-1 后,全屏状态,在左下角“开始”菜单,“应用”中打开“Anaconda Prompt”。输入
jupyter-lab
5)在弹出的浏览器页面中,选择“Notebook”下“Python 3”,默认建立一个Python文件。可重命名为“Server.ipynb”,后续步骤3中的代码放置于此文件中。
6)关于云服务器ECS-2 的远程连接,Python文件建立与以上步骤2)-5)类似,可将云服务器ECS-2 上的Python文件命名为“Clint.ipynb”。
4. 实验步骤2-实现基本的RSA算法
实现基本的RSA算法
在服务器端和客户端均需要实现基本的 RSA 算法,即步骤 2 的代码需放入步骤 3 和 4 的代码中,即文件“Server.ipynb”和“Clint.ipynb”中。
在本实验示例代码中,可能用到 random、sympy 库,需先导入。
import random
import sympy
在RSA算法的简单实现示例中,需要完成6个函数功能,分别是:
1)gcd(a, b) ,功能:求 a 与 b 的最大公约数。输入:整数 a 和 b。输出: a 与 b 的最大公约数。请同学们自行编写函数 gcd(a, b) 。
示例代码如下:
def gcd(a, b):
while b != 0:
a, b = b, a % b
return a
2)multiplicative_inverse(e, phi),功能:求 e 模 phi 的乘法逆元,用于计算私钥。输入:公钥 e,欧拉函数phi。输出:私钥 d 和 欧拉函数 phi 。请同学们自行编写函数 multiplicative_inverse(e, phi) 。
示例代码如下:
def multiplicative_inverse(e, phi):
d = 0
x1 = 0
x2 = 1
y1 = 1
temp_phi = phi
while e > 0:
temp1 = temp_phi//e
temp2 = temp_phi - temp1 * e
temp_phi = e
e = temp2
x = x2- temp1* x1
y = d - temp1 * y1
x2 = x1
x1 = x
d = y1
y1 = y
if temp_phi == 1:
return d + phi
3)generate_keypair(p, q),功能:生成公钥(e, n)和私钥(d, n)。输入:素数p,素数q。输出:公钥(e, n),私钥(d, n)。请同学们根据 RSA 算法原理,自行编写函数 generate_keypair(p, q)。
示例代码如下:
def generate_keypair(p, q):
n = pq #计算模数 n
phi = ((p-1)(q-1)) #计算欧拉函数 phi
e = random.randrange(1, phi) #随机选择一个公钥 e
g = gcd(e, phi) #计算 e 与 phi 的最大公约数
while g != 1: # 要求 e 与 phi 互素,循环选择 e 达到要求
e = random.randrange(1, phi)
g = gcd(e, phi)
d = multiplicative_inverse(e, phi)
return ((e, n), (d, n))
4)encrypt(pk, plaintext),功能:加密算法。输入:公钥 pk,明文plaintext。输出:密文 cipher。请同学们自行编写函数
示例代码如下:
def encrypt(pk, plaintext):
key, n = pk
cipher = (plaintext ** key)%n
return cipher
5)decrypt(pk, ciphertext),功能:解密算法。输入:私钥 pk,密文ciphertext。输出:明文plain。请同学们自行编写函数 decrypt(pk, ciphertext)。
示例代码如下:
def decrypt(pk, ciphertext):
key, n = pk
plain = (ciphertext ** key) % n
return plain
实验中,假设服务器端有数据 ‘m1’:111 和 ‘m2’:222。客户端想查询其中一个数据 m1 或者 m2,但是不想服务器知道客户端具体收到了哪个数据。先中服务器端初始化数据。
#给服务器端数据赋初值
query_data = {‘m1’:111,‘m2’:222}
服务器端建立 socket 服务,用于服务器端和客户端的连接服务。注意:此段代码中需要更新绑定的ECS的IP。
def socket_service():
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 防止socket server重启后端口被占用(socket.error: [Errno 98] Address already in use)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((‘IP’, 6666))#此处需绑定分配ECS-1的私有地址
s.listen(10)
except socket.error as msg:
print(msg)
sys.exit(1)
print(‘Waiting connection…’)
while 1:
conn, addr = s.accept()
t = threading.Thread(target=deal_data, args=(conn, addr))
t.start()
当服务器端收到客户端连接请求后,处理收到的数据。不经意传输协议主要在这个函数中实现。请同学自行设计服务器与客户端的交互命令,实现不经意传输协议。
示例代码如下:
def deal_data(conn, addr):
print(‘Accept new connection from {0}’.format(addr))
conn.send((‘Hi, Welcome to the server!’).encode())
#生成2对rsa密钥
p1 = sympy.randprime(1, 100)
q1 = sympy.randprime(1, 100)
p2 = sympy.randprime(1, 100)
q2 = sympy.randprime(1, 100)
public1, private1 = generate_keypair(p1, q1)
public2, private2 = generate_keypair(p2, q2)
print(public1)
print(private1)
print(public2)
print(private2)
while 1:
data = conn.recv(1024)
command = data.decode()
#如果服务器端收到客户端的命令“get public keys”,则发送2个公钥给客户端
if command == ‘get public keys’:
s = (public1.str()+‘?’+public2.str()).encode()
print(s)
conn.send(s)
#如果服务器端收到客户端的命令“Query Data”
if command == ‘Query Data’:
# 解密数据
print(‘---------------解密数据---------’)
c = conn.recv(1024)
c = int.from_bytes(c, byteorder=‘big’)
print©
r1 = decrypt(private1,c)
r2 = decrypt(private2,c)
print('服务器端收到的 2 个解密后的随机数:')
print(r1)
print(r2)
m1 = query_data['m1']
m2 = query_data['m2']
e1 = m1^r1
e2 = m2^r2
#int转bytes
e1 = e1.to_bytes(8,byteorder = 'big',signed = False)
e2 = e2.to_bytes(8,byteorder = 'big',signed = False)
conn.send(e1)
time.sleep(4)
conn.send(e2)
if command == 'exit' or not data:
print('{0} connection close'.format(addr))
conn.send(bytes('Connection closed!'),'UTF-8')
break
conn.close()
最后,编写主函数,程序的执行起点,调用socket_service()。
if name == ‘main’:
socket_service()
在本实验示例代码中,将用到 socket、sys、random、 numpy库,需先导入。
#--------------隐私查询客户端-------------
import socket
import sys
import random
import numpy as np
客户端建立 socket 服务,用于服务器端和客户端的通信,同时完成不经意传输协议。请同学自行设计服务器与客户端的交互命令,实现不经意传输协议。
def socket_client():
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#此处需绑定分配 ECS-1 的 私有地址,端口号与服务器端一致即可
s.connect((‘IP’, 6666))
except socket.error as msg:
print(msg)
sys.exit(1)
print(s.recv(1024))
#目的在于接受:Accept new connection from (…
while 1:
command = input(‘please input command: ‘)
s.send(command.encode())
#向服务器端请求公钥
if command == ‘get public keys’:
print(’----------get public keys----------’)
ku1,ku2 = s.recv(1024).decode().split(‘?’)
ku1 = tuple(eval(ku1))
ku2 = tuple(eval(ku2))
print(ku1)
print(ku2)
#向服务器端查询数据,请用户选择,若用户输入“1”表示查询数据 m1,用户输入“2”表示查询数据 m2。
if command == 'Query Data':
p = input('please input public key 1 or 2: ')
if p =='1':
k = ku1
if p =='2':
k = ku2
else:
k = ku1
#随机生成0——1000的随机整数
r = random.randint(0,1000)
print('random number:')
print(r)
#把整数转换为bytes;
#第一个参数8表示bytes的长度是8bytes
#byteorder这是设置为大端;signed表示是否有符号;
#r_b = r.to_bytes(8,byteorder = 'big',signed = False)
# 加密数据
crypto = encrypt(k,r)
crypto = crypto.to_bytes(8,byteorder = 'big',signed = False)
s.send(crypto)
for i in np.arange(2):
e = s.recv(1024)
e = int.from_bytes(e, byteorder='big')
m = e^r
print(m)
if command == 'exit':
break
s.close()
最后,编写客户端主函数,程序的执行起点,调用socket_client()。
if name == ‘main’:
socket_client()