作者:虛壞叔叔
早餐店不會開到晚上,想吃的人早就來了!😄
如何實作一個App應用程式,限制使用者時間使用?
一、場景說明
假設有這樣一個場景,你接了一個私活,幫别人做一個軟體,軟體沒有聯網功能。東西做好以後,客戶還沒有給錢,說要先試用一下。你選擇了相信客戶,把軟體發送給了他。然後他就把你拉黑了。
為了避免這種情況發生,你首先想到的辦法,肯定是把過期時間寫死到代碼裡面,時間到了 App 自動銷毀。對方付錢以後,你再把這個寫死的時間延長或者去掉。再重新編譯後發給客戶。
但問題是,每次重新編譯代碼并發給使用者是非常麻煩的事情,有沒有更簡單的辦法呢?能不能軟體始終是一個軟體,但是給使用者一個注冊碼,這個注冊碼裡面标記了有效時間。等到過期以後,隻需要給使用者一個新的注冊碼,就可以繼續使用了。
看到這裡,有同學肯定會想,怎麼在注冊碼指定有效期呢?首先這個時間肯定不能是明文的,否則使用者把它一改,豈不是就可以自行延長了。
但如果加密的話,就必須把解密算法放到軟體裡面,一旦使用者對程式進行初步的反編譯,就能拿到解密算法或者對稱加密的密鑰。
是以,我們隻能使用非對稱加密。而非對稱加密裡面,通過公鑰加密,使用私鑰解密。如果我們要讓軟體從注冊碼裡面解碼出有效時間,難道要在軟體裡面放私鑰?
私鑰不能洩露,是以放到軟體裡面的隻能是公鑰。但是難道能使用私鑰加密,用公鑰解密?
實際上,真的可以這樣做,但這不叫做私鑰加密公鑰解密,這叫做私鑰簽名(sign),公鑰驗證(verify)。并且,使用這個方法有一個好處,就是有效時間可以直接明文存放,不怕使用者修改。因為一旦修改了,簽名就比對不上。
二、PyCryptodome庫安裝
假設我們有一個字元串
message
,使用私鑰,可以對這個字元串進行簽名,獲得一個簽名字元串
signature
。而我們用公鑰,可以驗證
message
是否能夠生成簽名字元串
signature
。如果
message
發生了修改,或者
signature
發生了修改,或者
message
和
signature
同時發生了修改,公鑰驗證都會失敗。
各個語言都有非對稱加密相關的第三方庫。我們用Python中的
PyCryptodome
來進行示範。
首先,我們在macOS下面,生成一對公鑰和私鑰:
ssh-keygen -t rsa
根據提示輸入密鑰的儲存路徑就可以了,如下圖所示:
在目前檔案夾,生成了私鑰
sign
和公鑰
sign.pub
。
接下來,使用
pip
安裝
PyCryptodome
:
pip install pycryptodome
三、實戰示範限制使用者使用的時間
接下來,導入公鑰和私鑰:
>>> from Crypto.PublicKey import RSA
>>> with open('sign') as f:
... private = f.read()
...
>>> with open('sign.pub') as f:
... public = f.read()
...
>>> private_key = RSA.import_key(private)
>>> public_key = RSA.import_key(public)
由于我們之前生成密鑰使用的是
SHA256
算法,是以我們需要用
SHA256
算法對需要簽名的資料生成摘要。這一步在簽名和驗證簽名的時候都需要做。
>>> from Crypto.Hash import SHA256
>>> digest = SHA256.new()
>>> message = 'expire: 2022-03-01'
>>> digest.update(message.encode())
接下來,對這個資料進行簽名:
>>> import base64
>>> from Crypto.Signature import PKCS1_v1_5
>>> signer = PKCS1_v1_5.new(private_key)
>>> code = signer.sign(digest)
>>> signature = base64.b64encode(code)
>>> print(signature.decode())
運作效果如下圖所示:
現在,你隻需要把字元串
expire: 2022-03-01
和簽名字元串
xbelbTNpq8M...很長一串...
發送給客戶就可以了。
客戶把過期時間的字元串和簽名字元串輸入到軟體以後,軟體使用公鑰來驗證這個字元串是不是由自己對應的私鑰簽名的:
>>> message = 'expire: 2022-03-01'
>>> signature = 'xbelbTNpq8MCFkSxGBoTq7SwQ+oqHRAObrj5p8K2gyY+7uWs5dXGjsQ+GP2XTS5YskCtGjYIBZmAmeM5ey69lRQyk5S1m7t68pYNbUvf3o39Ym0rcmK7XGkBh3euZzVeRErs4JCl7ffTbfcqM4aAsWldDKESrZvaDNQ5DkC8VRYHPBfZfScHqPw/zcHCMRhC9Dch8j9eQlnk8/UKY0MM92jXT4map94PzZRfMLkD4vsciZTtMJm4a42UiiWDUpA6zIgQCYru2YyKspS1uZFE51atYP5DcgPWvJUVRDJS/ZjdPfi9chRjx0dS/Df1sFEreZ7myzXAJP7Y8FA6rvi7EZLlHZ1ViM9tTJp9ut/ZlKgnPAuDCp1JSyKMUk/doVqzUjTqTNHuORe+p3Hhb+xkCASyD8eUH+CyEDVLRcDkSMH5U3o/uONnOQao2o9dbkGiSYNkToElQJ2v20S3MnncPciij8H7iI2dDp1dwt8bkcZOD+E1Tf88LMvRaxB7YnhJ'
>>> digest = SHA256.new()
>>> digest.update(message.encode())
>>> reader = PKCS1_v1_5.new(public_key)
>>> reader.verify(digest, base64.b64decode(signature.encode()))
True
但如果你篡改了
message
的内容,那麼驗證就會失敗,如下圖所示:
軟體第一次驗證通過以後,就可以把這個過期時間的字元串和簽名字元串一起用檔案的形式存到硬碟上,每次啟動軟體的時候都檢查一遍。
發現合法并且沒有過期就正常運作,發現過期了或者不合法就就重新彈出輸入注冊碼的對話框。
四、windows 生成RSA公鑰和私鑰
生成
RSA
公鑰和私鑰步驟:
4.1 打開 openssl.exe 執行如下指令
4.2 執行
genrsa -out d:\openssl_rsa_private.pem 1024
4.3 執行
rsa -in d:\openssl_rsa_private.pem -pubout -out d:\openssl_rsa_public.pem
4.4 d盤檢視生成的檔案,用文本打開
4.5 完整代碼截圖
from Crypto.PublicKey import RSA
with open('openssl_rsa_private.pem') as f:
private = f.read()
with open('openssl_rsa_public.pem') as f:
public = f.read()
private_key = RSA.import_key(private)
public_key = RSA.import_key(public)
from Crypto.Hash import SHA256
digest = SHA256.new()
message = 'expire: 2022-09-01'
digest.update(message.encode())
import base64
from Crypto.Signature import PKCS1_v1_5
signer = PKCS1_v1_5.new(private_key)
code = signer.sign(digest)
signature = base64.b64encode(code)
print(signature)
print(signature.decode())
str = signature.decode()
#message = 'expire: 2022-03-01'
#signature = 'xbelbTNpq8MCFkSxGBoTq7SwQ+oqHRAObrj5p8K2gyY+7uWs5dXGjsQ+GP2XTS5YskCtGjYIBZmAmeM5ey69lRQyk5S1m7t68pYNbUvf3o39Ym0rcmK7XGkBh3euZzVeRErs4JCl7ffTbfcqM4aAsWldDKESrZvaDNQ5DkC8VRYHPBfZfScHqPw/zcHCMRhC9Dch8j9eQlnk8/UKY0MM92jXT4map94PzZRfMLkD4vsciZTtMJm4a42UiiWDUpA6zIgQCYru2YyKspS1uZFE51atYP5DcgPWvJUVRDJS/ZjdPfi9chRjx0dS/Df1sFEreZ7myzXAJP7Y8FA6rvi7EZLlHZ1ViM9tTJp9ut/ZlKgnPAuDCp1JSyKMUk/doVqzUjTqTNHuORe+p3Hhb+xkCASyD8eUH+CyEDVLRcDkSMH5U3o/uONnOQao2o9dbkGiSYNkToElQJ2v20S3MnncPciij8H7iI2dDp1dwt8bkcZOD+E1Tf88LMvRaxB7YnhJ'
digest = SHA256.new()
digest.update(message.encode())
reader123 = PKCS1_v1_5.new(public_key)
isVer = reader123.verify(digest, base64.b64decode(str.encode()))
print(isVer)
五、總結
- 本文完成MFC程式隻運作單個執行個體 的簡單示例。