密码学之背包加密系统(Merkle–Hellman公钥加密算法)原理

背包加密系统

背包问题

假定一个背包可以承重 W,现在有 n 个物品,其重量分别为 a 1 , a 2 , a 3 , . . . , a n a_1,a_2,a_3,...,a_n a1,a2,a3,...,an, 问装哪些物品可以恰好使得背包装满,并且每个物品只能被装一次。背包问题其实就是在解这样的一个问题:

x 1 a 1 + x 2 a 2 + x 3 a 3 + . . . + x n a n = W x_1a_1 + x_2a_2 + x_3a_3 + ... + x_na_n = W x1a1+x2a2+x3a3+...+xnan=W

其中所有的 x i x_i xi 只能为 0 和 1。显然我们必须枚举所有的 n 个物品的组合才能解决这个问题,而复杂度也就是 2n,这也就是背包加密的妙处所在。

在加密时,如果我们想要加密的明文为 x x x,那么我们可以将其表示为 n 位二进制数,然后分别乘上 a i a_i ai 即可得到加密结果。

但是解密的时候,该怎么办呢?我们确实让其他人难以解密密文,但是我们自己也确实没有办法解密密文。

但是当是超递增的话,我们就有办法解了,所谓超递增是指序列满足如下条件:

a i > ∑ k = 1 i − 1 a k a_i > \sum_{k=1}^{i-1}{a_k} ai>k=1i1ak

即第 i i i 个数大于前面所有数的和。

超递增背包问题的解很容易找到,我们可以计算其总量并与背包序列中最大的数比较,如果总重量小于这个数,则它不在背包中;如果总重量大于这个数,则它在背包中。背包重量减去这个数,进而考察序列中下一个最大的数,重复直到结束。

但是,这样又出现了一个问题,由于 a i a_i ai 是公开的,如果攻击者截获了密文,那么它也就很容易去破解这样的密码。为了弥补这样的问题,就出现了 Merkle–Hellman 这样的加密算法。

Merkle–Hellman

公私钥生成

生成私钥

私钥就是我们的初始的背包集,这里我们使用超递增序列,怎么生成呢?我们可以假设 a 1 = 1 a_1 = 1 a1=1,那么 a 2 a_2 a2 大于 1 即可,类似的可以依次生成后面的值。

生成公钥

在生成公钥的过程中主要使用了模乘的运算。

首先,我们生成模乘的模数 m m m,这里要确保

m > ∑ i = 1 i = n a i m > \sum_{i=1}^{i=n}{a_i} m>i=1i=nai

其次,我们选择模乘的乘数 w w w,作为私钥并且确保

g c d ( w , m ) = 1 gcd(w,m) = 1 gcd(w,m)=1

之后,我们便可以通过如下公式生成公钥

b i = w a i m o d    m b_i = wa_i \mod m bi=waimodm

并将这个新的背包集 b i b_i bi m m m 作为公钥。

加解密

加密

假设我们要加密的明文为 v,其每一个比特位为 v i v_i vi ,那么我们加密的结果为

m > ∑ i = 1 i = n b i v i m o d    m m > \sum_{i=1}^{i=n}{b_iv_i\mod m} m>i=1i=nbivimodm

解密

对于解密方,首先可以求的 w w w 关于 m m m 的逆元 w − 1 w^{-1} w1

然后我们可以将得到的密文乘以 w − 1 w^{-1} w1 即可得到明文,这是因为

∑ i = 1 i = n w − 1 b i v i m o d    m = ∑ i = 1 i = n a i v i m o d    m \sum_{i=1}^{i=n}{w^{-1}b_iv_i\mod m} = \sum_{i=1}^{i=n}{a_iv_i\mod m} i=1i=nw1bivimodm=i=1i=naivimodm

这里有

b i ≡ w a i m o d    m b_i \equiv wa_i \mod m biwaimodm

对于每一块的加密的消息都是小于 m 的,所以求得结果自然也就是明文了。

破解

该加密体制在提出后两年后该体制即被破译,破译的基本思想是我们不一定要找出正确的乘数 w(即陷门信息),只需找出任意模数 m ′ m^′ m 和乘数 w ′ w^′ w,只要使用 w ′ w^′ w 去乘公开的背包向量 B 时,能够产生超递增的背包向量即可。

Example

题目来源:Dest0g3 Crypto Bag

题目代码:

import gmpy2
from Crypto.Util.number import *
from secret import flag

message = bytes_to_long(flag[8:-1])
Baglenth=286
Bag=[]
Bag=Bag[::-1]
m=372992427307339981616536686110115630075342113098010788080347982669869622759400031649792
w=274062421102700155372289583695782343443
assert gmpy2.gcd(m,w)==1
h=0
j=0
if m.bit_length()%2==0:
    h=m.bit_length()
    j=int(h//2)
else:
    h=m.bit_length()
    j=int(h//2+1)
def pad(m,lenth):
    while len(m)<lenth:
        m='0'+m
    return m
def keygen():
    pk=[]
    sk=[]
    sk.append(m)
    sk.append(int(gmpy2.invert(w,m)))
    D=[]
    binD=[]
    for i in range(Baglenth):
        di=(w*Bag[i])%m
        D.append(di)
        bindi=bin(di)[2:]
        bindi=pad(bindi,h)
        binD.append(bindi)
    U=[]
    V=[]
    for i in range(Baglenth):
        tempu=int(str(binD[i][:j]),2)
        U.append(tempu)
        tempv=int(str(binD[i][j:]),2)
        V.append(tempv)
    e=gmpy2.next_prime(sum(V))+2
    f=gmpy2.next_prime(sum(U))
    assert gmpy2.gcd(e,f)==1
    sk.append(int(e))
    sk.append(int(f))
    for i in range(Baglenth):
        ai=e*U[i]+f*V[i]
        pk.append(int(ai))
    return pk,sk
Pk,Sk=keygen()
print(Pk)
print(Sk)
def Encrypt(plain,pk):
    mbin=bin(plain)[2:]
    c=0
    mbin=pad(mbin,Baglenth)
    for i in range(Baglenth):
        c=c+int(mbin[i])*pk[i]
    return c
c=Encrypt(message,Pk)
print(c)

使用LLL(Lenstra-Lenstra-Lovász)格基约化算法求解:

pubKey = []
nbit = len(pubKey)
encoded = 1475864207352419823225329328555476398971654057144688193866218781853021651529290611526242518
A = Matrix(ZZ, nbit + 1, nbit + 1)
for i in range(nbit):
    A[i, i] = 1
for i in range(nbit):
    A[i, nbit] = pubKey[i]
A[nbit, nbit] = -int(encoded)

res = A.LLL()
for i in range(0, nbit + 1):
    M = res.row(i).list()
    flag = True
    for m in M:
        if m != 0 and m != 1:
            flag = False
            break
    if flag:
        print(i, M)
        M = ''.join(str(j) for j in M)
        M = M[:-1]
        M = hex(int(M, 2))[2:]
        print(bytes.fromhex(M))

参考链接

背包加密 - CTF Wiki
密码学——公钥密码体系之背包算法1 - 哔哩哔哩
Dest0g3 520迎新赛 | Lazzaro

  • 7
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值