React与Python跨环境ECDSA签名验证失败问题排查
跨环境ECDSA签名验证问题:React(elliptic库)与Python(cryptography库)不兼容
问题背景
开发项目时遇到异常:React应用通过elliptic库生成ECDSA签名,Python后端用cryptography库验证失败,但各自环境内单独执行签名与验证均正常。
React签名生成代码
import { useEffect } from 'react'; import { ec as EC } from 'elliptic'; const App = () => { useEffect(() => { const ec = new EC('secp256k1'); const private_key_hex = "cf63f7ffe346cd800e431b34bdbd45f6aac3c2ac6055ac18195753aff9b9cce8"; const message_hash_hex = "2f8fc7172db7dcbd71ec70c83263db33a54ff761b02a54480a8d07b9c633d651"; const privateKey = ec.keyFromPrivate(private_key_hex, 'hex'); const signature = privateKey.sign(message_hash_hex, 'hex', { canonical: true }); const signature_der_hex = signature.toDER('hex'); console.log("Signature (DER hex):", signature_der_hex); }, []); return <div><h1>ECDSA Signature in React</h1></div>; }; export default App;
生成的签名:
3045022100e5c678f346cdd180815912e580c27d9d70a4a2e71ab6cfb2bdaedfbf4cdf24cc02200bba6ae9b3bb25c886b5cc8549ac6796438f295e91320a1d705f17e25cb7199b
Python验证代码
from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives import hashes from cryptography.hazmat.backends import default_backend def verify_signature(public_key_hex, signature_der_hex, message_hash_hex): public_key_bytes = bytes.fromhex(public_key_hex) try: public_key = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256K1(), public_key_bytes) except Exception as e: print(f"Error loading public key: {e}") return False hashed_message = bytes.fromhex(message_hash_hex) try: signature_bytes = bytes.fromhex(signature_der_hex) public_key.verify(signature_bytes, hashed_message, ec.ECDSA(hashes.SHA256())) return True except Exception as e: print(f"Verification failed: {e}") return False public_key_hex = "03aa4fcbf0792ce77a3be9415bf96bff99a832466f56ecba3bb8ce630877de2701" # Replace with signature from React signature_der_hex = "3045022100e5c678f346cdd180815912e580c27d9d70a4a2e71ab6cfb2bdaedfbf4cdf24cc02200bba6ae9b3bb25c886b5cc8549ac6796438f295e91320a1d705f17e25cb7199b" message_hash_hex = "2f8fc7172db7dcbd71ec70c83263db33a54ff761b02a54480a8d07b9c633d651" verification_result = verify_signature(public_key_hex, signature_der_hex, message_hash_hex) print("Verification:", verification_result)
已尝试操作
- 确认两端消息哈希完全一致
- React端将签名转为DER格式后发送
- 对齐React与Python的公钥格式
问题原因与解决方案
核心问题
差异出在签名输入的处理逻辑:
- elliptic库的
sign方法传入'hex'格式哈希时,直接将hex解码为字节后签名,不会再次哈希。 - Python cryptography库默认会对传入的
hashed_message再次执行SHA256哈希,导致验证时使用的哈希值与签名时不一致。
修复方案
修改Python验证代码,启用预哈希验证模式,告诉库传入的消息已经是哈希后的结果:
# 替换原verify调用部分 public_key.verify( signature_bytes, hashed_message, ec.ECDSA(hashes.SHA256(), prehashed=True) # 关键参数:prehashed=True )
完整修复后的Python代码:
from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives import hashes def verify_signature(public_key_hex, signature_der_hex, message_hash_hex): public_key_bytes = bytes.fromhex(public_key_hex) try: public_key = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256K1(), public_key_bytes) except Exception as e: print(f"Error loading public key: {e}") return False hashed_message = bytes.fromhex(message_hash_hex) try: signature_bytes = bytes.fromhex(signature_der_hex) # 启用预哈希验证,避免重复哈希 public_key.verify(signature_bytes, hashed_message, ec.ECDSA(hashes.SHA256(), prehashed=True)) return True except Exception as e: print(f"Verification failed: {e}") return False public_key_hex = "03aa4fcbf0792ce77a3be9415bf96bff99a832466f56ecba3bb8ce630877de2701" signature_der_hex = "3045022100e5c678f346cdd180815912e580c27d9d70a4a2e71ab6cfb2bdaedfbf4cdf24cc02200bba6ae9b3bb25c886b5cc8549ac6796438f295e91320a1d705f17e25cb7199b" message_hash_hex = "2f8fc7172db7dcbd71ec70c83263db33a54ff761b02a54480a8d07b9c633d651" verification_result = verify_signature(public_key_hex, signature_der_hex, message_hash_hex) print("Verification:", verification_result)
内容的提问来源于stack exchange,提问作者Muppet
相关产品推荐
相关产品推荐

