数据加密解密和哈希的解析

news2024/7/7 16:27:13

[S1301]数据的加解密

对提供的原始数据(字符串或者二进制数组)进行加密是数据保护框架体提供的基本功能,接下来我们利用一个简单的控制台程序来演示一下加解密如何实现。数据的加解密均由IDataProtector对象来完成,而该对象由IDataProtectionProvider(不是IDataProtectorProvider)对象来提供,所以在大部分应用场景中针对数据的加密和解密只涉及这两个对象。有了依赖注入的加持,我们也不需要了解这两个接口的具体实现类型,只需要在利用注入的IDataProtectionProvider对象来提供对应的IDataProtector对象,并利用后者完成加解密的工作。

上述的这两个接口定义在 “Microsoft.AspNetCore.DataProtection.Abstractions”这个NuGet包中,它们的默认实现类型以及其他核心类型则承载于NuGet包 “Microsoft.AspNetCore.DataProtection”中,所以我们需要为演示程序添加针对这个NuGet包的引用。由于需要使用到依赖注入框架,我们需要添加针对“Microsoft.Extensions.DependencyInjection”的引用。必要的NuGet包引用添加完成之后,我们编写了如下的演示程序。

using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
using System.Diagnostics;

var originalPayload = Guid.NewGuid().ToString();
var protectedPayload = Encrypt("foo", originalPayload);
var unprotectedPayload = Decrypt("foo", protectedPayload);
Debug.Assert(originalPayload == unprotectedPayload);

static string Encrypt(string purpose, string originalPayload) => GetDataProtector(purpose).Protect(originalPayload);
static string Decrypt(string purpose, string protectedPayload) => GetDataProtector(purpose).Unprotect(protectedPayload);

static IDataProtector GetDataProtector(string purpose)
{
    var services = new ServiceCollection();
    services.AddDataProtection();
    return services
        .BuildServiceProvider()
        .GetRequiredService<IDataProtectionProvider>()
        .CreateProtector(purpose);
}

如上面的代码片段所示,我们将数据的加密和解密操作分别定义在Encrypt和Decrypt方法中,它们使用IDataProtector对象由GetDataProtector方法来提供。在GetDataProtector方法中,我们创建了一个ServiceCollection对象,并调用AddDataProtection扩展方法注册了数据保护框架的基础服务。我们最终利用构建的IServiceProvider对象来提供所需的IDataProtectionProvider对象。IDataProtectionProvider接口的CreateProtector方法定义了一个字符串类型名为“purpose”的参数。从字面上来讲,该参数表示加密的“目的(Purpose)”,它在整个数据保护模型中起到了“秘钥隔离”的作用,我们在本书后续内容中将其称为“Purpose字符串”。

Encrypt和Decrypt方法来利用指定的Purpose字符串作为参数调用GetDataProtector方法得到对应的IDataProtector对象之后,分别调用了该对象的Protect和Unprotect方法完成了针对给定文本内容的加密和解密。我们使用一个GUID转换的字符串作为待加密的数据,并使用“foo”作为Purpose字符串调用Encrypt方法对它进行了加密,最后采用相同的Purpose字符串调用Decrypt方法对加密内容进行解密。

前面的演示实例通过调用IServiceProvider对象的GetRequiredService<T>扩展方法得到所需的IDataProtectionProvider对象,该对象也可以按照如下的形式调用GetDataProtectionProvider扩展方法来获取。IServiceProvider接口还定义了如下这个GetDataProtector扩展方法直接返回IDataProtector对象。

...
static IDataProtector GetDataProtector(string purpose)
{
    var services = new ServiceCollection();
    services.AddDataProtection();
    return services
        .BuildServiceProvider()
        .GetDataProtectionProvider()
        .CreateProtector(purpose);
}

或者

...
static IDataProtector GetDataProtector(string purpose)
{
    var services = new ServiceCollection();
    services.AddDataProtection();
    return services
        .BuildServiceProvider()
        .GetDataProtector (purpose);
}

除了利用依赖注入框架,我们也可以按照如下的方法利用静态类型DataProtectorProvider(定义在“Mcrosoft.AspNetCore.DataProtection.Extensions”NuGet包中)来创建IDataProtectionProvider对象。该类型提供了若干用于创建IDataProtector对象的Create方法重载,我们选择的重载传入的参数为当前应用的名称。

...
static IDataProtector GetDataProtector(string purpose) => DataProtectionProvider.Create("App").CreateProtector(purpose);

[S1302]Purpose字符串一致性

前面我们说到参与同一份数据加解密的两个IDataProtector对象必须具有一致的Purpose字符串,我们现在就来验证这一点。如下面的代码片段所示,我们在调用Decrypt方法进行解密的时候将Purpose字符串从“foo”替换成“bar”。

...
var originalPayload = Guid.NewGuid().ToString();
var protectedPayload = Encrypt ("foo", originalPayload);
var unprotectedPayload = Decrypt ("bar", protectedPayload);
Debug.Assert(originalPayload == unprotectedPayload);
...

当我们调用IDataProtector对象的Unprotect方法对指定内容进行解密时,由于当前Purpose字符串与待解密内容采用的Purpose字符串不符,会直接抛出如图1所示的CryptographicException异常。

image

图1 Purpose字符串不一致导致的异常

[S1303]设置加密内容的有效期

我们知道不论采用的何种加密算法,采用的秘钥位数有多长,如果算力资源或者时间充足,解密都能成功。但是黑客具有的算力资源总归是有限的,如果能够在秘钥能推算出来之前就已经无效了,那么我们采用的加密方式就是安全的。针对有效时间的加解密通过ITimeLimitedDataProtector对象来完成,这个接口都定义在“Mcrosoft.AspNetCore.DataProtection.Extensions” 这个NuGet包中。为了使用这个对象,我们将演示程序改写成如下的形式。

using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
using System.Diagnostics;

var originalPayload = Guid.NewGuid().ToString();
var protectedPayload = Encrypt("foo", originalPayload, TimeSpan.FromSeconds(5));

var unprotectedPayload = Decrypt("foo", protectedPayload);
Debug.Assert(originalPayload == unprotectedPayload);

await Task.Delay(5000);
Decrypt("foo", protectedPayload);

static string Encrypt(string purpose, string originalPayload, TimeSpan timeout)
=> GetDataProtector(purpose)
.Protect(originalPayload, DateTimeOffset.UtcNow.Add(timeout));
static string Decrypt(string purpose, string protectedPayload)
    => GetDataProtector(purpose).Unprotect(protectedPayload, out _);

static ITimeLimitedDataProtector GetDataProtector(string purpose)
{
    var services = new ServiceCollection();
    services.AddDataProtection();
    return services
        .BuildServiceProvider()
        .GetDataProtector(purpose)
        .ToTimeLimitedDataProtector();
}

我们让GetDataProtector方法返回一个ITimeLimitedDataProtector对象,它通过IDataProtector对象的ToTimeLimitedDataProtector扩展方法“转化”而成。用于加密的Encrypt方法添加了一个表示过期时间的timeout参数(类型为TimeSpan),由于ITimeLimitedDataProtector的Protect方法中表示过期时间的参数类型为DateTimeOffset,所以我们基于当前时间和指定的过期时间(TimeSpan)将这个过期时间点计算出来。ITimeLimitedDataProtector接口用于解密的Unprotect方法具有一个表示过期日期的输出参数。

在演示程序中,我们调用Encrypt方法对数据进行加密时将过期时间设置为5秒。对于加密后的内容,我们采用相同的方式对它进行了两次解密,第一个发生在5秒内,第二次则发生在5秒后。程序运行后,第一次解密成功,第二次抛出如图13-3所示的CryptographicException异常。

image


图2 加密数据过期导致的解密异常

[S1304]撤销加密密钥(单个密钥)

在如下的演示程序中,我们创建了ServiceCollection对象并在调用AddDataProtection扩展方法注册了数据保护框架的核心服务。在利用构建的IServiceProvider对象得到IDataProtector对象之后,我们利用它对指定的文本进行加密。在此之后,我们将加密采用的密钥撤销掉。

using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();
services.AddDataProtection();
var sericeProvider = services.BuildServiceProvider();
var protector = sericeProvider.GetDataProtector("foobar");
var originalPayload = Guid.NewGuid().ToString();
var protectedPayload = protector.Protect(originalPayload);

var keyRingProvider = sericeProvider.GetRequiredService<IKeyRingProvider>();
var KeyRing = keyRingProvider.GetCurrentKeyRing();
var keyManager = sericeProvider.GetRequiredService<IKeyManager>();
keyManager.RevokeKey(KeyRing.DefaultKeyId);
protector.Unprotect(protectedPayload);

具体来说,我们利用IServiceProvider对象提供的IKeyRingProvider对象得到对应的IKeyRing对象,该对象的DefaultKeyId属性代表默认使用的密钥ID,我们撤销的也这是这个ID代表的密钥。,我们借助于依赖注入容器得到IKeyManager对象,并将此密钥ID作为参数调用其RevokeKey方法。在密钥撤销之后,我们利用同一个IDataProtector对加密内容进行解密,此时程序会抛出如图3所示的CryptographicException异常。

image


图3 秘钥被撤销导致的解密异常

[S1305]撤销加密密钥(所有密钥)

除了调用IKeyManager的RevokeKey方法撤销某个指定的密钥之外,我们还可以按照如下的方式调用它的RevokeAllKeys方法撤销所有密钥。如果我们觉得目前的所有密钥均不安全,可以调用这个方法。我们在调用该方法的时候需要指定一个撤销的时间和原因(可选)。

using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();
services.AddDataProtection();
var sericeProvider = services.BuildServiceProvider();
var protector = sericeProvider.GetDataProtector("foobar");
var originalPayload = Guid.NewGuid().ToString();
var protectedPayload = protector.Protect(originalPayload);

var keyManager = sericeProvider.GetRequiredService<IKeyManager>();
keyManager.RevokeAllKeys(revocationDate: DateTimeOffset.UtcNow, reason: "No reason");
protector.Unprotect(protectedPayload);

[S1306]瞬时加解密

在某些应用场景中,针对数据的加解密只在一个限定的上下文中进行(比如当前应用的生命周期内),这种场景适用一种被称为“瞬时(Transient或者Ephemeral)加解密”的方式。这种加解密方式会使用到EphemeralDataProtectionProvider类型,该类型同样实现了ITimeLimitedDataProtector接口。如果我们利用它提供的IDataProtector对象对一段二进制内容进行加密,密文只能通过它自身提供的IDataProtector对象才能解开。

如下面的代码片段所示,我们定义了一个CreateEphemeralDataProtectionProvider方法用来创建上述的这个对象。我们在调用ServiceCollection对象的AddDataProtection扩展方法并得到返回的IDataProtectionBuilder之后,我们调用了该对象的UseEphemeralDataProtectionProvider扩展方法完成针对EphemeralDataProtectionProvider的服务注册,所以我们最终得到的IDataProtectionProvider对象的类型就是EphemeralDataProtectionProvider。

using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
using System.Diagnostics;

var originalPayload = Guid.NewGuid().ToString();
var dataProtectionProvider = CreateEphemeralDataProtectionProvider();
var protector = dataProtectionProvider.CreateProtector("foobar");
var protectedPayload = protector.Protect(originalPayload);

protector = dataProtectionProvider.CreateProtector("foobar");
Debug.Assert(originalPayload == protector.Unprotect(protectedPayload));

protector = CreateEphemeralDataProtectionProvider().CreateProtector("foobar");
protector.Unprotect(protectedPayload);

static IDataProtectionProvider CreateEphemeralDataProtectionProvider()
{
    var services = new ServiceCollection();
    services.AddDataProtection().UseEphemeralDataProtectionProvider();
    return services.BuildServiceProvider().GetRequiredService<IDataProtectionProvider>();
}

在利用EphemeralDataProtectionProvider提供的IDataProtector对象对一段文本加密后,我们对密文实施了两次解密。第一次采用的IDataProtector对象通过同一个EphemeralDataProtectionProvider对象提供的,第二个则则不是。该演示程序运行之后,第一次解密顺利完成,第二次则抛出了如图4所示的CryptographicException异常。

image


图4 利用EphemeralDataProtectionProvider提供“瞬时”加解密

[S1307]密钥哈希

用户密码作为机密性最高的信息是不能以明文形式存储的,我们一般会存储密码的哈希值。虽然哈希的非对称性确保不能直接通过哈希值得到被哈希的原始内容,但是在强大的算力面前已经不足以提供我们期望的安全保障。针对密钥的保护,目前最安全的哈希方式应该是PBKDF2(Password-Based Key Derivation Function 2)。PBKDF2是一种基于密码的Key Derivation(采用某种算法根据指定的密码或者主键生成一个密钥)函数,它采用伪随机函数以任意指定长度导出密钥。它目前是RSA实验室公钥加密标准(PKCS:Public-Key Cryptography Standards)序列的一部分。PBKDF2提高安全系数主要采用“添加随机盐(Salt)”和“多次哈希”这两种手段。如果希望对PBKDF2具有深入的了解,可以参阅官方规范文档(https://tools.ietf.org/html/rfc2898#section-5.2)。

我们在可以利用“Microsoft.AspNetCore.Cryptography.KeyDerivation”这个NuGet包提供的API来对密码进行哈希。这是一个完全独立的类库,与上面介绍的以IDataProtector对象为核心的数据保护框架没有关系。基于PBKDF2的密码哈希可以直接调用KeyDerivation类型的如下这个静态方法Pbkdf2来完成。

public static class KeyDerivation
{
public static byte[] Pbkdf2(string password, byte[] salt, KeyDerivationPrf prf,
    int iterationCount, int numBytesRequested);
}

public enum KeyDerivationPrf
{
    HMACSHA1,
    HMACSHA256,
    HMACSHA512
}

PBKDF2并没有限制使用某种固定的加密算法。在调用上面这个Pbkdf2方法的时候,我们可以利用prf参数指定采用的伪随机算法(PRF:Pseudo-random Function)。这是一个KeyDerivationPrf类型的枚举,三个枚举项对应的哈希算法分别为SHA-1、SHA-256和SHA-512。Pbkdf2方法的其他参数分别表示待哈希的密码、随机盐、迭代次数(次数越大、安全系数越大)和最终生成哈希值的字节数。

using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using System.Security.Cryptography;

var password 	= "password";
var salt 		= new byte[16];
var iteration 	= 1000;

using (var generator = RandomNumberGenerator.Create())
{
    generator.GetBytes(salt);
}

Console.WriteLine(Hash(KeyDerivationPrf.HMACSHA1));
Console.WriteLine(Hash(KeyDerivationPrf.HMACSHA256));
Console.WriteLine(Hash(KeyDerivationPrf.HMACSHA512));

string Hash(KeyDerivationPrf prf)
{
    var hashed = KeyDerivation.Pbkdf2(
        password: password,
        salt: salt,
        prf: prf,
        iterationCount: iteration,
        numBytesRequested: 32);
    return Convert.ToBase64String(hashed);
}

上面的代码片段演示了如何为提供的密码(“password”)生成指定位数(32字节,256位)的哈希值。我们采用一个随机生成的盐值(16字节,128位),执行1000次迭代,针对三种不同的哈希算法生成对应的哈希值。Base64编码后的三个哈希值以如图13-5所示的方式输出到控制台上。

image


图5 采用PBKDF2生成的密码哈希

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

React+TS 从零开始教程(4):useEffect

上一节传送门&#xff1a;ReactTS 从零开始教程&#xff08;3&#xff09;&#xff1a;useState 源码链接&#xff1a;https://pan.quark.cn/s/c6fbc31dcb02 上一节&#xff0c;我们已经学会了React的第一个Hook&#xff1a;useState。 这一节&#xff0c;我们要学习的是另一…

2024年江西省研究生数学建模竞赛A题交通信号灯管理论文和代码分析

经过不懈的努力&#xff0c;2024年江西省研究生数学建模竞赛A题论文和代码已完成&#xff0c;代码为A题全部问题的代码&#xff0c;论文包括摘要、问题重述、问题分析、模型假设、符号说明、模型的建立和求解&#xff08;问题1模型的建立和求解、问题2模型的建立和求解、问题3模…

作业7.2

用结构体数组以及函数完成: 录入你要增加的几个学生&#xff0c;之后输出所有的学生信息 删除你要删除的第几个学生&#xff0c;并打印所有的学生信息 修改你要修改的第几个学生&#xff0c;并打印所有的学生信息 查找你要查找的第几个学生&#xff0c;并打印该的学生信息 1 /*…

Qt中使用MySQL数据库详解,好用的模块类封装

本文将详细介绍如何在Qt应用程序中集成MySQL数据库&#xff0c;并封装实现好用的mysql数据库操作类。包括环境准备、连接数据库、执行查询及异常处理等关键步骤&#xff0c;同时包含mysql驱动的编译。分享给有需要的小伙伴&#xff0c;喜欢的可以点击收藏。 目录 环境准备 项…

scikit-learn教程

scikit-learn&#xff08;通常简称为sklearn&#xff09;是Python中最受欢迎的机器学习库之一&#xff0c;它提供了各种监督和非监督学习算法的实现。下面是一个基本的教程&#xff0c;涵盖如何使用sklearn进行数据预处理、模型训练和评估。 1. 安装和导入包 首先确保安装了…

win10下安装PLSQL14连接Oracle数据库

问题背景 在使用Oracle开发过程中&#xff0c;经常会使用工具来连接数据库&#xff0c;方便查询、处理数据。其中有很多工具可以使用&#xff0c;比如dbeaver、plsql等。本文主要介绍在win10环境下&#xff0c;plsql14的安装步骤以及安装过程中遇到的一些问题。 安装步骤及问题…

JDBC操作流程

目录 简介 具体操作 1. 引入驱动包 1&#xff09;下载驱动包 2&#xff09;引入驱动包到项目中 2. 编写代码 1&#xff09;创建数据源 2&#xff09;建立连接 3&#xff09;构造 SQL 语句 4&#xff09;执行 SQL 语句 5&#xff09;释放资源 总结 简介 JDBC 就是使…

2024年工程项目管理者的软件指南:11款必试进度管理工具

本文将分享11个值得关注的工程项目进度管理软件&#xff1a;Worktile、Fieldwire、Procore、Buildxact、InEight、Contractor Foreman、Housecall Pro、ClickUp、RedTeam Go、Visual Planning、B2W Schedule。 在竞争激烈的建筑行业&#xff0c;工程项目的进度管理是项目成功的…

rocketmq实现多数据源配置

rocketmq实现多数据源配置 背景&#xff1a;一 添加ExtRocketMQTemplateConfiguration配置类二 添加非标mq的配置参数三 非标准RocketMQTemplate 背景&#xff1a; 在实际项目中我们可能会遇到在springboot项目中使用多个mq数据源&#xff0c;那我们该如何配置呢&#xff1f; …

[DataWhale大模型应用开发]学习笔记1-尝试搭建向量数据库

1.词向量 1.定义 词向量&#xff08;Word Vector&#xff09;是将单词表示为向量形式的技术&#xff0c;是自然语言处理&#xff08;NLP&#xff09;中的一种常用方法。通过将单词转化为向量&#xff0c;计算机能够更好地理解和处理语言。简单来说&#xff0c;词向量就是将单…

golang结合neo4j实现权限功能设计

neo4j 是非关系型数据库之图形数据库&#xff0c;这里不再赘述。 传统关系数据库基于rbac实现权限, user ---- role ------permission,加上中间表共5张表。 如果再添上部门的概念&#xff1a;用户属于部门&#xff0c;部门拥有 角色&#xff0c;则又多了一层&#xff1a; user-…

vulnhub靶场ai-web 2.0

1 信息收集 1.1 主机发现 arp-scan -l 主机地址为192.168.1.4 1.2 服务端口扫描 nmap -sS -sV -A -T5 -p- 192.168.1.4 开放22&#xff0c;80端口 2 访问服务 2.1 80端口访问 http://192.168.1.4:80/ 先尝试admin等其他常见用户名登录无果 然后点击signup发现这是一个注…

Codeforces Round 346 (Div. 2) E. New Reform 题解 并查集

New Reform 题目描述 Berland has n n n cities connected by m m m bidirectional roads. No road connects a city to itself, and each pair of cities is connected by no more than one road. It is not guaranteed that you can get from any city to any other one,…

如何养成爱自己的习惯:吸引世间美好,改变命运

在这个快节奏、高压力的时代&#xff0c;我们常常被各种事务所困扰&#xff0c;内心难以得到真正的宁静。然而&#xff0c;古老的智慧告诉我们&#xff0c;“静”是宇宙万物的根源&#xff0c;是生命恢复的根本。本文将探讨如何养成“静”的习惯&#xff0c;从而吸引世间美好&a…

Apache POI、EasyPoi、EasyExcel

&#xff08;一&#xff09;Apache PoI 使用 &#xff08;二&#xff09;EasyPoi使用 &#xff08;三&#xff09;EasyExcel使用 官方文档&#xff1a; 写Excel | Easy Excel 官网 写 使用注解在字段上进行标识 使用最简单的方法二即可 /*** 最简单的写* <p>* 1. 创建…

入门PHP就来我这(纯干货)04

~~~~ 有胆量你就来跟着路老师卷起来&#xff01; -- 纯干货&#xff0c;技术知识分享 ~~~~ 路老师给大家分享PHP语言的知识了&#xff0c;旨在想让大家入门PHP&#xff0c;并深入了解PHP语言。 我们接着《想入门PHP就来我这&#xff08;纯干货&#xff09;03》继续往下学习&am…

忘记家里的wifi密码用iPhone苹果手机怎么找回?

忘记家里的wifi密码用iPhone苹果手机怎么找回&#xff1f; 1、打开iPhone苹果手机上的设置&#xff1b; 2、在iPhone苹果手机设置里找到并进入无线局域网&#xff1b; 3、选择要找回密码的wifi&#xff0c;且已连接&#xff0c;并点击后面的更多进入&#xff1b; 4、进入无线局…

Excel分组求和

目录 1 参考文章2 UNIQUE函数分组3 SUMIF函数分组求和 1 参考文章 1.整体思路&#xff1a;https://blog.csdn.net/Alice_loong/article/details/135580130 2.UNIQUE函数&#xff1a;https://mp.weixin.qq.com/s?__bizMzI3OTcwNDE3OQ&mid2247487044&idx1&sna28108…

浅谈Web性能测试(原创)

一、性能测试不是什么高技术的活&#xff1a; 说到性能测试&#xff0c;很多工作时间较短的新同事或者应届生就很害怕。 为什么害怕&#xff0c;因为感觉无从下手&#xff0c;不知道该做什么、怎么做、做到什么程度&#xff1f; 一听性能测试首先想到的是各种专业的性能测试…

Echarts-柱状图

1.案例1 1.1代码 option = {textStyle: {color: #fff // 标题文字颜色为白色},tooltip: {trigger: axis,axisPointer: {type: shadow,},},legend: {textStyle: {color: white}},grid: {top: 15%,left: 4%,right: 4%,bottom: 7%,containLabel: true},xAxis:{type: category,da…