BCP加密算法復(fù)現(xiàn)記錄

1.BCP算法簡(jiǎn)介

2.復(fù)現(xiàn)BCP加密算法

2.1 環(huán)境配置

  • OS: Linux(Ubuntu 18.04 LTS)
  • 編程語(yǔ)言:Python3.6
  • 依賴包:Charm惧蛹,Charm是Joseph A. Akinyele等在2013提出的一個(gè)用于進(jìn)行快速加密的平臺(tái)(Python庫(kù))扇救。項(xiàng)目地址:https://github.com/JHUISI/charm 。Charm庫(kù)本身還需要依賴GMP,PBCOPENSSL. 關(guān)于Charm及其依賴包的安裝可以參考文章:Charm-crypto的安裝與使用香嗓。

2.2 重要步驟說(shuō)明

  • 首先需要從Charm包中導(dǎo)入相關(guān)的函數(shù)迅腔、類。
from charm.toolbox.integergroup import IntegerGroup
from charm.schemes.pkenc.pkenc_rsa import RSA_Enc, RSA_Sig
from charm.core.math.integer import integer,randomBits,random,randomPrime,isPrime,encode,decode,hashInt,bitsize,legendre,gcd,lcm,serialize,deserialize,int2Bytes,toInt

這些函數(shù)的作用可以根據(jù)其名字推斷靠娱,例如randomPrime是隨機(jī)找一個(gè)質(zhì)數(shù)沧烈,isPrime是判斷一個(gè)數(shù)是否為質(zhì)數(shù),等等像云。

  • 第一步:初始化锌雀,生成公共參數(shù)和系統(tǒng)主密鑰。
    選擇一個(gè)安全參數(shù)secparam迅诬,用來(lái)表示位長(zhǎng)度(一個(gè)整數(shù)轉(zhuǎn)換為二進(jìn)制后的長(zhǎng)度腋逆,稱為位長(zhǎng)度)。選取兩個(gè)安全素?cái)?shù)p,q侈贷,之后計(jì)算p^{'} = (p -1)/2,q^{'} = (q-1)/2惩歉,根據(jù)安全素?cái)?shù)的定義,p^{'}q^{'}也都是素?cái)?shù)俏蛮。之后計(jì)算N = pq作為模數(shù)撑蚌,使其位長(zhǎng)度為secparam。之后選取g\in Z^*_{N^2}(Z^*_{N^2}表示從1到N^2所有與N^2互質(zhì)的數(shù))搏屑,使得g的階(群中元素的階的概念)為pp^{'}qq{'},并且使得g^{p^{'}q^{'}} mod N^2 = 1+ k N,k\in[1,N-1]争涌。這樣明文空間是Z_N,公共參數(shù)PP = (N,k,g),主密鑰MK = (p^{'},q^{'})辣恋。
    相關(guān)代碼如下:
def __init__(self,secparam=1024,param = None):
        # 安全參數(shù)secparam被設(shè)置為1024亮垫,即默認(rèn)的位長(zhǎng)度是1024
        # 可以指定參數(shù)
        if param:
            self.N2 = param.N2
            self.N = param.N
            self.g = param.g
            self.k = param.k   
        # 如果未指定參數(shù)         
        else:
            # p,q用randomPrime()函數(shù)生成解幼,位長(zhǎng)度是512位(N=pq,要求N是1024位,則p,q為512位)
            # 第二個(gè)參數(shù)設(shè)置為True包警,表示是安全素?cái)?shù)撵摆,即可以保證(p-1)/2和(q-1)/2也是素?cái)?shù)
            self.p, self.q = randomPrime(int(secparam/2),True), randomPrime(int(secparam/2),True) 
            # pp,qq對(duì)應(yīng)p',q'
            self.pp = (self.p -1)/2
            self.qq = (self.q - 1)/2
            # N = pq
            self.N = self.p * self.q
            # 下面這段while邏輯害晦,是要找一個(gè)好的N
            while True: 
                if bitsize(self.N) ==secparam and len(int2Bytes(self.N)) == int(secparam/8) and int2Bytes(self.N)[0] &128 !=0:
                    break
                self.p, self.q = randomPrime(int(secparam/2),True), randomPrime(int(secparam/2),True) 
                self.pp = (self.p -1)/2
                self.qq = (self.q - 1)/2
                self.N = self.p * self.q
            self.N2 = self.N**2
            self.g = random(self.N2)
            one = integer(1)% self.N2
            # 下面這段邏輯特铝,是找到一個(gè)好的g
            while True: 
                # 經(jīng)過(guò)下面兩步,保證g足夠隨機(jī)
                self.g = random(self.N2)
                self.g = integer((int(self.g)-1)*(int(self.g)-1))% self.N2
                # 保證g的階不是0
                if self.g == one:
                    continue
                # 保證g的階不是p
                tmp = self.g**self.p %self.N2
                if tmp == one:
                    continue
                # 保證g的階不是p'
                tmp = self.g**self.pp % self.N2
                if tmp == one:
                    continue
                # 保證g的階不是q
                tmp = self.g**self.q %self.N2
                if tmp == one:
                    continue
                # 保證g的階不是q'
                tmp = self.g**self.qq %self.N2
                if tmp == one:
                    continue
                # 保證g的階不是pp'
                tmp =self.g**(self.p*self.pp) % self.N2
                if tmp == one:
                    continue 
                # 保證g的階不是pq
                tmp = self.g**(self.p*self.q) %self. N2
                if tmp== one:
                    continue 
                # 保證g的階不是pq'
                tmp = self.g**(self.p*self.qq) % self.N2
                if tmp == one:
                    continue 
                # 保證g的階不是p'q
                tmp = self.g**(self.pp*self.q) % self.N2
                if tmp == one:
                    continue 
                # 保證g的階不是p'q'
                tmp = self.g**(self.pp*self.qq) % self.N2
                if tmp == one:
                    continue 
                # 保證g的階不是qq'
                tmp = self.g**(self.q*self.qq) % self.N2
                if tmp == one:
                    continue
                # 保證g的階不是pp'q
                tmp = self.g**(self.p*self.pp*self.q) % self.N2
                if tmp == one:
                    continue   
                # 保證g的階不是pp'q'
                tmp =self.g**(self.p*self.pp*self.qq) % self.N2
                if tmp == one:
                    continue
                # 保證g的階不是pqq'
                tmp =self.g**(self.p*self.q*self.qq) % self.N2
                if tmp == one:
                    continue
                # 保證g的階不是p'qq'
                tmp =self.g**(self.pp*self.q*self.qq) % self.N2
                if tmp == one:
                    continue  
                break 
            # 求得k
            self.k = integer((int(self.g**(self.pp*self.qq)) - 1)) / self.N % self.N
            # 獲得主密鑰
            self.MK ={"pp":self.pp,"qq":self.qq}
  • 第二步:生成密鑰
    根據(jù)公共參數(shù)PP = (N,k,g)生成密鑰壹瘟。
    隨機(jī)選擇\alpha \in Z_{N^2}鲫剿,計(jì)算h = g^\alpha mod N^2,公鑰pk = h私鑰sk = \alpha稻轨。
    相關(guān)代碼:
    def KeyGen(self):
        tmp = self.N2 /2
        sk = random(tmp) % self.N2
        pk = (self.g**sk) % self.N2
        return pk,sk
  • 第三步:加密
    明文m \in Z_N灵莲,隨機(jī)選取r \in Z_{N^2},利用公鑰pk = h加密得到密文(A,B)殴俱,其中A = g^r mod N { ^2}政冻,B = h^r(1+mN) mod N^2
    相關(guān)代碼:
def Encrypt(self,pk,plaintext):
        r = random(self.N/4) % self.N2
        A = (self.g** r ) % self.N2 
        B1 = (self.N*plaintext+1)% (self.N2)
        B2 = (pk**r) % (self.N2)
        B = B1*B2 % self.N2
        ciphertext = {"A":A,"B":B}
        return ciphertext
  • 第四步:解密
    當(dāng)一個(gè)明文m由公鑰pk = h加密得到密文(A,B)后,利用私鑰sk = \alpha進(jìn)行解密线欲,解密公式:m = \frac{(B/(A^\alpha)-1)mod N^2} {N}明场。
    相關(guān)代碼:
    def Decrypt(self,ciphertext,sk):
        t1 = integer(int(ciphertext['B']*((ciphertext['A']**-1)**sk)) -1) % self.N2
        m = integer(t1) / self.N
        return m
  • 第五步:利用主密鑰解密
    當(dāng)一個(gè)明文m由公鑰pk = h加密得到密文(A,B)后,還可以使用主密鑰MK = (p^{'},q^{'})進(jìn)行解密李丰。解密步驟如下:
    首先計(jì)算私鑰(\alpha) mod N = {(h^{p^{'}q^{'}}-1)mod N^2\over N}.k^{-1}mod N 苦锨,這里k^{-1}表示kN的逆。
    之后計(jì)算加密步驟中選取的隨機(jī)數(shù)(r )mod N = {(A^{p^{'}q^{'}}-1)mod N^{2}\over N}.k^{-1}mod N趴泌。
    \delta表示p^{'}q^{'}N的逆舟舒,并且令\gamma = (\alpha r)modN,這樣明文m = {((B/(g^{\gamma}))^{p^{'}q^{'}}-1)mod N^{2}\over N}\delta mod N
    相關(guān)代碼:
def DecryptMK(self,ciphertext,MK,pk):
        k_1 = self.k ** -1
        tmp = (int(pk**(MK['pp']*MK['qq'])) -1) % self.N2
        tmp = integer(tmp) /self.N 
        a = tmp * integer(k_1) % self.N
        
        tmp = (int(ciphertext['A'] **(MK['pp']*MK['qq'])) -1) % self.N2
        tmp = integer(tmp) /self.N 
        r = tmp * integer(k_1) % self.N
        
        gama = a*r %self.N
        sig = ((MK['pp']*MK['qq'])%self.N) **-1
        
        tmp = (self.g **-1)**gama
        tmp = ciphertext['B'] *tmp    
        tmp = (int(tmp**(MK['pp']*MK['qq'])) -1)% self.N2
        tmp = integer(tmp) /self.N
        
        m = integer(tmp) * integer(sig) %self.N
        return integer(m) 
  • 關(guān)于解密的正確性和算法的安全性嗜憔,可以閱讀開頭提到的論文秃励。
  • 完整代碼:
from charm.toolbox.integergroup import IntegerGroup
from charm.schemes.pkenc.pkenc_rsa import RSA_Enc, RSA_Sig
from charm.core.math.integer import integer,randomBits,random,randomPrime,isPrime,encode,decode,hashInt,bitsize,legendre,gcd,lcm,serialize,deserialize,int2Bytes,toInt

class Param():
    def __init__(self):
        pass
    def setParam(self,N2,N,g,k):
        self.N2 = N2
        self.N = N
        self.g = g
        self.k = k

class BCP():
    def __init__(self,secparam=1024,param = None):
        if param:
            self.N2 = param.N2
            self.N = param.N
            self.g = param.g
            self.k = param.k            
        else:
            self.p, self.q = randomPrime(int(secparam/2),True), randomPrime(int(secparam/2),True) 
            self.pp = (self.p -1)/2
            self.qq = (self.q - 1)/2
            self.N = self.p * self.q
            while True: 
                if bitsize(self.N) ==secparam and len(int2Bytes(self.N)) == int(secparam/8) and int2Bytes(self.N)[0] &128 !=0:
                    break
                self.p, self.q = randomPrime(int(secparam/2),True), randomPrime(int(secparam/2),True) 
                self.pp = (self.p -1)/2
                self.qq = (self.q - 1)/2
                self.N = self.p * self.q
            self.N2 = self.N**2
            self.g = random(self.N2)
            one = integer(1)% self.N2
            while True: #choose a good g
                self.g = random(self.N2)
                self.g = integer((int(self.g)-1)*(int(self.g)-1))% self.N2
                if self.g == one:
                    continue
                tmp = self.g**self.p %self.N2
                if tmp == one:
                    continue
                tmp = self.g**self.pp % self.N2
                if tmp == one:
                    continue
                tmp = self.g**self.q %self.N2
                if tmp == one:
                    continue
                tmp = self.g**self.qq %self.N2
                if tmp == one:
                    continue
                tmp =self.g**(self.p*self.pp) % self.N2
                if tmp == one:
                    continue 
                tmp = self.g**(self.p*self.q) %self. N2
                if tmp== one:
                    continue 
                tmp = self.g**(self.p*self.qq) % self.N2
                if tmp == one:
                    continue 
                tmp = self.g**(self.pp*self.q) % self.N2
                if tmp == one:
                    continue 
                tmp = self.g**(self.pp*self.qq) % self.N2
                if tmp == one:
                    continue 
                tmp = self.g**(self.q*self.qq) % self.N2
                if tmp == one:
                    continue
                tmp = self.g**(self.q*self.qq) % self.N2
                if tmp == one:
                    continue
                tmp = self.g**(self.p*self.pp*self.q) % self.N2
                if tmp == one:
                    continue   
                tmp =self.g**(self.p*self.pp*self.qq) % self.N2
                if tmp == one:
                    continue
                tmp =self.g**(self.p*self.q*self.qq) % self.N2
                if tmp == one:
                    continue
                tmp =self.g**(self.pp*self.q*self.qq) % self.N2
                if tmp == one:
                    continue  
                break 
            self.k = integer((int(self.g**(self.pp*self.qq)) - 1)) / self.N % self.N
            self.MK ={"pp":self.pp,"qq":self.qq}
    
    def GetMK(self):
        return self.MK
    
    def GetParam(self):
        param = Param()
        param.setParam(self.N2, self.N, self.g, self.k)
        return param
    
    def KeyGen(self):
        tmp = self.N2 /2
        sk = random(tmp) % self.N2
        pk = (self.g**sk) % self.N2
        return pk,sk
    
    def Encrypt(self,pk,plaintext):
        r = random(self.N/4) % self.N2
        A = (self.g** r ) % self.N2 
        B1 = (self.N*plaintext+1)% (self.N2)
        B2 = (pk**r) % (self.N2)
        B = B1*B2 % self.N2
        ciphertext = {"A":A,"B":B}
        return ciphertext
    
    def Decrypt(self,ciphertext,sk):
        t1 = integer(int(ciphertext['B']*((ciphertext['A']**-1)**sk)) -1) % self.N2
        m = integer(t1) / self.N
        return m
    
    def DecryptMK(self,ciphertext,MK,pk):
        k_1 = self.k ** -1
        tmp = (int(pk**(MK['pp']*MK['qq'])) -1) % self.N2
        tmp = integer(tmp) /self.N 
        a = tmp * integer(k_1) % self.N
        
        tmp = (int(ciphertext['A'] **(MK['pp']*MK['qq'])) -1) % self.N2
        tmp = integer(tmp) /self.N 
        r = tmp * integer(k_1) % self.N
        
        gama = a*r %self.N
        sig = ((MK['pp']*MK['qq'])%self.N) **-1
        
        tmp = (self.g **-1)**gama
        tmp = ciphertext['B'] *tmp    
        tmp = (int(tmp**(MK['pp']*MK['qq'])) -1)% self.N2
        tmp = integer(tmp) /self.N
        
        m = integer(tmp) * integer(sig) %self.N
        return integer(m) 

    def multiply(self,ciphertext1,ciphertext2):
        ciphertext={}
        ciphertext['A'] = ciphertext1['A'] * ciphertext2['A']
        ciphertext['B'] = ciphertext1['B'] * ciphertext2['B'] 
        return ciphertext

    def exponentiate(self,ciphertext,m):
        text={}    
        text['A'] = ciphertext['A'] **m % self.N2
        text['B'] = ciphertext['B'] **m % self.N2
        return text  

3.測(cè)試

  • 測(cè)試代碼
if __name__ == "__main__":

    bcp = BCP()
    mk = bcp.GetMK()
    print("mk is:",mk)
    pk,sk = bcp.KeyGen()
    print("------------------------")
    print("pk is:",pk,"sk is:",sk)
    plaintext = 1024
    print("------------------------")
    print("plaintext is:",plaintext)
    ciphertext = bcp.Encrypt(pk,plaintext)
    print("-------------------------")
    print("ciphertext is:",ciphertext)
    m1 = bcp.Decrypt(ciphertext,sk)
    print("-------------------------")
    print("Using sk to decrypt ciphertext,result is:",m1)
    m2 = bcp.DecryptMK(ciphertext,mk,pk)
    print("-------------------------")
    print("Using mk to decrypt ciphertext,result is:",m2)
  • 結(jié)果:滿足正常加解密運(yùn)算
mk is: {'pp': 8085308361220211021, 'qq': 8074162483544779991}
------------------------
pk is: 2327067015883561054197990332426819861947346756531764313703384468027403763054 mod 68188027578479421068471198257847466460478763055291934213409505489802965962361 sk is: 57367126269859549947589515646769721080756993424096049630783473449899601736315 mod 68188027578479421068471198257847466460478763055291934213409505489802965962361
------------------------
plaintext is: 1024
-------------------------
ciphertext is: {'A': 938193878176646758481378597525256135099055012583309087654629417103271925777 mod 68188027578479421068471198257847466460478763055291934213409505489802965962361, 'B': 65213284378251907450069755084784844288839906212981359092246031298436351553270 mod 68188027578479421068471198257847466460478763055291934213409505489802965962361}
-------------------------
Using sk to decrypt ciphertext,result is: 1024
-------------------------
Using mk to decrypt ciphertext,result is: 1024
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市痹筛,隨后出現(xiàn)的幾起案子莺治,更是在濱河造成了極大的恐慌,老刑警劉巖帚稠,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異床佳,居然都是意外死亡滋早,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門砌们,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)杆麸,“玉大人搁进,你說(shuō)我怎么就攤上這事∥敉罚” “怎么了饼问?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)揭斧。 經(jīng)常有香客問(wèn)我莱革,道長(zhǎng),這世上最難降的妖魔是什么讹开? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任盅视,我火速辦了婚禮,結(jié)果婚禮上旦万,老公的妹妹穿的比我還像新娘闹击。我一直安慰自己,他們只是感情好成艘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布赏半。 她就那樣靜靜地躺著,像睡著了一般淆两。 火紅的嫁衣襯著肌膚如雪除破。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天琼腔,我揣著相機(jī)與錄音瑰枫,去河邊找鬼。 笑死丹莲,一個(gè)胖子當(dāng)著我的面吹牛光坝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播甥材,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼盯另,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了洲赵?” 一聲冷哼從身側(cè)響起鸳惯,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎叠萍,沒(méi)想到半個(gè)月后芝发,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡苛谷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年辅鲸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腹殿。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡独悴,死狀恐怖例书,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情刻炒,我是刑警寧澤决采,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站坟奥,受9級(jí)特大地震影響树瞭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜筏勒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一移迫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧管行,春花似錦厨埋、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至迅涮,卻和暖如春废赞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叮姑。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工唉地, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人传透。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓耘沼,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親朱盐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子群嗤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容