Rust: 加密算法库 ring 如何用于 RSA 数字签名?

本来用 rsa 库基本搞定,但文心一言建议改用 ring 库。原因是 rsa 库已经放弃维护,而 ring 库性能公认很好。但是如何进行 RSA 数字签名,网上几乎查不到这方面材料。仔细查看了 ring 库的源代码和代码注释,终于完成趟坑。总结一下供大家参考。

1. 生成密钥文件

用 RSA数字签名算法时,如何从磁盘文件加载密钥?这需要用 openssl 工具。这个工具如何下载安装不再赘述。

1.1 PEM 格式密钥文件

PEM(Privacy-Enhanced Mail)直译为“保密邮件”,但在此上下文中,它主要指的是一种用于存储加密密钥和证书的文件格式。PEM格式是一种基于ASCII编码的密钥和证书的存储格式,说白了就是文本文件,广泛用于安全领域,特别是在SSL/TLS协议中的证书和密钥管理。

以下是关于PEM格式密钥文件的详细解释:

  1. 文件内容

    • PEM文件可以包含公钥、私钥、证书等敏感信息。
    • 这些信息通常以Base64编码的形式存储,并且包含了起始标记和结束标记,以便于识别和区分不同类型的密钥和证书。
  2. 文件结构

    • PEM文件的内容通常以-----BEGIN <标签>-----开头,以-----END <标签>-----结束。
    • <标签>部分用于指示文件内容的类型,例如CERTIFICATE表示证书,PRIVATE KEY表示私钥,等等。
    • Base64编码的数据位于这两个标记之间,是实际的密钥或证书内容。
  3. 应用场景

    • PEM格式的文件在建立安全的网络连接(如HTTPS)时用于证明服务器的身份。
    • 它们也用于对数据进行加密和解密,或者进行数字签名。
    • PEM文件还可以用于构建完整的证书链,以验证服务器证书的有效性。
  4. 安全性

    • 由于PEM文件中包含了敏感的密钥和证书信息,因此需要采取一些措施来保护其安全性。
    • 可以通过严格限制访问权限、使用加密算法进行加密存储、以及采取安全通道和安全设备来传输和存储PEM文件,以防止中间人攻击和数据泄露。
  5. 与其他格式的转换

    • PEM格式与其他格式(如DER、PKCS#7、PKCS#12等)之间的转换是常见的需求。
    • 可以使用OpenSSL工具或其他相关工具来完成这些转换。

综上所述,PEM是一种重要的密钥和证书存储格式,在安全通信和数据保护方面发挥着关键作用。了解PEM格式的结构和应用场景,有助于更好地管理和保护敏感信息的安全性。

1.2 生成 PEM 格式的 RSA 密钥文件

下面 openssl 命令可以生成一个名为private_key.pem 的密钥文件。该文件可以用记事本打开查看结果。

openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048

1.3 从 RSA 密钥文件导出 RSA 公钥文件

下面 openssl 命令可以从私钥文件 private_key.pem 中提取出公钥文件 public_key.pem

openssl rsa -pubout -in private_key.pem -out public_key.pem

1.4 PEM 密钥文件的使用

我曾用过 C++ 的 Crypto 库,这个库可以直接使用 PEM 密钥文件加载密钥。我以为 ring 库也是如此,结果非常失望。如果哪位朋友能用 ring 库直接加载,希望能告知,本人不胜感谢!

1.5 生成 PKCS#8 密钥

私钥文件用于签名。ring 库可以导入PKCS#8 规格的密钥,要求密钥文件按照 pk8 格式保存。ring 库的源代码注释提供了数字签名的方法。首先 ring 库需要的 PKCS#8 密钥证书的生成方法,代码如下:

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048  -pkeyopt rsa_keygen_pubexp:65537 | openssl pkcs8 -topk8 -nocrypt -outform der > rsa-2048-private-key.pk8

1.6 导出公钥文件

公钥文件用于验证数字签名。ring 库可以读取 DER 格式的公钥文件。但 ring 的源代码等文档并没提供如何获得这份文件。经过反复尝试,发现从私钥文件抽取公钥文件分两步进行:首先生成 PEM 格式公钥文件,然后再转换成 DER 格式的公钥文件。命令如下:

openssl rsa -pubout -in rsa-2048-private-key.pk8 -inform DER -outform PEM -out rsa-2048-public-key.pem
openssl rsa -pubin -in rsa-2048-public-key.pem -inform PEM -RSAPublicKey_out -outform DER -out rsa-2048-public-key.der

1.7 关于 PEM、DER、PK8 格式的说明

PEM、DER、PK8(通常指的是PKCS#8私钥格式,但为简化讨论,这里将PK8视为PKCS#8的私钥部分的简称)是三种常见的密钥文件格式,它们各自有不同的特点和应用场景。以下是关于这三种密钥文件格式以及它们名字来历的详细说明:

1.7.1 PEM(Privacy-Enhanced Mail)
  • 来历
    • PEM格式最早由美国互联网工程任务组(IETF)在1987年的RFC 1421中定义。
    • 最初是为了解决电子邮件的加密和签名问题而设计的。
  • 特点
    • PEM格式是一种基于ASCII编码的密钥和证书的存储格式。
    • 文件通常以“.pem”为后缀名。
    • 可以包含公钥、私钥、证书等敏感信息。
    • 使用Base64编码,并且包含了起始标记和结束标记,以便于识别和区分不同类型的密钥和证书。
    • 具有良好的可读性和可编辑性。

1.7.2 DER(Distinguished Encoding Rules)

  • 来历
    • DER是一种二进制编码格式,常用于证书和密钥的存储和传输。
    • 它是ASN.1(Abstract Syntax Notation One)标准的一部分,由国际电信联盟(ITU-T)定义。
  • 特点
    • DER文件通常以“.der”或“.cer”(在某些上下文中,特别是Windows平台上,“.cer”可能用于表示DER编码的证书文件)为后缀名。
    • 与PEM格式相比,DER格式更加紧凑和高效,因为它使用二进制编码而不是Base64编码。
    • DER格式的文件不易于阅读和编辑,通常需要专业的工具才能查看和解析。

1.7.3 PKCS#8(Private-Key Cryptography Standards #8)

  • 来历
    • PKCS#8是由RSA实验室发布的一种标准,用于存储私钥信息。
    • 它是PKCS(Public-Key Cryptography Standards)系列标准的一部分。
  • 特点
    • PKCS#8定义了私钥的语法,包括如何对私钥进行编码和加密。
    • 提供了未加密和加密两种格式的私钥存储方式。
    • 未加密的私钥信息通常以明文形式存储(但编码为Base64或其他格式以方便存储和传输),而加密的私钥信息则使用密码短语或其他加密算法进行保护。
    • 常与OpenSSL等工具配合使用。
    • 在较新版本的OpenSSL中,生成的RSA私钥默认符合PKCS#8的PrivateKeyInfo结构。

需要注意的是,虽然PEM、DER和PKCS#8在密钥和证书的存储和管理中扮演着重要角色,但它们并不是互斥的。实际上,它们可以相互转换,并且可以在不同的应用场景中共同使用。例如,可以使用OpenSSL等工具将PEM格式的私钥转换为DER格式,或者将PKCS#8格式的私钥转换为PKCS#1格式的私钥(尽管PKCS#1主要用于公钥的存储)。

2. 实现签名与验证的源代码

下面直接贴 ring 提供的示例代码:
Cargo.toml 添加下面库:

ring = "0.17.8"

程序源代码:

use ring::{rand, rsa, signature};

fn sign_and_verify_rsa(private_key_path: &std::path::Path,
                       public_key_path: &std::path::Path)
                       -> Result<(), MyError> {
// Create an RSA keypair from the DER-encoded bytes. This example uses
// a 2048-bit key, but larger keys are also supported.
let private_key_der = read_file(private_key_path)?;
let key_pair = rsa::KeyPair::from_der(&private_key_der)
    .map_err(|_| MyError::BadPrivateKey)?;

// Sign the message "hello, world", using PKCS#1 v1.5 padding and the
// SHA256 digest algorithm.
const MESSAGE: &'static [u8] = b"hello, world";
let rng = rand::SystemRandom::new();
let mut signature = vec![0; key_pair.public().modulus_len()];
key_pair.sign(&signature::RSA_PKCS1_SHA256, &rng, MESSAGE, &mut signature)
    .map_err(|_| MyError::OOM)?;

// Verify the signature.
let public_key =
    signature::UnparsedPublicKey::new(&signature::RSA_PKCS1_2048_8192_SHA256,
                                      read_file(public_key_path)?);
public_key.verify(MESSAGE, &signature)
    .map_err(|_| MyError::BadSignature)
}

#[derive(Debug)]
enum MyError {
   IO(std::io::Error),
   BadPrivateKey,
   OOM,
   BadSignature,
}

fn read_file(path: &std::path::Path) -> Result<Vec<u8>, MyError> {
    use std::io::Read;

    let mut file = std::fs::File::open(path).map_err(|e| MyError::IO(e))?;
    let mut contents: Vec<u8> = Vec::new();
    file.read_to_end(&mut contents).map_err(|e| MyError::IO(e))?;
    Ok(contents)
}

我加了一段测试程序供参考:

#[test]
fn test2() {
    let pkcs8 = read_file(std::path::Path::new("rsa-2048-private-key.pk8")).unwrap();
    let key_pair = rsa::KeyPair::from_pkcs8(&pkcs8).unwrap();

    const MESSAGE: &'static [u8] = b"hello, world";
    let rng = rand::SystemRandom::new();
    let mut signature = vec![0; key_pair.public().modulus_len()];
    
    key_pair
        .sign(&signature::RSA_PKCS1_SHA256, &rng, MESSAGE, &mut signature)
        .map_err(|_| MyError::OOM)
        .unwrap();
    println!("\nsignature:{:?}", signature);

    let public_key = signature::UnparsedPublicKey::new(
        &signature::RSA_PKCS1_2048_8192_SHA256,
        read_file(std::path::Path::new("rsa-2048-public-key.der")).unwrap(),
    );
    
    println!("\npublic_key = {:?}", public_key);
    public_key
        .verify(MESSAGE, &signature).unwrap();
}

3. 心得体会

  • 阅读开源库的源代码可以解决文档不足的问题。
  • 希望多展开交流,加快学习进步!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/909073.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

zxing生成、解析二维码,条形码

1、maven依赖 <!--zxing依赖--><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.1.0</version></dependency><dependency><groupId>com.google.zxing</groupI…

有效增加网站流量的实用策略和技巧

内容概要 在数字化时代&#xff0c;网站流量的增加被视为在线业务成功的关键。网站流量不仅仅意味着访问者的数量&#xff0c;还影响着品牌知名度、用户参与度和销售转化率。针对这一需求&#xff0c;企业需要采取行之有效的策略&#xff0c;例如搜索引擎优化&#xff08;SEO&…

玄机-应急响应- Linux入侵排查

一、web目录存在木马&#xff0c;请找到木马的密码提交 到web目录进行搜索 find ./ type f -name "*.php" | xargs grep "eval(" 发现有三个可疑文件 1.php看到密码 1 flag{1} 二、服务器疑似存在不死马&#xff0c;请找到不死马的密码提交 被md5加密的…

从 vue 源码看问题 — vue 如何进行异步更新?

前言 在上一篇 如何理解 vue 响应式&#xff1f; 中&#xff0c;了解到响应式其实是通过 Observer 类中调用 defineReactive() 即 Object.defineProperty() 方法为每个目标对象的 key&#xff08;key 对应的 value 为非数组的&#xff09; 设置 getter 和 setter 实现拦截&…

本地部署bert-base-chinese模型交互式问答,gradio

首先下载bert-base-chinese&#xff0c;可以在 Huggingface, modelscope, github下载 pip install gradio torch transformers import gradio as gr import torch from transformers import BertTokenizer, BertForQuestionAnswering# 加载bert-base-chinese模型和分词器 mod…

SYN590RH是SYNOXO全新开发设计的一款宽电压范围,低功耗,高性能,无需外置AGC电容de单芯片ASK或00 K射频接收器

一般描述 SYN590RH是SYNOXO全新开发设计的一款宽电压范围&#xff0c;低功耗&#xff0c;高性能&#xff0c;无需外置AGC电容&#xff0c;灵敏度达到典型-110 dBm,400MHz~450MHz频率范围应用的单芯片ASK或00 K射频接收器。 SYN590RH是一款典型的即插即用型单片高…

Unreal5从入门到精通之如何在指定的显示器上运行UE程序

前言 我们有一个设备,是一个带双显示器的机柜,主显示器是一个小竖屏,可以触屏操作,大显示器是一个普通的横屏显示器。我们用这个机柜的原因就是可以摆脱鼠标和键盘,直接使用触屏操作,又可以在大屏观看,非常适合用于教学。 然后我们为这款机柜做了很多个VR项目,包括Uni…

C++中unordered_map和unordered_set的介绍以及用哈希表封装实现unordered_map和unordered_set

目录 1.unordered_map和unordered_set的使用 1.1unordered_set类的介绍 1.2unordered_set和set的使用差异 1.3unordered_map和map的使用差异 1.4unordered_multimap/unordered_multiset 2.用哈希表封装实现unordered_set和unordered_map 2.1实现出复用哈希表的框架并支持…

stm32学习4

学习目录 一.流水灯1.创建文件2.编写相关代码 一.流水灯 1.创建文件 将方法进行分类保存在不同的 .c 文件中&#xff0c;方便复用和寻找&#xff1b; 创建Hardware\LED文件&#xff0c;其中有led.c和led.h文件&#xff0c;用于存放有关LED灯操作的方法&#xff1b; 在User文…

JVM结构图

JVM&#xff08;Java虚拟机&#xff09;是Java编程语言的核心组件之一&#xff0c;负责将Java字节码翻译成机器码并执行。JVM由多个子系统组成&#xff0c;包括类加载子系统、运行时数据区、执行引擎、Java本地接口和本地方法库。 类加载子系统&#xff08;Class Loading Subsy…

mysql之命令行基础指令

一&#xff1a;安装好mysql后&#xff0c;注册好账号密码。 二&#xff1a;在命令行进行登录的指令如下 mysql -u用户名 -p 例如&#xff1a;mysql -uroot -p; 然后按下回车&#xff0c;进入输入密码。 三&#xff1a;基本指令&#xff1a; 1&#xff1a;查看当前账户的所有…

LabVIEW适合开发的软件

LabVIEW作为一种图形化编程环境&#xff0c;主要用于测试、测量和控制系统的开发。以下是LabVIEW在不同应用场景中的适用性和优势。 一、测试与测量系统 LabVIEW在测试与测量系统中的应用广泛&#xff0c;是工程测试领域的主流工具之一。利用其强大的数据采集与处理功能&…

MySQL表的增删改查(CRUD3约束)

这次我们开始先不复习嗷&#xff0c;等到把数据表的删除说完咱们统一&#xff0c;总结书写 1.数据表的删除&#xff1a; 语法&#xff1a; 1. 使用 DROP TABLE 语句删除单个表 基本语法&#xff1a;DROP TABLE [IF EXISTS] table_name; table_name是要删除的表的名称。IF EXIS…

国内首位聋人 Android 软件工程师体验通义灵码,“这真是太棒了”

Hi 大家好&#xff01; 我就是人见人爱、Bug 闪开的通义灵码&#xff01; 上个月&#xff0c;我上线了一项新能力&#xff1a; 体验通义灵码 workspace&#xff1a;轻松分析项目结构&#xff0c;结合代码仓库理解工程、查询问答等 补充说明&#xff1a;当你需要快速了解一个工…

萌熊数据科技:剑指脑机转入,开启科技新篇章

近日&#xff0c;科技圈传来一则令人瞩目的消息&#xff0c;天津萌熊数据科技有限公司和天津一万年科技发展有限公司在全国范围内大力开展AI加生命科学的主体业务&#xff0c;并明确将朝着脑机转入方向深入发展&#xff0c;引发了行业内外的广泛关注。 天津萌熊数据科技有限公司…

OceanBase 安装使用详细说明

OceanBase 安装使用详细说明 一、系统环境要求二、安装OceanBase环境方案一:在线下载并安装all-in-one安装包方案二:离线安装all-in-one安装包安装前的准备工作三、配置OceanBase集群编辑配置文件部署和启动集群连接到集群集群状态和管理四、创建业务租户和数据库创建用户并赋…

MYSQL---TEST5(Trigger触发器Procedure存储过程综合练习)

触发器Trigger 数据库mydb16_trigger创建 表的创建 goods create table goods( gid char(8) primary key, #商品号 name varchar(10), #商品名 price decimal(8,2), #价格 num int&#xff1b;&#xff09; #数量orders create tabl…

MySQL 完整教程:从入门到精通

MySQL 完整教程&#xff1a;从入门到精通 MySQL 是一个广泛使用的关系型数据库管理系统&#xff0c;它使用结构化查询语言 (SQL) 来管理和操作数据。本文将详细介绍 MySQL 的基本概念、安装与配置、常用 SQL 语法、数据表的创建与管理、索引、视图、存储过程、触发器等高级特性…

winfrom控制应用程式不能双开

功能&#xff1a;控制winform应用程式不能双开 //应用程式不能双开bool isAppRunning false;Mutex mutex new Mutex(true, "MyApp", out isAppRunning);if (!isAppRunning){MessageBox.Show("程序已运行&#xff0c;不能再次打开&#xff01;");Environm…