使用Rust P256 crate从十六进制私钥获取ECDSA签名密钥遇阻
问题
我正在用Rust编写一个小型区块链模拟程序,需要获取ECDSA签名密钥来对添加到区块链的交易进行签名,但遇到了问题。
我在crypto.rs文件中定义了如下密钥对结构体:
pub struct KeyPair { pub name: String, pub public_key: String, private_key: String, }
并实现了密钥生成逻辑:
impl KeyPair { pub fn generate(name: String, key_store: &mut KeyStore) -> KeyPair { // 先生成私钥 let secret = SecretKey::random(&mut OsRng); let private_key = hex::encode(&secret.to_bytes()); // 再生成公钥 let public_key = hex::encode(secret.public_key().to_sec1_bytes()); let key_pair: KeyPair = KeyPair { name, public_key, private_key, }; // 存储副本并返回原对象供后续操作 key_store.append_key_pair(key_pair.clone()); key_store.write(); key_pair } }
测试时在key_store.json中生成了如下内容:
{ "keys": [ { "name": "TEST", "public_key": "040eda3b0d1b30a78cc830ab9e69dc25a52d5448c3c8228511481a67b1f6420bc55beb4efc297a49a657cfe08b7873293e78fef553724453f55eea3656a6369d5a", "private_key": "ccb789f12826fd81b45f27489b73ac5650f46f9b8f0281922f459be515f018b6" } ] }
现在我需要从私钥(SecretKey)获取签名密钥来对交易哈希签名,但尝试的方法均失败。
第一次尝试的函数触发signature error panic:
pub fn get_signature(&self, hash: &String) -> String { let private_key = self.get_private_key(); let signing_key = match private_key.parse::<SigningKey>() { Ok(key) => key, Err(e) => panic!("Error parsing signing key from private key: {}", e), }; let signature: Signature = signing_key.sign(&hash.as_bytes()); signature.to_string() }
我参考了相关文章,该实现使用pem,但我选择将字节串转为十六进制存储,这是常见的密钥存储方式。
第二次尝试触发Error parsing bytes to String: invalid utf-8 sequence of 1 bytes from index 0 panic:
pub fn get_signature(&self, hash: &String) -> String { let private_key: String = self.get_private_key(); let decoded: Vec<u8> = match hex::decode(&private_key) { Ok(bytes) => bytes, Err(e) => panic!("Error decoding bytes: {}", e), }; let str_from_bytes: String = match String::from_utf8(decoded) { Ok(s) => s, Err(e) => panic!("Error parsing bytes to String: {}", e), }; let signing_key = match str_from_bytes.parse::<SigningKey>() { Ok(key) => key, Err(e) => panic!("Error parsing signing key from private key: {}", e), }; let signature: Signature = signing_key.sign(&hash.as_bytes()); signature.to_string() }
请问如何将转为十六进制存储的SecretKey还原为签名密钥?
解决方案
核心问题是错误地将十六进制解码后的私钥字节转成了UTF-8字符串,再尝试解析为SigningKey——私钥字节本身不是合法的UTF-8序列,而且SigningKey本身支持直接从字节数组构建,不需要转成字符串。
正确的实现步骤:
- 将存储的十六进制私钥字符串解码为字节数组
- 直接用这个字节数组实例化
SecretKey,再转换为SigningKey(取决于你使用的ECDSA库,比如k256或ecdsacrate) - 用
SigningKey对交易哈希签名并转为字符串
假设你使用的是k256和ecdsa crate,修正后的代码如下:
use k256::SecretKey; use ecdsa::SigningKey; use hex; pub fn get_signature(&self, hash: &String) -> String { let private_key_hex = self.get_private_key(); // 解码十六进制字符串为原始私钥字节 let private_key_bytes = hex::decode(private_key_hex) .expect("Failed to decode private key hex"); // 从字节数组恢复SecretKey,再转为SigningKey let secret_key = SecretKey::from_slice(&private_key_bytes) .expect("Failed to recover SecretKey from bytes"); let signing_key = SigningKey::from(secret_key); // 对哈希签名并转为字符串返回 let signature = signing_key.sign(hash.as_bytes()); signature.to_string() }
关键说明:
- 私钥的十六进制编码是字节的直接表示,解码后得到的是原始私钥字节,不需要转成UTF-8字符串——这是第二次尝试失败的根本原因。
SecretKey::from_slice方法可以直接从原始字节数组恢复密钥,和生成时调用secret.to_bytes()的操作完全对应。- 如果你的
SigningKey来自ecdsacrate,它可以通过From<SecretKey>trait直接从SecretKey转换,无需额外解析。
无论使用的ECDSA库版本如何,核心逻辑一致:直接使用十六进制解码后的字节数组初始化签名密钥,不要经过UTF-8字符串转换。
内容的提问来源于stack exchange,提问作者PumpkinBreath
相关产品推荐
相关产品推荐

