为何同一消息的ECDSA P-384签名未生成不同结果?
ECDSA P-384签名一致性问题咨询
问题描述
ECDSA P-384规范要求,对同一消息多次签名时每次生成的签名需不同。JavaScript的Crypto Subtle库可满足该特性,但使用Rust的p384 crate对同一消息签名时,每次结果均一致。
测试代码如下(密钥和消息为测试用假数据):
use p384::{elliptic_curve::SecretKey, ecdsa::{SigningKey, Signature, signature::Signer}}; fn main() { let jwk_string = format!(r#"{{"kty": "EC","crv": "P-384","x": "w6CbiBS2SKN4Y4hov7BqJbq3ZVZeEks_3PEUFEb281nYHWZYpCd8BrC1M7CfJYi4","y": "yYugPHaZMaOo_BKWaIMDDXDauZD9tKGGg444dY-tJ-0lmsZI3jJ99fw5LlXkCJMp","d": "zO5NR7o0T5jJc3aqr2mwHVjQeDAfzMovpc3-9uYeKxBAfZ4_07G0_jQDaiuyKNPM"}}"#); let key = match SecretKey::from_jwk_str(&jwk_string) { Ok(p) => SigningKey::from(p), Err(e) => { println!("Not valid secret key: {}",e); return; }, }; let to_sign = "public.xvRyWGu5CjC-ufl18xIQFUHN9XlLQoXFkU8RCLfRMooi63ftX-nAHXt5tuNP_ZYC.wjRSUmKCVY_yL8ZjpoHWCOaB7NOc2yEt-VtrsqQJHGrYr1uCT_m6Qbrg84BCkHMF"; let to_sign_bytes = to_sign.as_bytes(); println!("to_sign_bytes: {:?}",to_sign_bytes); let signature : Signature = key.sign(to_sign_bytes); println!("Signature: {:x?}",hex::encode(signature.to_bytes())); }
输出结果:
to_sign_bytes: [112, 117, 98, 108, 105, 99, 46, 120, 118, 82, 121, 87, 71, 117, 53, 67, 106, 67, 45, 117, 102, 108, 49, 56, 120, 73, 81, 70, 85, 72, 78, 57, 88, 108, 76, 81, 111, 88, 70, 107, 85, 56, 82, 67, 76, 102, 82, 77, 111, 111, 105, 54, 51, 102, 116, 88, 45, 110, 65, 72, 88, 116, 53, 116, 117, 78, 80, 95, 90, 89, 67, 46, 119, 106, 82, 83, 85, 109, 75, 67, 86, 89, 95, 121, 76, 56, 90, 106, 112, 111, 72, 87, 67, 79, 97, 66, 55, 78, 79, 99, 50, 121, 69, 116, 45, 86, 116, 114, 115, 113, 81, 74, 72, 71, 114, 89, 114, 49, 117, 67, 84, 95, 109, 54, 81, 98, 114, 103, 56, 52, 66, 67, 107, 72, 77, 70] Signature: "f4ff418ceece0b6d276e75938ea825ada59885249da48d492ed671af0f77f53b8e9ddf2c9401f848239279acf717b4f7ca158721d2c9a80078312ae02e76e239fafb3bd117099aee37d41a239ac6b305cfed009ab8b75641e44a227752077bd7"
疑问:
- 这是否是
p384crate的Bug? - 该问题是否与某SM2签名方法重复签名结果一致的issue相关?
回答
这不是
p384crate的Bug。p384crate的ECDSA实现默认采用确定性ECDSA(RFC6979),该算法通过固定的伪随机数生成方式推导签名所需的k值,因此同一密钥对同一消息签名时结果始终一致。而JavaScript的Crypto Subtle库默认使用随机化ECDSA,每次签名会生成新的随机k值,所以签名结果不同。两种实现均符合ECDSA规范,只是选择了不同的签名模式。该问题与提到的SM2签名问题无关。SM2是中国商用密码算法,与ECDSA属于不同的签名体系,那个issue是SM2实现未正确引入随机数导致签名重复,和p384的ECDSA默认使用确定性模式的情况完全不同。
如果需要在
p384crate中实现每次签名结果不同的随机化ECDSA,可使用SigningKey::sign_with_rng方法,传入一个安全的随机数生成器(如rand_core::OsRng)。修改后的示例代码如下:
use p384::{elliptic_curve::SecretKey, ecdsa::{SigningKey, Signature, signature::Signer}}; use rand_core::OsRng; // 需要在Cargo.toml中添加rand_core依赖 fn main() { let jwk_string = format!(r#"{{"kty": "EC","crv": "P-384","x": "w6CbiBS2SKN4Y4hov7BqJbq3ZVZeEks_3PEUFEb281nYHWZYpCd8BrC1M7CfJYi4","y": "yYugPHaZMaOo_BKWaIMDDXDauZD9tKGGg444dY-tJ-0lmsZI3jJ99fw5LlXkCJMp","d": "zO5NR7o0T5jJc3aqr2mwHVjQeDAfzMovpc3-9uYeKxBAfZ4_07G0_jQDaiuyKNPM"}}"#); let key = match SecretKey::from_jwk_str(&jwk_string) { Ok(p) => SigningKey::from(p), Err(e) => { println!("Not valid secret key: {}",e); return; }, }; let to_sign = "public.xvRyWGu5CjC-ufl18xIQFUHN9XlLQoXFkU8RCLfRMooi63ftX-nAHXt5tuNP_ZYC.wjRSUmKCVY_yL8ZjpoHWCOaB7NOc2yEt-VtrsqQJHGrYr1uCT_m6Qbrg84BCkHMF"; let to_sign_bytes = to_sign.as_bytes(); println!("to_sign_bytes: {:?}",to_sign_bytes); // 使用随机数生成器进行签名 let signature : Signature = key.sign_with_rng(&mut OsRng, to_sign_bytes); println!("Signature: {:x?}",hex::encode(signature.to_bytes())); }
注意:需要在Cargo.toml中添加rand_core依赖,例如:
[dependencies] p384 = { version = "0.12", features = ["ecdsa", "jwk"] } hex = "0.4" rand_core = { version = "0.6", features = ["std"] }
内容的提问来源于stack exchange,提问作者sudoExclamationExclamation
相关产品推荐
相关产品推荐

