用python3实现MD5withRSA数字签名的验证
在某次安全研究中。需要手工驗證下某重要文件MD5withRSA的數字簽名是否正確。于是寫了個腳本。現記錄下來,供大家學習討論。
1. 數字簽名基礎知識
數字簽名:
一旦選擇足夠安全的散列算法, 那么就能夠使用其實現數字簽名系統。數字簽名基礎結構具有兩個明顯的目標:
1.數字化的簽名消息可以向接收方保證: 消息確實來自己聲明的發送者, 并且實施了不可否認性(也就是說, 排除了發送者之后聲稱消息是偽造的情況)。
2.數字化的簽名消息可以向接收方保證: 消息在發送方和接收方之間進行傳輸的過程中不會被改變。這種方法確保消息不會受到惡意的修改(第三方想要修改消息的含義)以及無意識的修改(由通信過程中的故障造成,如電磁干擾) 。
數字簽名算法的基礎是我的一篇博客中講到的兩個重要概念: 公鑰密碼學和散列函數。
CISSP考試要求里的“應用密碼學”內容輔助記憶趣味串講_曉翔仔的博客-CSDN博客_cissp密碼學
(公鑰密碼學=非對稱密碼學)
如果Alice 想要數字化簽名一條發送給Bob 的消息,那么她會執行下列動作:
(1) Alice 使用一種足夠安全的散列算法(如SHA-512)生成原始明文消息的消息摘要。
(2) 然后, A1ice 使用她的私鑰只對消息摘要進行加密。加密的消息摘要便是數字簽名。
(3) Alice 將簽名的消息摘要添加到明文消息中。
(4) Alice 將完成添加的消息傳送給Bob。
當Bob 接收到數字化簽名的消息時,他會逆向完成如下過程:(這就是簽名驗證需要做的!)
(1) Bob 使用Alice 的公鑰解密數字簽名。
(2) Bob 使用相同的散列函數, 生成從Alice 那里接收到的完整明文消息的消息摘要。
(3) 然后, Bob 將從Alice那里接收到的已解密的消息摘要與自己計算得到的消息摘要進行比較。
如果兩個消息摘要匹配, 那么Bob 就能夠確認接收到的消息是由Alice 發送的。如果這兩個消息摘要不匹配, 那么這條消息有可能不是Alice 發送的, 也有可能在傳輸過程中被修改了。
需要注意的是,數字簽名過程本身并不提供任何隱私保護。數字簽名只是確保滿足加密目標中的完整性和不可否認性。然而,如果Alice 想保證發送給Bob 的消息的隱私性,那么她就要在消息生成的過程中增加額外的步驟。在將己簽名的消息摘要添加到明文消息中以后,Alice可以用Bob的公鑰加密整條消息。當Bob接收到消息時,他會用自己的私鑰在上述所列的步驟之前對消息進行解密。
數字簽名不僅僅用于消息,軟件供應商經常使用數字簽名技術對從互聯網上下載的編碼分發(例如,applet和軟件補丁)進行身份認證。
2. 簽名文件的產生
2.1 生成一對公私鑰
RSA密鑰對的生成方式很多,這里為了方便,利用在線工具生成了一對公私鑰。
公鑰:
-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCr0erlUoNR/7WNp3RxIBM3UQVE/e7kbS8rKeybytjxPyNKta9/nlMWm3Yt9t8DQ0Tc5D18Uj1pmBGpplYCVKSxHF5VEzCK7+gMCJnieeZ/WLjGHqZJjIOwg3y5H52012jvQ5EcE7JIXRTOzIj7+2+J50JmDBmSUB4Vq+vAtdeA+wIDAQAB -----END PUBLIC KEY-----私鑰:
-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQCr0erlUoNR/7WNp3RxIBM3UQVE/e7kbS8rKeybytjxPyNKta9/ nlMWm3Yt9t8DQ0Tc5D18Uj1pmBGpplYCVKSxHF5VEzCK7+gMCJnieeZ/WLjGHqZJ jIOwg3y5H52012jvQ5EcE7JIXRTOzIj7+2+J50JmDBmSUB4Vq+vAtdeA+wIDAQAB AoGAAzjbi9NhTQ9biz2BrIFqetYVIDcW3MfDY67UlhNWwCdr9QG1OdJxQnJF5BTG F9ySFQZetM8iAbHtWt4YrNCDihrNNN8jhAK3l/QmqUjDmJuf34KRMWm1v0v3LE6C 5NOTreq5RDw2xEvPu9SDprhues9fVz0FCNagasI4l3UMM3ECQQDuCSjJOsjg9d8V YUwNKlB7renL4laeO+qCwgkmuApX5nw3cPZAr6XlJDuy6oV0x16gdZfISU0BghL4 IdmArf1ZAkEAuMl43wWOxKphfqdP7lJ/besrzU6uNlGP56KtHy61cJ1s7Dexs46B b89SRG2sCq1TgledPUynsepAOdxM7tQCcwJBAKZivKG8d4HYG7GkKzbgpxVswYoE kitVEl2IKEFqT8CVmapfSaJ5pOqA40Hy8IpqediLmickJbGyLHRJpPFvrTECQEMR xVBVaRNSsB5TRfy/bscLo5NaKntGsU+myOVXg72DNiSqtlHwuGRHwJlboSUi8fuO J2E3cGpwFq2pmzbYNqcCQQCp6HwOLqsZ3GKPgX45txlESpHqeb4bNKm4G7M/b3nw C0wdkZ3UHtbnc04m++atZB7re3eiQ/sYdMcN0RXXDBqv -----END RSA PRIVATE KEY-----2.2 用私鑰對重要文件的哈希做加密
新建文件,寫了重要內容。計算文件的MD5是
2da853603413c8823df8454ed579cd64用上一步生成的私鑰對MD5信息進行加密,加密結果用BASE64編碼顯示,是:
PXhWLNkyGRwBiuONnaeDXFv4HD6kvCIFO61jpjC9gX61qGfplMPrbcBn+co2eImNO10gAaXMXB1rxWnkxTETU9otaUO0I/SorqWbqiE4eOu1vlmuUZ0rXlxHGkLzfzJov25iFc8Lgt/qGbwuDL2p+yvjPNljhK2K3idOzO2v4I8=用一個單獨文件存放密文。這個就是該重要文件的數字簽名。
3. 簽名驗證
3.1 利用RSA公鑰對signature做解密。
熟悉RSA算法的你一定知道:RSA解密計算的時候,需要密文,模和指數。這密文就是數字簽名signature,模(m)和指數(e)都存在與RSA公鑰文件里面。
RSA公鑰文件的主體部分是:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCr0erlUoNR/7WNp3RxIBM3UQVE/e7kbS8rKeybytjxPyNKta9/nlMWm3Yt9t8DQ0Tc5D18Uj1pmBGpplYCVKSxHF5VEzCK7+gMCJnieeZ/WLjGHqZJjIOwg3y5H52012jvQ5EcE7JIXRTOzIj7+2+J50JmDBmSUB4Vq+vAtdeA+wIDAQABRSA公鑰文件的主體部分做BASE64解碼后是:
30819f300d06092a864886f70d010101050003818d0030818902818100abd1eae5528351ffb58da77471201337510544fdeee46d2f2b29ec9bcad8f13f234ab5af7f9e53169b762df6df034344dce43d7c523d699811a9a6560254a4b11c5e5513308aefe80c0899e279e67f58b8c61ea6498c83b0837cb91f9db4d768ef43911c13b2485d14cecc88fbfb6f89e742660c1992501e15abebc0b5d780fb0203010001
褐色部分是模(m),紫色部分是指數(e)。
這里需要注意的是RSA公鑰文件是由不同格式的。一種是“BEGIN RSA PUBLIC KEY”,另一種是“BEGIN PUBLIC KEY”。兩種格式下,模(m)在公鑰文件中所處的位置不一樣(注意看python代碼“# 找到模數和指數的開頭結束位置”),指數(e)則永遠都在公鑰文件的結尾。
如何在兩種公鑰格式之間進行轉換,一種是“BEGIN RSA PUBLIC KEY”,另一種是“BEGIN PUBLIC KEY” - 碼客
根據上述分析,編寫python3代碼,用RSA公鑰對signature做解密
import rsa import base64class DecryptByPublicKey:"""先產生模數因子然后生成rsa公鑰再使用rsa公鑰去解密傳入的加密str"""def __init__(self, encrypt_text, pub_key):missing_padding = 4 - len(encrypt_text) % 4if missing_padding:encrypt_text += '=' * missing_paddingself._encrypt_text = encrypt_text.replace(" ", "+")self._pub_string_key = pub_key# 使用公鑰字符串求出模數和因子self._modulus = None # 模數self._exponent = None # 因子# 使用PublicKey(模數,因子)算出公鑰self._pub_rsa_key = Nonedef _gen_modulus_exponent(self, s):# 對字符串解碼, 解碼成功返回 模數和指數b_str = base64.b64decode(s)# print("len(b_str):", len(b_str))# if len(b_str) < 162:# print("_gen_modulus_exponent return false")# # return Falsehex_str = b_str.hex()# print("hex_str is: ", hex_str)# 找到模數和指數的開頭結束位置 BEGIN PUBLIC KEY格式的開頭位置if pub_key_type == 'BEGIN PUBLIC KEY':m_start = 29 * 2e_start = 159 * 2m_len = 128 * 2e_len = 3 * 2elif pub_key_type == 'BEGIN RSA PUBLIC KEY':m_start = 7 * 2e_start = 137 * 2m_len = 128 * 2e_len = 3 * 2else:print("pubkey type not recognized")self._modulus = int(hex_str[m_start:m_start + m_len], 16)self._exponent = int(hex_str[e_start:e_start + e_len], 16)# print("exponent is:", hex(self._exponent))# print("modulus is:", hex(self._modulus))def _gen_rsa_pubkey(self):# 將pub key string 轉換為 pub rsa keytry:rsa_pubkey = rsa.PublicKey(self._modulus, self._exponent)# 賦值到_pub_rsa_keyself._pub_rsa_key = rsa_pubkey.save_pkcs1()except Exception as e:raise edef decode(self):"""decrypt msg by public key"""public_key = rsa.PublicKey.load_pkcs1(self._pub_rsa_key)b64decoded_encrypt_text = base64.b64decode(self._encrypt_text)length = len(b64decoded_encrypt_text)# print("b64decoded_encrypt_text is: ", b64decoded_encrypt_text)# print("length of b64decoded_encrypt_text is: ", length)length_max = 128decryptDataText = []data = b''if length >= 128:for i in range(0, length, length_max):encrypted = rsa.transform.bytes2int(b64decoded_encrypt_text[i:i + length_max])print("encrypted data(signature):", hex(encrypted))print("public_key.e:", public_key.e)print("public_key.n:", public_key.n)decrypted = rsa.core.decrypt_int(encrypted, public_key.e, public_key.n)decrypted_bytes = rsa.transform.int2bytes(decrypted)# print("decrypted_bytes: ",decrypted_bytes)print("decrypted data(hex):", hex(decrypted))else:encrypted = rsa.transform.bytes2int(b64decoded_encrypt_text)decrypted = rsa.core.decrypt_int(encrypted, public_key.e, public_key.n)decrypted_bytes = rsa.transform.int2bytes(decrypted)try:raw_info = decrypted_bytes[decrypted_bytes.find(b'\x00') + 1:]except Exception as e:p.error(e)raise eprint("decrypt sign by public key is: ", raw_info.decode("utf-8"))return 'TRUE'def decrypt(self):"""先產生模數因子然后生成rsa公鑰再使用rsa公鑰去解密"""self._gen_modulus_exponent(self._pub_string_key)self._gen_rsa_pubkey()ret = self.decode()print("ret is:", ret)return retif __name__ == "__main__":# 在線生成pub_key_type = 'BEGIN PUBLIC KEY'pub_key = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCr0erlUoNR/7WNp3RxIBM3UQVE/e7kbS8rKeybytjxPyNKta9/nlMWm3Yt9t8DQ0Tc5D18Uj1pmBGpplYCVKSxHF5VEzCK7+gMCJnieeZ/WLjGHqZJjIOwg3y5H52012jvQ5EcE7JIXRTOzIj7+2+J50JmDBmSUB4Vq+vAtdeA+wIDAQAB'encrypt_text = 'PXhWLNkyGRwBiuONnaeDXFv4HD6kvCIFO61jpjC9gX61qGfplMPrbcBn+co2eImNO10gAaXMXB1rxWnkxTETU9otaUO0I/SorqWbqiE4eOu1vlmuUZ0rXlxHGkLzfzJov25iFc8Lgt/qGbwuDL2p+yvjPNljhK2K3idOzO2v4I8=' # encrypt_text 是被私鑰加密后的密文result = DecryptByPublicKey(encrypt_text, pub_key).decrypt()print(result)備注:借鑒了這篇博客,特感謝
【Python3】RSA公鑰解密_Parkour1990的博客-CSDN博客_python rsa 公鑰解密
計算輸出是:
decrypt sign by public key is: 2da853603413c8823df8454ed579cd643.2 哈希對比
對重要文件計算MD5,并與上一步python3解密計算的輸出對比,發現是一致的,因此我們可以說本次數字證書驗證通過。
4.總結
????????使用python進行RSA公鑰加密私鑰解密,網上方法很多。但是當你需要驗證某數字簽名的時候,發現很多博客的代碼都無法直接拿過來用,我的python3簽名驗證代碼親測通過,希望我的這篇博客能夠幫助到你。
總結
以上是生活随笔為你收集整理的用python3实现MD5withRSA数字签名的验证的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 魔兽修改默认服务器,魔兽怎么设置默认服务
- 下一篇: Object Removal by Ex