目錄
- 非對稱加密實戰:Python實現數字簽名
- 引言:數字世界的身份驗證
- 1. 非對稱加密基礎
- 1.1 核心概念
- 1.2 非對稱加密算法比較
- 2. 數字簽名原理
- 2.1 數字簽名工作流程
- 2.2 數字簽名的核心特性
- 3. RSA數字簽名實現
- 3.1 RSA算法數學基礎
- 3.1.1 密鑰生成
- 3.1.2 簽名生成
- 3.1.3 簽名驗證
- 4. Python實現數字簽名系統
- 4.1 完整代碼實現
- 5. 代碼解析與輸出
- 5.1 測試輸出示例
- 5.2 關鍵組件解析
- 6. 數字簽名在區塊鏈中的應用
- 6.1 區塊鏈交易簽名流程
- 6.2 關鍵應用場景
- 7. 安全實踐與最佳方案
- 7.1 安全注意事項
- 7.2 區塊鏈簽名最佳實踐
- 結論:構建可信的數字世界
非對稱加密實戰:Python實現數字簽名
引言:數字世界的身份驗證
在數字通信中,數字簽名技術解決了兩個核心問題:身份認證和數據完整性。通過非對稱加密算法,數字簽名允許接收方驗證消息來源的真實性并確認消息在傳輸過程中未被篡改。本文將深入探討數字簽名的原理,并使用Python實現完整的數字簽名流程,包括密鑰生成、簽名創建和簽名驗證。
1. 非對稱加密基礎
1.1 核心概念
非對稱加密使用一對數學上相關的密鑰:
- 公鑰(Public Key):可公開分享,用于加密或驗證簽名
- 私鑰(Private Key):嚴格保密,用于解密或創建簽名
數學關系表示為:
加密:? C = E ( P K , M ) 解密:? M = D ( S K , C ) 簽名:? S = Sign ( S K , M ) 驗證:?Verify ( P K , M , S ) = { True 簽名有效 False 簽名無效 \text{加密: } C = E(PK, M) \\ \text{解密: } M = D(SK, C) \\ \text{簽名: } S = \text{Sign}(SK, M) \\ \text{驗證: } \text{Verify}(PK, M, S) = \begin{cases} \text{True} & \text{簽名有效} \\ \text{False} & \text{簽名無效} \end{cases} 加密:?C=E(PK,M)解密:?M=D(SK,C)簽名:?S=Sign(SK,M)驗證:?Verify(PK,M,S)={TrueFalse?簽名有效簽名無效?
1.2 非對稱加密算法比較
算法 | 密鑰長度 | 安全性 | 應用場景 | 特點 |
---|---|---|---|---|
RSA | 2048-4096位 | ★★★★ | 數字簽名、密鑰交換 | 數學基礎簡單,應用廣泛 |
ECDSA | 256-521位 | ★★★★★ | 加密貨幣、物聯網 | 密鑰短,計算效率高 |
EdDSA | 256-512位 | ★★★★★ | 高安全系統 | 快速安全,抗側信道攻擊 |
DSA | 1024-3072位 | ★★★ | 傳統系統 | NIST標準,正被淘汰 |
2. 數字簽名原理
2.1 數字簽名工作流程
2.2 數字簽名的核心特性
- 不可否認性:簽名者無法否認其簽名
- 數據完整性:任何修改都會使簽名失效
- 身份認證:證明消息來自特定發送方
- 時效性:可添加時間戳防止重放攻擊
3. RSA數字簽名實現
3.1 RSA算法數學基礎
3.1.1 密鑰生成
- 選擇兩個大質數 p p p 和 q q q
- 計算模數 n = p × q n = p \times q n=p×q
- 計算歐拉函數 ? ( n ) = ( p ? 1 ) ( q ? 1 ) \phi(n) = (p-1)(q-1) ?(n)=(p?1)(q?1)
- 選擇公鑰指數 e e e 滿足 1 < e < ? ( n ) 1 < e < \phi(n) 1<e<?(n) 且 gcd ? ( e , ? ( n ) ) = 1 \gcd(e, \phi(n)) = 1 gcd(e,?(n))=1
- 計算私鑰指數 d d d 滿足 d × e ≡ 1 ( m o d ? ( n ) ) d \times e \equiv 1 \pmod{\phi(n)} d×e≡1(mod?(n))
公鑰: ( e , n ) (e, n) (e,n)
私鑰: ( d , n ) (d, n) (d,n)
3.1.2 簽名生成
對于消息 M M M:
- 計算哈希值 H = Hash ( M ) H = \text{Hash}(M) H=Hash(M)
- 簽名 S = H d m o d n S = H^d \mod n S=Hdmodn
3.1.3 簽名驗證
- 計算哈希值 H = Hash ( M ) H = \text{Hash}(M) H=Hash(M)
- 計算 H ′ = S e m o d n H' = S^e \mod n H′=Semodn
- 驗證 H ≡ H ′ H \equiv H' H≡H′
4. Python實現數字簽名系統
4.1 完整代碼實現
import hashlib
import os
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from Crypto.Cipher import PKCS1_OAEP
from base64 import b64encode, b64decode
import json
import timeclass DigitalSignatureSystem:"""數字簽名系統實現類功能:- RSA密鑰對生成- 消息簽名- 簽名驗證- 密鑰管理- 數字證書生成"""def __init__(self, key_size=2048):"""初始化數字簽名系統參數:key_size: RSA密鑰長度 (默認2048位)"""self.key_size = key_sizeself.private_key = Noneself.public_key = Noneself.certificate = Nonedef generate_keys(self):"""生成RSA密鑰對步驟:1. 生成隨機密鑰對2. 導出公鑰和私鑰"""# 生成密鑰對key = RSA.generate(self.key_size)# 導出私鑰 (PEM格式)self.private_key = key.export_key()# 導出公鑰 (PEM格式)self.public_key = key.publickey().export_key()return self.private_key, self.public_keydef save_keys(self, private_key_path="private_key.pem", public_key_path="public_key.pem"):"""保存密鑰到文件參數:private_key_path: 私鑰文件路徑public_key_path: 公鑰文件路徑"""if not self.private_key or not self.public_key:raise ValueError("請先生成密鑰對")with open(private_key_path, "wb") as priv_file:priv_file.write(self.private_key)with open(public_key_path, "wb") as pub_file:pub_file.write(self.public_key)print(f"私鑰已保存至: {private_key_path}")print(f"公鑰已保存至: {public_key_path}")def load_keys(self, private_key_path="private_key.pem", public_key_path="public_key.pem"):"""從文件加載密鑰參數:private_key_path: 私鑰文件路徑public_key_path: 公鑰文件路徑"""with open(private_key_path, "rb") as priv_file:self.private_key = priv_file.read()with open(public_key_path, "rb") as pub_file:self.public_key = pub_file.read()print("密鑰已成功加載")def sign_message(self, message: bytes) -> bytes:"""對消息進行數字簽名參數:message: 原始消息 (字節串)返回:數字簽名 (字節串)"""if not self.private_key:raise ValueError("未加載私鑰")# 創建消息的哈希值message_hash = SHA256.new(message)# 加載私鑰rsa_key = RSA.import_key(self.private_key)# 使用PKCS#1 v1.5標準進行簽名signer = pkcs1_15.new(rsa_key)signature = signer.sign(message_hash)return signaturedef verify_signature(self, message: bytes, signature: bytes) -> bool:"""驗證數字簽名參數:message: 原始消息 (字節串)signature: 數字簽名 (字節串)返回:bool: 簽名是否有效"""if not self.public_key:raise ValueError("未加載公鑰")# 創建消息的哈希值message_hash = SHA256.new(message)# 加載公鑰rsa_key = RSA.import_key(self.public_key)# 驗證簽名verifier = pkcs1_15.new(rsa_key)try:verifier.verify(message_hash, signature)return Trueexcept (ValueError, TypeError):return Falsedef create_certificate(self, issuer: str, subject: str, validity_days: int = 365):"""創建簡易數字證書參數:issuer: 證書頒發者subject: 證書主體validity_days: 有效期 (天)返回:證書字典 (可序列化為JSON)"""if not self.public_key:raise ValueError("未加載公鑰")# 證書基本信息cert = {"version": "1.0","issuer": issuer,"subject": subject,"public_key": self.public_key.decode('utf-8'),"algorithm": "RSA-SHA256","issued_date": time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()),"expiry_date": time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(time.time() + validity_days * 24 * 3600))}# 對證書信息進行簽名cert_info = json.dumps(cert, sort_keys=True).encode('utf-8')cert_signature = self.sign_message(cert_info)# 添加簽名到證書cert["signature"] = b64encode(cert_signature).decode('utf-8')self.certificate = certreturn certdef verify_certificate(self, certificate: dict) -> bool:"""驗證數字證書參數:certificate: 證書字典返回:bool: 證書是否有效"""# 提取簽名和證書內容signature = b64decode(certificate["signature"])cert_copy = certificate.copy()cert_copy.pop("signature")cert_info = json.dumps(cert_copy, sort_keys=True).encode('utf-8')# 加載證書中的公鑰public_key = RSA.import_key(certificate["public_key"].encode('utf-8'))# 驗證簽名message_hash = SHA256.new(cert_info)verifier = pkcs1_15.new(public_key)try:verifier.verify(message_hash, signature)# 檢查有效期expiry_date = time.mktime(time.strptime(certificate["expiry_date"], "%Y-%m-%d %H:%M:%S"))if time.time() > expiry_date:print("證書已過期")return Falsereturn Trueexcept (ValueError, TypeError):return Falsedef encrypt_with_public_key(self, message: bytes) -> bytes:"""使用公鑰加密消息參數:message: 原始消息 (字節串)返回:加密后的消息 (字節串)"""if not self.public_key:raise ValueError("未加載公鑰")rsa_key = RSA.import_key(self.public_key)cipher = PKCS1_OAEP.new(rsa_key)return cipher.encrypt(message)def decrypt_with_private_key(self, ciphertext: bytes) -> bytes:"""使用私鑰解密消息參數:ciphertext: 加密消息 (字節串)返回:解密后的原始消息 (字節串)"""if not self.private_key:raise ValueError("未加載私鑰")rsa_key = RSA.import_key(self.private_key)cipher = PKCS1_OAEP.new(rsa_key)return cipher.decrypt(ciphertext)# 測試函數
def test_digital_signature():"""數字簽名系統測試函數"""# 初始化系統dss = DigitalSignatureSystem()print("=" * 70)print("數字簽名系統測試")print("=" * 70)# 生成密鑰print("\n1. 生成RSA密鑰對 (2048位)...")private_key, public_key = dss.generate_keys()print(" 私鑰長度:", len(private_key), "字節")print(" 公鑰長度:", len(public_key), "字節")# 保存密鑰dss.save_keys()# 加載密鑰print("\n2. 從文件加載密鑰...")dss.load_keys()# 測試消息messages = [b"Hello, Blockchain World!",b"Digital signatures provide authenticity and integrity",b"RSA algorithm is widely used in public-key cryptography",b"Long message test: " + os.urandom(1024) # 1KB隨機數據]# 測試簽名和驗證print("\n3. 測試簽名和驗證:")print("-" * 70)for i, msg in enumerate(messages):# 創建簽名signature = dss.sign_message(msg)# 驗證簽名 (應成功)valid = dss.verify_signature(msg, signature)# 篡改消息后驗證 (應失敗)tampered_msg = msg + b"tampered"invalid = dss.verify_signature(tampered_msg, signature)# 篡改簽名后驗證 (應失敗)tampered_sig = bytearray(signature)if len(tampered_sig) > 10:tampered_sig[10] = (tampered_sig[10] + 1) % 256invalid_sig = dss.verify_signature(msg, bytes(tampered_sig))# 顯示結果print(f"消息 {i+1}:")print(f" 原始消息: {msg[:50]}{'...' if len(msg)>50 else ''}")print(f" 簽名長度: {len(signature)} 字節")print(f" 驗證結果: {'有效' if valid else '無效'}")print(f" 篡改消息驗證: {'有效' if invalid else '無效'} (應無效)")print(f" 篡改簽名驗證: {'有效' if invalid_sig else '無效'} (應無效)")print("-" * 70)# 測試加密解密print("\n4. 測試非對稱加密:")print("-" * 70)test_message = b"Secret message for encryption test"print(" 原始消息:", test_message.decode('utf-8'))# 加密ciphertext = dss.encrypt_with_public_key(test_message)print(" 加密結果:", b64encode(ciphertext).decode('utf-8')[:50] + "...")# 解密decrypted = dss.decrypt_with_private_key(ciphertext)print(" 解密結果:", decrypted.decode('utf-8'))print(" 加解密成功:", decrypted == test_message)print("-" * 70)# 測試數字證書print("\n5. 測試數字證書:")print("-" * 70)issuer = "Blockchain Certification Authority"subject = "example@blockchain.com"# 創建證書certificate = dss.create_certificate(issuer, subject)print(" 證書內容:")print(json.dumps(certificate, indent=2))# 驗證證書cert_valid = dss.verify_certificate(certificate)print("\n 證書驗證結果:", "有效" if cert_valid else "無效")# 篡改證書測試print("\n6. 篡改證書測試:")tampered_cert = certificate.copy()tampered_cert["subject"] = "hacker@evil.com"print(" 篡改后的主體:", tampered_cert["subject"])# 驗證篡改后的證書tampered_valid = dss.verify_certificate(tampered_cert)print(" 篡改證書驗證:", "有效" if tampered_valid else "無效 (應無效)")# 過期證書測試print("\n7. 過期證書測試:")expired_cert = certificate.copy()expired_cert["expiry_date"] = "2000-01-01 00:00:00" # 設置為過期expired_valid = dss.verify_certificate(expired_cert)print(" 過期證書驗證:", "有效" if expired_valid else "無效 (應無效)")# 性能測試
def performance_test():"""數字簽名性能測試"""import timeitdss = DigitalSignatureSystem()dss.generate_keys()# 測試數據message = b"Performance test message" * 100 # 約2KB# 簽名性能sign_time = timeit.timeit(lambda: dss.sign_message(message), number=100)# 驗證性能signature = dss.sign_message(message)verify_time = timeit.timeit(lambda: dss.verify_signature(message, signature), number=100)print("\n性能測試結果 (100次操作):")print("=" * 50)print(f"簽名平均時間: {sign_time/100*1000:.2f} 毫秒/次")print(f"驗證平均時間: {verify_time/100*1000:.2f} 毫秒/次")if __name__ == "__main__":# 運行測試test_digital_signature()# 運行性能測試performance_test()# 示例用法print("\n示例用法:")dss = DigitalSignatureSystem()dss.generate_keys()# 創建消息message = b"Important contract for blockchain project"# 簽名signature = dss.sign_message(message)print(f"消息: {message.decode('utf-8')}")print(f"簽名: {b64encode(signature).decode('utf-8')[:50]}...")# 驗證valid = dss.verify_signature(message, signature)print(f"簽名驗證: {'成功' if valid else '失敗'}")# 創建證書print("\n創建數字證書:")certificate = dss.create_certificate("Blockchain Inc.", "developer@blockchain.com")print(json.dumps(certificate, indent=2))
5. 代碼解析與輸出
5.1 測試輸出示例
======================================================================
數字簽名系統測試
======================================================================1. 生成RSA密鑰對 (2048位)...私鑰長度: 1216 字節公鑰長度: 392 字節
私鑰已保存至: private_key.pem
公鑰已保存至: public_key.pem2. 從文件加載密鑰...
密鑰已成功加載3. 測試簽名和驗證:
----------------------------------------------------------------------
消息 1:原始消息: b'Hello, Blockchain World!'簽名長度: 256 字節驗證結果: 有效篡改消息驗證: 無效 (應無效)篡改簽名驗證: 無效 (應無效)
----------------------------------------------------------------------
消息 2:原始消息: b'Digital signatures provide authenticity and integrity'簽名長度: 256 字節驗證結果: 有效篡改消息驗證: 無效 (應無效)篡改簽名驗證: 無效 (應無效)
----------------------------------------------------------------------
消息 3:原始消息: b'RSA algorithm is widely used in public-key cryptogra...'簽名長度: 256 字節驗證結果: 有效篡改消息驗證: 無效 (應無效)篡改簽名驗證: 無效 (應無效)
----------------------------------------------------------------------
消息 4:原始消息: b'Long message test: \xf5\xf1\xc8\xc4\xd8\xd4\xa7\xc5\xd5\xc3\xd1\xd8\xd3\xd9\xd1\xd2\xd0\xd0\xd1\xd8\xd7\xd5\xd4\xd3\xd2\xd1\xd0\xcf\xce\xcd\xcc\xcb\xca\xc9\xc8\xc7\xc6\xc5\xc4\xc3\xc2\xc1\xc0\xbf\xbe\xbd\xbc\xbb...'簽名長度: 256 字節驗證結果: 有效篡改消息驗證: 無效 (應無效)篡改簽名驗證: 無效 (應無效)
----------------------------------------------------------------------4. 測試非對稱加密:
----------------------------------------------------------------------原始消息: Secret message for encryption test加密結果: b'X7c2KdT9zUw...'解密結果: Secret message for encryption test加解密成功: True
----------------------------------------------------------------------5. 測試數字證書:
----------------------------------------------------------------------證書內容:
{"version": "1.0","issuer": "Blockchain Certification Authority","subject": "example@blockchain.com","public_key": "-----BEGIN PUBLIC KEY-----\nMIIB...\n-----END PUBLIC KEY-----","algorithm": "RSA-SHA256","issued_date": "2023-10-15 08:30:45","expiry_date": "2024-10-14 08:30:45","signature": "dGhpcyBpcyBqdXN0IGFuIGV4YW1wbGUgc2lnbmF0dXJl"
}證書驗證結果: 有效6. 篡改證書測試:篡改后的主體: hacker@evil.com篡改證書驗證: 無效 (應無效)7. 過期證書測試:過期證書驗證: 無效 (應無效)性能測試結果 (100次操作):
==================================================
簽名平均時間: 15.32 毫秒/次
驗證平均時間: 0.87 毫秒/次示例用法:
消息: Important contract for blockchain project
簽名: MEUCIQDe6Yw6y5zLf3J9J7K4x8Zz1aXb6cYtGp0q3sRv7AiEAiM0KZ4r0...
簽名驗證: 成功創建數字證書:
{"version": "1.0","issuer": "Blockchain Inc.","subject": "developer@blockchain.com","public_key": "-----BEGIN PUBLIC KEY-----\nMIIB...\n-----END PUBLIC KEY-----","algorithm": "RSA-SHA256","issued_date": "2023-10-15 08:31:22","expiry_date": "2024-10-14 08:31:22","signature": "dGhpcyBpcyBhbm90aGVyIGV4YW1wbGUgc2lnbmF0dXJl"
}
5.2 關鍵組件解析
-
密鑰管理:
- 密鑰生成:使用
RSA.generate()
創建密鑰對 - 密鑰存儲:保存為PEM格式文件
- 密鑰加載:從文件系統讀取密鑰
- 密鑰生成:使用
-
簽名流程:
- 哈希計算:使用SHA-256計算消息摘要
- 簽名創建:使用PKCS#1 v1.5標準
- 簽名驗證:檢查簽名與消息哈希的匹配性
-
數字證書:
- 證書結構:包含頒發者、主體、公鑰等信息
- 證書簽名:對證書內容進行簽名
- 證書驗證:檢查簽名和有效期
-
加密解密:
- 公鑰加密:使用OAEP填充方案
- 私鑰解密:恢復原始消息
6. 數字簽名在區塊鏈中的應用
6.1 區塊鏈交易簽名流程
6.2 關鍵應用場景
-
交易驗證:
- 比特幣交易使用ECDSA簽名
- 以太坊交易使用ECDSA或更高效的EdDSA
-
智能合約調用:
function transfer(address to, uint amount) external {require(verifySignature(msg.sender, to, amount), "Invalid signature");// 執行轉賬 }
-
身份認證:
- 使用DID(Decentralized Identifiers)實現去中心化身份
- 結合零知識證明保護隱私
-
跨鏈通信:
- 通過多重簽名驗證跨鏈交易
- 使用門限簽名提高安全性
7. 安全實踐與最佳方案
7.1 安全注意事項
-
私鑰保護:
- 使用硬件安全模塊(HSM)
- 避免在代碼中硬編碼私鑰
- 實施密鑰輪換策略
-
算法選擇:
- 優先選擇ECDSA或EdDSA
- RSA密鑰長度至少2048位
- 棄用SHA-1等不安全哈希算法
-
側信道攻擊防護:
- 恒定時間算法實現
- 防止時序攻擊和故障攻擊
7.2 區塊鏈簽名最佳實踐
# 安全的區塊鏈交易簽名示例
from web3 import Web3
from eth_account import Accountdef sign_blockchain_transaction(private_key: str, to: str, value: int):"""安全地簽名區塊鏈交易參數:private_key: 十六進制格式私鑰to: 接收方地址value: 轉賬金額 (wei)返回:簽名后的交易"""# 創建交易字典transaction = {'to': to,'value': value,'gas': 21000,'gasPrice': Web3.to_wei('50', 'gwei'),'nonce': 0, # 實際應用中需要從鏈上獲取'chainId': 1 # 主網}# 簽名交易 (使用eth_account庫)signed_tx = Account.sign_transaction(transaction, private_key)return signed_tx.rawTransaction# 使用環境變量管理私鑰
import os
private_key = os.getenv("BLOCKCHAIN_PRIVATE_KEY")if not private_key:raise ValueError("未設置私鑰環境變量")signed_tx = sign_blockchain_transaction(private_key, "0x...", 10**18)
結論:構建可信的數字世界
數字簽名技術作為區塊鏈安全的基石,實現了去中心化系統中的身份驗證和交易完整性保障。通過本文的深入探討和Python實現,我們掌握了:
- 非對稱加密的數學原理
- 數字簽名的工作流程和標準
- RSA算法的完整實現
- 數字證書的創建和驗證
- 區塊鏈中的實際應用場景
- 安全最佳實踐
隨著量子計算的發展,傳統數字簽名算法面臨新的挑戰。后量子密碼學研究正在推進抗量子簽名算法(如基于格的Dilithium算法)的標準化進程。然而在當前階段,RSA和ECDSA仍是區塊鏈領域的主流選擇。
在數字世界中,數字簽名如同親筆簽名——它確認了你的身份,證明了你的意圖,并確保你的承諾不可篡改。掌握這項技術,是構建可信數字生態系統的關鍵一步。