【密码学】一文读懂基于Elgamal的数字签名

简介: 本文来先填一下之前挖的坑,简单介绍一个实际的数字签名的方案,给自己挖的坑总是要自己来填的。

一文读懂基于Elgaml的数字签名


6JQ5NHU1M)MD@WQYI)2L9]C.jpg基于Elgamal的数字签名方案

本文来先填一下之前挖的坑,简单介绍一个实际的数字签名的方案,给自己挖的坑总是要自己来填的。


知识回顾

在介绍具体的签名算法之前,首先来回顾一下之前讲过的离散对数和数论相关的知识,对于素数p, 如果是p的原根,那么对于取模p之后的值是不同的,然后,我们可以得到下面两个结论。

  • 对于任意的整数m,  , 当且仅当
  • 对于任意整数i, j,, 当且仅当


密钥选择

如果读者看过我之前聊过的基于elgamal的加密,理解后面这些内容相对来说会轻松不少,不过要是没看过也没有关系,我尽量也是把每个细节聊清楚。

基于Elgamal的数字签名,全局元素是p和  , 其中  是p的原根,然后根据下面的方法生成公私钥对。

  • 生成随机整数 , 使得
  • 计算
  • A的私钥是, A的公钥是


签名过程

如果用户A需要对消息M进行签名,则按照如下的步骤进行处理

  • 计算消息M的哈希值m = H(M),其中 m 在
  • 随机选择一个整数K, 使  并且 , 也就是K和p-1互素
  • 计算
  • 计算, 这里可不是普通的指数运算,而是求逆运算这也就是前面要求互素的原因, 因为不互素是没有逆的。
  • 计算
  • 最终生成的签名(, )


签名验证过程

对于任意的用户, 都能按照如下的方式对签名进行验证

image.png

如果, 说明签名有效。

接下来,简单证明一下,为什么上面, 就可以说明签名是有效的吧, 假设这个等式成立,则有。

image.png

代码实现

好久没给出具体的实现了,这篇文章回归一下,来用rust简单的实现一下这个数字签名算法。

use std::ops::{Mul, Sub};
use num_bigint::{BigInt, RandBigInt, ToBigInt};
use num_traits::{zero, one};
pub struct Elgamal {}
pub struct PublicKey {
    alpha: BigInt,
    p: BigInt,
    y: BigInt,
}
pub struct PrivateKey {
    alpha: BigInt,
    p: BigInt,
    x: BigInt,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ElGamalSign {
    s1: BigInt,
    s2: BigInt,
}
pub fn mod_inv(u: &BigInt, v: &BigInt) -> BigInt {
    let mut q: BigInt;
    let mut t1: BigInt;
    let mut t3: BigInt;
    let (mut u1, mut u3, mut v1, mut v3) =
        (BigInt::from(1u8), u.clone(), BigInt::from(0u8), v.clone());
    let mut iter: i8 = 1;
    while &v3 != &zero() {
        q = &u3 / &v3;
        t3 = &u3 % &v3;
        t1 = &u1 + &q * &v1;
        u1 = v1;
        v1 = t1;
        u3 = v3;
        v3 = t3;
        iter = -iter;
    }
    if &u3 != &one() {
        return zero();
    }
    if iter < 0 {
        return v - u1;
    }
    u1
}
impl Elgamal {
    fn sign(m: &BigInt, sk: &PrivateKey) -> ElGamalSign {
        let mut rng = rand::thread_rng();
        let mut k = rng.gen_bigint_range(&BigInt::from(1u8), &(&sk.p - BigInt::from(1u8)));
        let p_sub_one = &sk.clone().p.clone().sub(1.to_bigint().unwrap());
        let mut key_inv = mod_inv(&k, p_sub_one);
        while key_inv.eq(&zero()) {
            k = rng.gen_bigint_range(&BigInt::from(1u8), &(&sk.p - BigInt::from(1u8)));
            key_inv = mod_inv(&k, p_sub_one);
        }
        let s1 = sk.alpha.modpow(&k, &sk.p) % &sk.p;
        let s2 = key_inv.mul(m.sub(&sk.clone().x.clone().mul(&s1))) % &sk.clone().p.clone().sub(1.to_bigint().unwrap()) % (&sk.clone().p.clone().sub(1.to_bigint().unwrap()));
        ElGamalSign {
            s1: s1.clone(),
            s2,
        }
    }
    fn verify(m: &BigInt, s1: &BigInt, s2: &BigInt, pk: &PublicKey) -> bool {
        let v1 = pk.alpha.modpow(&m, &pk.p);
        let v2 = pk.y.modpow(&s1, &pk.p).mul(s1.modpow(&s2, &pk.p)) % &pk.p;
        v1.eq(&v2)
    }
}
#[cfg(test)]
mod test {
    use num_bigint::BigInt;
    use crate::elgamal_sign::{PublicKey, PrivateKey, Elgamal};
    #[test]
    fn test() {
        let p = BigInt::from(19u8);
        let alpha = BigInt::from(10u8);
        let x = BigInt::from(5u8);
        let y = alpha.modpow(&x, &p);
        let pk = PublicKey {
            p: p.clone(),
            alpha: alpha.clone(),
            y,
        };
        let sk = PrivateKey {
            p: p.clone(),
            alpha: alpha.clone(),
            x,
        };
        let m = BigInt::from(17u8);
        let sign = Elgamal::sign(&m, &sk);
        println!("{:?}", sign);
        let result = Elgamal::verify(&m, &sign.s1, &sign.s2, &pk);
        println!("{:?}", result);
    }
}


相关文章
|
4月前
|
数据采集 人工智能 监控
AI智能体是刚需还是噱头?3大争议辨真相
AI智能体赛道冰火两重天:微软豪掷百亿押注,Cerebras却黯然退场。资本狂热背后,是生产力革命还是泡沫?西门子、蚂蚁等落地案例展现潜力,但多数企业困于集成成本与技术瓶颈。低代码平台降低门槛,也引发生态锁死担忧。短期ROI难算清,长期价值需战略定力。智能体不是万能药,唯有回归业务本质,方能穿越 hype,走向真正落地。
215 0
|
存储 算法 安全
一文读懂DES加密算法和实现
DES是一种使用56位秘钥对64位长分组进行加密的加密算法。
1376 0
一文读懂DES加密算法和实现
|
10月前
|
机器学习/深度学习 人工智能 算法
最大熵逆强化学习:理论基础、数学推导与工程实现
本文重点讨论逆强化学习(Inverse Reinforcement Learning, IRL),这是模仿学习的重要分支,其核心目标是基于演示数据学习能够最大化期望奖励的最优策略。
387 0
|
关系型数据库 MySQL 大数据
MySQL分区与分表:优化性能与提升可扩展性
本文深入探讨了MySQL数据库中的分区与分表策略,通过详细的代码示例,解释了分区的概念与用途、不同的分区类型以及创建分区表的步骤。同时,文章还介绍了分表的概念、策略和实际操作方法,以代码演示展示了如何创建分表、插入数据以及查询数据。分区和分表作为优化数据库性能和提升可扩展性的关键手段,通过本文的阐述,读者将能够深入了解如何根据数据特点选择合适的分区方式,以及如何灵活地处理大量数据,提高查询和维护效率。这些技术将为数据库设计和优化提供有力支持,确保在大数据场景下能够高效地管理和查询数据。
2850 0
|
SQL Java 数据库连接
JPA异常:Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
JPA异常:Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
|
5月前
|
运维 Java Serverless
Serverless 架构模式深度解析
Serverless并非“无服务器”,而是开发者无需管理服务器,专注业务逻辑。具备按需付费、弹性伸缩、事件驱动等优势,适用于突发流量、定时任务等场景,结合FaaS与BaaS可构建高效应用,是云原生发展的重要方向。
814 1
|
Java
【Java用法】Java中常见的 \t \n 的用法,并附有九九乘法表的Java代码的例子
【Java用法】Java中常见的 \t \n 的用法,并附有九九乘法表的Java代码的例子
606 0
|
存储 缓存 移动开发
PixiJS源码分析系列:第三章 使用 canvas 作为渲染器
PixiJS源码分析系列:第三章 使用 canvas 作为渲染器
|
Java Spring 容器
Spring - 解决 SpringUtil getBean NPE 问题
Spring - 解决 SpringUtil getBean NPE 问题
1320 0