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

[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生成的密码哈希

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

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

相关文章

FormLayout布局和FormItem对比

FormLayout布局和FormItem对比 FormLayout布局 package mainimport ("fyne.io/fyne/v2""fyne.io/fyne/v2/app""fyne.io/fyne/v2/container""fyne.io/fyne/v2/layout""fyne.io/fyne/v2/widget" )func main() {myApp : app.…

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发现这是一个注…

【Rust入门教程】hello world程序

文章目录 前言Hello World程序运行总结 前言 对于学习任何一种新的编程语言&#xff0c;我们都会从编写一个简单的Hello World程序开始。这是一个传统&#xff0c;也是一个开始。在这篇文章中&#xff0c;我们将一起学习如何在Rust中编写你的第一个程序&#xff1a;Hello Worl…

重磅发布|WAIC 2024最新活动日程安排完整发布!

WAIC 2024 将于 7 月在上海世博中心和世博展览馆举行&#xff0c;论坛时间为 7 月 4 日至 6 日&#xff0c;展览时间为 7 月 4 日至 7 日。会议涵盖 AI 伦理治理、大模型、具身智能、投融资、教育人才等重点话题&#xff0c;体现 AI 向善等价值导向&#xff0c;9 位大奖得主和 …

免交互简单操作

免交互 交互&#xff1a;我们发出指令控制程序的运行&#xff0c;程序在接收到指令后按照指令的效果作出对应的反应 免交互&#xff1a;间接的&#xff0c;通过第三方的方式把指令传给程序&#xff0c;不用直接下达指令 Here Document免交互 这是命令行格式&#xff0c;也可…

CVE-2024-0603 漏洞复现

CVE-2024-0603 源码&#xff1a;https://gitee.com/dazensun/zhicms 开题&#xff1a; CVE-2024-0603描述&#xff1a;ZhiCms up to 4.0版本的文件app/plug/controller/giftcontroller.php中存在一处未知漏洞。攻击者可以通过篡改参数mylike触发反序列化&#xff0c;从而远程…

Dockerhub无法拉取镜像配置阿里镜像加速器

打开阿里镜像加速地址&#xff1a; https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors 根据平台类型按照对应方式进行配置&#xff1a;Dokcer Desktop是在右上角点开配置 找到Docker Engine 进行设置JSON结构&#xff1a; 记得要重启Docker服务才会生效&#xff01…

无偏归一化自适应心电ECG信号降噪方法(MATLAB)

心电信号作为一种生物信号&#xff0c;含有大量的临床应用价值的信息&#xff0c;在现代生命医学研究中占有重要的地位。但心电信号低频、低幅值的特点&#xff0c;使其在采集和传输的过程中经常受到噪声的干扰&#xff0c;使心电波形严重失真&#xff0c;从而影响后续的病情分…

WSO2 products 文件上传漏洞(CVE-2022-29464)

前言 CVE-2022-29464 是一个影响多个 WSO2 产品的严重远程代码执行&#xff08;RCE&#xff09;漏洞。这些产品包括 WSO2 API Manager、WSO2 Identity Server 和 WSO2 Enterprise Integrator 等。由于用户输入验证不当&#xff0c;该漏洞允许未经身份验证的攻击者在服务器上上…

修改Springboot项目名称

修改Springboot项目名称 1. 整体描述2. 具体步骤2.1 修改module名称2.2 修改程序包名2.3 mybatis/mybatis-plus配置修改2.4 logback文件2.5 yml配置2.6 Application启动类2.7 其他 3. 总结 1. 整体描述 开发过程中&#xff0c;经常遇到新来个项目&#xff0c;需要一份初始代码…

平衡树专题Splay

写在前面&#xff1a; 部分来自孙宝&#xff08;Steven24&#xff09;的博客&#xff0c;表示感谢。 认识 什么是Splay 就是BST的一种&#xff0c;整体效率是很高的&#xff0c;均摊的次数是O(logn)级别的。 基本操作就是把节点旋转到BST的root&#xff0c;从而改善BST的平…

线性代数大题细节。

4.4 方程组解的结构&#xff08;二&#xff09;_哔哩哔哩_bilibili

无序中的秩序:为何看似混乱的工作方式可能更高效;刚刚!研究表明:混乱可能更有利于创造力;注意!你的过度整理可能正在浪费时间

当面对循规蹈矩&#xff0c;还是自由独立的选择题时&#xff0c;你应当选择自由独立。因为这样&#xff0c;你不但更省力&#xff0c;更省心&#xff0c;而且效率更高&#xff0c;生活更好。 在日常生活和工作中,经常会遇到两种截然不同的人: • 一种是事无巨细,将一切都安排得…

全面了解机器学习

目录 一、基本认识 1. 介绍 2. 机器学习位置 二、机器学习的类型 1. 监督学习 2. 无监督学习 3. 强化学习 三、机器学习术语 1. 训练样本 2. 训练 3. 特征 4. 目标 5. 损失函数 四、机器学习流程 五、机器学习算法 1. 分类算法 2. 聚类算法 3. 关联分析 4. …

红队工具Finger 安装具体以步骤-示例centos

1.git clone https://github.com/EASY233/Finger.git 如果没有 yum install git 2.pip3 install -r requirements.txt 找到finger所在的文件夹 可以用find -name "Finger"进入文件中配置命令 前提要安装python yum install python-pip33.python3 Finger.py -h

Databend 开源周报第 151 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend。 支持递归调用 UD…

浅谈k8s中cni0和docker0的关系和区别

最近在复习k8s网络方面的知识&#xff0c;查看之前学习时整理的笔记和文档还有过往自己总结的博客之后发现一个问题&#xff0c;就是在有关flannel和calico这两个k8s网络插件的文章和博客中&#xff0c;会涉及到cni0和docker0这两个网桥设备&#xff0c;但是都没有明确说明他们…

新华三通用大模型算力底座方案:为AI时代注入强大动力

在人工智能技术日新月异的今天&#xff0c;大模型作为推动AI进步的重要驱动力&#xff0c;是百行百业不断追逐的热点。大模型以其强大的泛化能力、卓越的模型效果和广泛的应用场景&#xff0c;正改变着人工智能的未来。作为国内领先的ICT解决方案提供商&#xff0c;新华三集团凭…

【刷题汇总--牛牛的快递、最小花费爬楼梯、数组中两个字符串的最小距离】

C日常刷题积累 今日刷题汇总 - day0021、牛牛的快递1.1、题目1.2、思路1.3、程序实现1.4、程序实现(扩展) 2、最小花费爬楼梯2.1、题目2.2、思路2.3、程序实现 3、数组中两个字符串的最小距离3.1、题目3.2、思路3.3、程序实现3.4、补充0x3f3f3f3f 4、题目链接 今日刷题汇总 - d…

解码未来城市:探秘数字孪生的奥秘

在科技日新月异的今天&#xff0c;"数字孪生"&#xff08;Digital Twin&#xff09;这一概念如同一颗璀璨的新星&#xff0c;照亮了智慧城市、智能制造等多个领域的前行之路。本文将深入浅出地解析数字孪生的定义、技术原理、应用场景及未来发展&#xff0c;带您一窥…