一、简介
数据库经过加密算法加密后会以密文的形式存在,数据的隐私因此受到加密算法的保护,然而为了保证数据库的可用性,必须要求对密文进行有效的检索查询.
可搜索查询的加密算法均只容许单一的精准匹配,换句话说,用户的查询请求只能匹配到某个具体密文,而对于更加复杂的查询请求(如范围查询),以上算法都是无效的。而保序加密算法能够解决此问题,所以它适用于数据库的加密需求,允许在密文数据库上进行比较操作。
二、算法描述
Boldyreva 等人提出了一种利用随机保序函数和超几何分布设计的可证安全的OPE 算法。其根据语义安全的概念定义了 OPE 算法的理想安全状态,并给出了 OPE 算法的安全性证明,即攻击者即使得到全部密文,除了密文的顺序以外,就再也得不到其它任何有用的信息。
1. 随机保序函数和超几何分布的关系
超几何分布 在一个抽球模型中,假若有 N 个球,其中黑球 M 个,则白球 N–M 个,每次不放回地随机抽取一个球。用随机变量 X 表示在抽取的 y 个球中黑球的个数,那么 X=x 的概率为

这里直接使用numpy里面的库:
超几何分布(hypergeometric):
----numpy中实现:
np.random.hypergeometric(ngood,nbad,nsample,size)-->产生size个随机数,每个随机数为在总样本中随机抽取
nsample个样本后好样本的个数。所有样本由ngood个好样本和nbad个坏样本组成。
import numpy as np
# 超几何分布,7个好的3个坏的,摸3个,重复10次,返回好球的个数组成的数组
r = np.random.hypergeometric(7, 3, 3, 10)
print(r)
运行结果:
[2 1 3 3 2 2 3 3 2 2]
2. 保序函数模型
例如下图,横坐标是明文域,纵坐标是密文域,密文域要比明文域大。加密的过程就是,将明文在保证顺序的情况下,随机映射到密文域中。在二位坐标系中就是,这些点的连线是一个单调递增的函数。

3. 保序函数伪代码
论文中的伪代码讲的很详细完全可以复现
4. 保序函数实例讲解
密钥表 key_table 是一个保存抽样点的 n×2 矩阵,密钥表初始是空的,随着加密明文越多而增加。在整个加密过程中共用该密钥表,其作用类似于使用同一个随机保序函数 f,使得每次加密都沿着该函数进行。解密过程中,密钥表只作查询使用,并不会有所增加。为了更好理解上述过程,在图 2-6 中给出了详细的加解密过程的例子。
假设明文域(1,…, 100)要映射到密文域(1,…, 1000),现要分别加密 25 和 26 两个值。详细过程如下:
加密值25时,第一步中,把密文域 R 中值 500 传进超几何分布函数 HGD 中,得出抽样值 44>25,则向左进一步缩小明文域 D 和密文域 R 的范围,将抽样点(500, 44)保存到密钥表 key_table,以此重复该过程,到了第七步时明文域只剩值 25,则从对应密文域中随机抽取一个值作为对应的密文。
加密值 26 时,由于前六步均可在密钥表 key_table中找到抽样点,则不需要使用超几何分布函数 HGD,到了第七步,把抽样点(306, 26)存进表中。可以发现,两个连续的明文值所对应的密文值,会分别落在两个连续且不相交的密文区间中,因此算法是保序的。该算法的解密过程类似于加密过程,整个过程中,key_table 只作查询使用。
三、python仿真实验
新建一个BCLO09.csv文件,存放密钥表格
再创建一个BCLO09.py文件
import csv
import random
import numpy as np
import pymysql
key_table = "BCLO09.csv"
def HGD(D, R):
len_D = len(D)
len_R = len(R)
y = int(len_R / 2)
x = np.random.hypergeometric(len_D, len_R, y, size=1)
a = x[0]
if x[0] >= len_D:
a = len_D - 1
return D[a]
def writeCsv(path, data, modle): #传入变量csv文件的路径和要写入的数据
with open(path,modle,newline="") as f: # 以写入的方式打开文件,newline="" 可以让数据不隔行写入
writer_csv=csv.writer(f) #调用csv的writer方法往文件里面写入数据,并赋值给writer_scv变量
writer_csv.writerow(data) #把数据循环写入到writer_csv变量中
#读取csv文件
def readCsv(path): #传入变量csv文件的路径
list_one=[] #定义一个空列表
with open(path,"r") as f: #以只读的方式打开文件
read_scv=csv.reader(f) #调用csv的reader方法读取文件并赋值给read_scv变量
for i in read_scv:
list_one.append(i) #将读取到的数据追加到list列表里面
return list_one #返回列表数据
def key_tableIsContain(param):
list_one = readCsv(key_table)
if len(list_one) == 0:
return 0, 0
flag = 0
for i in range(len(list_one)):
if str(int(param)) == list_one[i][0]:
flag = 1
return 1, int(list_one[i][1])
if flag != 1:
return 0, 0
def Enc2(key_table, D, R, m):
_D = D
_R = R
M = max(D) - min(D) + 1
N = max(R) - min(R) + 1
center = int((max(R) + min(R)) / 2)
iscontain, values = key_tableIsContain(center)
if iscontain == 1:
c = values #记录D中的抽样值
x = values
else:
x = HGD(D, R)
a = []
a.append(center)
a.append(x)
writeCsv(key_table, a ,"a")
print("区间是D[%d, %d]" % (min(_D), max(_D)))
print("区间是R[%d, %d]" % (min(_R), max(_R)))
if M == 1:
print(m)
print("区间是R[%d, %d]" % (min(_R), max(_R)))
# return random.randint(min(R), max(R))
return str(m) + " " + str(min(R)) + " " + str(max(R))
if M == 2:
print(m)
if min(D) == m:
print("最后的区间是R(%d, %d]" % (min(_R), center))
return str(m) + " " + str(min(R)) + " " + str(center)
else:
print("最后的区间是R(%d, %d]\n" % (center, max(_R)))
return str(m) + " " + str(center) + " " + str(max(R))
# return random.randint(min(R), max(R))
if m <= x: #左边
D = []
for i in range(min(_D), x + 1):
D.append(i)
R = []
for j in range(min(_R), center + 1):
R.append(j)
else: #右边
D = []
for i in range(x + 1, max(_D) + 1):
D.append(i)
R = []
for j in range(center + 1, max(_R) + 1):
R.append(j)
print("D[%d, %d] 抽样值:%d"%(min(_D), max(_D), x))
print("R[%d, %d] 中间值:%d \n" % (min(_R), max(_R), center))
return Enc2(key_table, D, R, m)
def Dnc2(key_table, D, R, m):
_D = D
_R = R
M = max(D) - min(D) + 1
N = max(R) - min(R) + 1
center = int((max(R) + min(R)) / 2)
print("区间是D[%d, %d]" % (min(_D), max(_D)))
print("区间是R[%d, %d]" % (min(_R), max(_R)))
if M == 1:
print(m)
print("明文是:%d\n区间是R[%d, %d]" % (min(D), min(_R), max(_R)))
return random.randint(min(R), max(R))
if M == 2:
if m <= center:
print("明文是:%d\n最后的区间是R(%d, %d]" % (min(D), min(_R), center))
else:
print("明文是:%d\n最后的区间是R[%d, %d)\n" % (max(D), center, max(_R)))
return random.randint(min(R), max(R))
iscontain, values = key_tableIsContain(center)
if iscontain == 1:
c = values # 记录D中的抽样值
x = values
else:
print("可能解密错误!")
return
x = HGD(D, R)
a = []
a.append(center)
a.append(x)
# writeCsv(key_table, a, "a")
if m <= center: # 左边
D = []
for i in range(min(_D), x + 1):
D.append(i)
R = []
for j in range(min(_R), center + 1):
R.append(j)
else: # 右边
D = []
for i in range(x + 1, max(_D) + 1):
D.append(i)
R = []
for j in range(center + 1, max(_R) + 1):
R.append(j)
return Dnc2(key_table, D, R, m)
if __name__ == '__main__':
# searchContentList = searchContent()
# print(searchContentList)
# Back()
while (1):
print("=================\n"
"\t1、加密\n"
"\t2、解密\n"
"\t3、循环加密\n"
"=================")
flag = input("请输入你要操作的编号:")
if flag == "1":
D = []
for i in range(100):
D.append(i + 1)
R = []
for i in range(1000):
R.append(i + 1)
m = input("请输入你要加密的数字")
Enc2(key_table, D, R, int(m))
if flag == "2":
D = []
for i in range(100):
D.append(i + 1)
R = []
for i in range(1000):
R.append(i + 1)
m = input("请输入你要解密的数字")
Dnc2(key_table, D, R, int(m))
if flag == "3":
D = []
result = []
for i in range(100):
D.append(i + 1)
R = []
for i in range(1000):
R.append(i + 1)
for i in range(100):
result.append(Enc2(key_table, D, R, int(i + 1)))
for c in range(len(result)):
print(result[c])
四、参考文献
[1]何嘉勇. 保序加密域中的数据库水印算法研究[D].暨南大学,2018.