You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何同一消息的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"

疑问:

  • 这是否是p384 crate的Bug?
  • 该问题是否与某SM2签名方法重复签名结果一致的issue相关?

回答

  • 这不是p384 crate的Bug。p384 crate的ECDSA实现默认采用确定性ECDSA(RFC6979),该算法通过固定的伪随机数生成方式推导签名所需的k值,因此同一密钥对同一消息签名时结果始终一致。而JavaScript的Crypto Subtle库默认使用随机化ECDSA,每次签名会生成新的随机k值,所以签名结果不同。两种实现均符合ECDSA规范,只是选择了不同的签名模式。

  • 该问题与提到的SM2签名问题无关。SM2是中国商用密码算法,与ECDSA属于不同的签名体系,那个issue是SM2实现未正确引入随机数导致签名重复,和p384的ECDSA默认使用确定性模式的情况完全不同。

  • 如果需要在p384 crate中实现每次签名结果不同的随机化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 17:12:02