AI 五子棋 第五步
恭喜你到達第五步!
我想你一定很艱難,前一步的問題需要大數運算,因為這個算法依賴於質因數分解的復雜度,只有數字相當大時才能保證這個算法難於破解。
這是服務器使用的公鑰:
- 65537,
135261828916791946705313569652794581721330948863485438876915508683244111694485850733278569559191167660149469895899348939039437830613284874764820878002628686548956779897196112828969255650312573935871059275664474562666268163936821302832645284397530568872432109324825205567091066297960733513602409443790146687029
`
你需要把你的密碼使用公鑰加密后交給服務器,當然在真正這么做之前,我們先做些准備:
- 驗證:將數字31415926使用公鑰加密后作為num字段提交到
http://2**.2**.**.1**:9012/step_05
如果沒有錯誤,我們進行下一步。我們這里加密的是數字,而密碼是個字符串,因此需要把字符串轉換為數字。轉換規則是這樣的:把所有字符轉換為ASCII,之后把字符串看作一個256進制的數,比如:
'abc'
'a' = 97, 'b' = 98, 'c' = 99
'abc' = 97*256^2 + 98*256^1 + 99*256^0 = 6382179
-
驗證:將字符串
hello, world!轉換為數字后作為str2num字段提交到http://2**.2**.**.1**:9012//step_05
如果沒有錯誤,我們進行下一步。轉換后的字符串可以加密了。 -
驗證:將字符串
hello, world!轉換為數字,使用公鑰加密后作為str字段提交到http://2**.2**.**.1**:9012/step_05
如果沒有錯誤,我們進行下一步。加密后的數字可以用16進制表示,這樣會短一點,我們秘鑰長度為1024字節,使用16進制,一位16進制數可以表示4個字節,生成的加密數據有256位長,可以節省一點網絡流量。 -
驗證:將字符串
hello, world!轉換為數字,使用公鑰加密后轉換為16進制,作為hex字段提交到http://2**.2**.**.1**:9012/step_05
如果沒有錯誤,我們進行下一步。你可以把你的密碼加密了。
任務 5
把你的用戶名作為user字段,加密后的密碼作為password字段,提交到.http://2**.2**.**.1**:9012/step_05
不要急着找下一步的地址。作為一個確定性的程序,你的密碼每次加密得到的結果都是一樣的,我們可以為密碼加噪,擾亂竊密者。在加密時,我們可以編碼的信息不大於公鑰中的第二個數字,這個數字很大,因此我們可以編碼很長的信息。
在c/c++中字符串以字符\0作為結束標志,\0以后的所有數據都會被忽略掉,因此我們可以在密碼后面添加一個字符\0,之后連接上一個隨機生成的沒用的字符串。這樣每次都會產生不同的密文,就能更有效的保護你的密碼了。去試試吧!
回到我們得到的信息,它也是被加密的,你需要解密,服務器使用它自己的私鑰加密了數據,因為秘鑰是對稱的,你可以用公鑰解密它。
待處理信息
無,提交用戶名密碼即可。
Python代碼實現
import requests as re
def fastModular(x): # 快速冪模實現 也就是加解密算法
"""x[0] = base """
"""x[1] = power"""
"""x[2] = modulus"""
result = 1
while(x[1] > 0):
if(x[1] & 1):
result = result * x[0] % x[2]
x[1] = int(x[1]/2) # 可用 x[1] >>= 2 位運算代替 更快
x[0] = x[0] * x[0] % x[2]
return result
def str_to_num(strings):
"""將字符看作數字,以256進制的進位做加法"""
"""返回一個十進制數字"""
sum = 0
lens = len(strings)
for i in range(0,lens):
sum += ord(strings[i])*256**(lens-i-1)
return sum
def num_to_str(num):
messageList = []
while num != 0 :
messageList.append(num%256)
num = num // 256 # 整數除符號 不能用 int(num // 256)
messageList.reverse() # 返回None 同時列表本身被倒置修改
decodeString = ''
for i in messageList:
decodeString += chr(i)
return decodeString
# 公鑰
power = 65537
modulus = 135261828916791946705313569652794581721330948863485438876915508683244111694485850733278569559191167660149469895899348939039437830613284874764820878002628686548956779897196112828969255650312573935871059275664474562666268163936821302832645284397530568872432109324825205567091066297960733513602409443790146687029
password = 'XXXXX'
myHex = hex(fastModular([str_to_num(password),power,modulus])) # 加密后的密碼
print(f"My encryption code is { myHex }")
param = {
'user' : '1414081721',
'password': myHex
}
getHtml = re.get('http://2**.2**.**.1**:9012/step_05/', params = param)
prefix = '0x' # 16進制前綴 后面int()用到
# 服務器返回的16進制字符串沒有前綴,加上前綴,將他轉化為數字,這個數字是經過加密的
# 將得到的數字用公鑰解密,得到字符串變為的數字,用逆方法解出
num = fastModular([int(prefix + getHtml.json()['message'],16),power,modulus])
print(num_to_str(num))
直接運行即可得到結果。
解題tips
通過 十進制轉 256進制的函數后,得到的結果只有 Hi 0191 t中間一堆空格。
用的是 num = int( num /256) 因為不加int會變成小數,這個方法將256進制的低位去除掉。可是得到一堆 0 的中間結果 可是還是能解出幾個字符。
一開始以為是密碼輸錯了,服務器回的信息是錯的。就拿QQ號注冊了一個賬號。這回是代碼的問題了。
那怎么改呢?這轉換函數可是一點錯都沒有 ,按照以前 C++的經驗。
如果有不一樣的地方
可能就在 int(num / 256) , 以前c++ 直接寫 num/256就可以得到。這個也不敢改,到晚上十點多,還是沒想出來。
想到程序員黃金法則,我就把它扔一遍去了。
第二天突然人來瘋,Python的進制轉換是怎么來的?
網上一搜,全是函數。根本沒有自己去實現的............
又搜 Python 十進制轉八進制 這是一個亮點。終於找到一個自己寫函數的大哥。
他寫的和c++差不多,但是 迭代的時候用的是 // 而不是那種int。
上網再搜 // 是整除 /是浮點除 立刻毛色頓開。
完善 10 -256 函數。得到答案~~~~~~
公鑰加密-密鑰交換算法
如何能在別人可以直接接觸我們的通信時,保護我們的傳遞的信息呢?類似郵遞員郵遞一張明信片。他們可以看到我們任何的通信內容。
實話說現在的網絡技術還真就是這樣,包括http傳輸,都是明擺着的信息,別人能看到為什么不看?所以現在基本都變成https加密形式了。
我們想到讓信息變成加密的形式。可這個加解密的一致性的協商過程也是可以被人看到的。
公鑰加密-密鑰交換算法就是通過公鑰私鑰來來進行協商
使看到整個協商過程的人,也不知道怎么去破解。
這篇文章講的使 運用冪和鍾算混合數字。這個機制是由 Martin Hellman and Whitfield Diffie於1976年首次發表的。只有協商雙方可以獲得到共享密鑰的數字。KEA
第一步:協商雙方各自單獨選擇一個私人數字。
第二步:對鍾的大小11 基數2達成一致 兩個公開數字。
第三步:通過使用冪符號和鍾算,雙方將各自的私人數字和公開數字混合,分別得到一個公開私人數字。public-private number PPN
- PPN = base ^ 私人數字 mod 鍾大小。
第四步: 將對方公開的PPN和自己的私人數字混合。可以得到共享密鑰。雙方得到的共享密鑰是一致的。
- 共享密鑰 = 其他人的PPN ^ 自己的私人數字 mod 鍾大小
可以建立一個等式
(base ^ A私人數字 mod 鍾大小) ^ B私人數字 mod 鍾大小 = (base ^ B私人數字 mod 鍾大小) ^ A私人數字 mod 鍾大小
利用模運算的引理
(base ^ A私人數字 ) ^ B私人數字 mod 鍾大小 = (base ^ B私人數字 ) ^ A私人數字 mod 鍾大小
這個方法要求鍾(模數)的大小使必須是一個素數,而基數必須是鍾(模數)大小的本源根。本原根是數學內的一種術語。
如果使得a^m≡1 mod n成立的最小正冪m滿足m=φ(n),則稱a是n的本原根。 其中φ(n)為歐拉函數
加油吧少年,根據這個博客你也可以寫出一個相對智能的五子棋程序,甚至更強的AI算法!
文章會隨時改動,注意到博客里去看。一些網站會爬取本文章,但是可能會有出入。
https://www.cnblogs.com/asmurmur/
