《ASP.NET Core 6框架揭秘》实例演示[19]:数据加解密与哈希

数据保护(Data Protection)框架旨在解决数据在传输与持久化存储过程中的一致性(Integrity)和机密性(confidentiality)问题,前者用于检验接收到的数据是否经过篡改,后者通过对原始的数据进行加密以避免真实的内容被人窥视。数据保护是支撑ASP.NET身份认证的一个重要的基础框架,同时也可以作为独立的框架供我们使用。[本文节选《ASP.NET Core 6框架揭秘》第13章]

[S1301]数据的加解密(源代码)
[S1302]Purpose字符串一致性(源代码)
[S1303]设置加密内容的有效期(源代码)
[S1304]撤销加密密钥(单个密钥)(源代码)
[S1305]撤销加密密钥(所有密钥)(源代码)
[S1306]瞬时加解密(源代码)
[S1307]密钥哈希(源代码)

[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异常。

1c49ea908d60b62ef94910fbf7c66442.png

图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异常。

11a6b4338f399f385ad46e40917ea9cc.png
图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异常。

0513e8e1301dcc1888e1a3fcd219218f.png
图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异常。

fe5c93fdbb59b5af305b6a204517e816.png
图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所示的方式输出到控制台上。

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

《ASP.NET Core 6框架揭秘》实例演示[01]:编程初体验
《ASP.NET Core 6框架揭秘》实例演示[02]:各种形式的API开发
《ASP.NET Core 6框架揭秘》实例演示[03]:Dapr初体验
《ASP.NET Core 6框架揭秘》实例演示[04]:自定义依赖注入框架
《ASP.NET Core 6框架揭秘》实例演示[05]:依赖注入基本编程模式
《ASP.NET Core 6框架揭秘》实例演示[06]:依赖注入框架设计细节
《ASP.NET Core 6框架揭秘》实例演示[07]:文件系统
《ASP.NET Core 6框架揭秘》实例演示[08]:配置的基本编程模式
《ASP.NET Core 6框架揭秘》实例演示[09]:将配置绑定为对象
《ASP.NET Core 6框架揭秘》实例演示[10]:Options基本编程模式
《ASP.NET Core 6框架揭秘》实例演示[11]:诊断跟踪的几种基本编程方式 
《ASP.NET Core 6框架揭秘》实例演示[13]:日志的基本编程模式
《ASP.NET Core 6框架揭秘》实例演示[14]:日志的进阶用法
《ASP.NET Core 6框架揭秘》实例演示[15]:针对控制台的日志输出
《ASP.NET Core 6框架揭秘》实例演示[16]:内存缓存与分布式缓存的使用
《ASP.NET Core 6框架揭秘》实例演示[17]:利用IHttpClientFactory工厂来创建HttpClient
《ASP.NET Core 6框架揭秘》实例演示[18]:HttpClient处理管道

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

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

相关文章

如何在ABAP Netweaver和CloudFoundry里记录并查看日志

Netweaver 要记录日志需要有一个checkpoint group&#xff0c;可以自行创建也可以使用标准的。这里我重用标准的group&#xff1a;DEMO_CHECKPOINT_GROUP。 tcode SAAB&#xff0c;点Display <->Activate进入编辑模式&#xff0c;将Logpoints设置为"Log"&#…

如何成为有效学习的高手(许岑)——思维导图

总结自许岑精品课《如何成为有效学习的高手》&#xff0c;图片看不清的可以看下面。 最后有彩蛋&#xff01;最后有彩蛋&#xff01;最后有彩蛋&#xff01; 定义 高效学习的定义&#xff1a;找到最适合自己的学习手法&#xff0c;在相对短的时间内集中注意力&#xff0c;以解决…

WPF Canvas 平滑笔迹

WPF Canvas 平滑笔迹控件名&#xff1a;CanvasHandWriting作者&#xff1a;小封&#xff08;邝攀升&#xff09;原文链接&#xff1a; https://github.com/WPFDevelopersOrg/WPFDevelopers编辑&#xff1a;驚鏵完整的思路如下收集路径点集。平均采样路径点集。将路径点集转为…

NetSpeed

NetSpeed公司提供的NOC包括三部分&#xff0c;可以通过NocStudio进行配置生成。 1)NetSpeed Orion&#xff0c;面向快速SoC design的可综合平台。 2)Linley NetSpeed NoC面向复杂的interconnect实现&#xff0c;同时优化内部physical implementation和timing closure. NoC是基于…

js ajax java传参_ajax参数传递与后台接收

ajax参数传递与后台接收Servlet中读取http参数的方法Enumeration getParameterNames() 返回一个 String 对象的枚举&#xff0c;包含在该请求中包含的参数的名称String getParameter(String name) 以字符串形式返回请求参数的值&#xff0c;或者如果参数不存在则返回 null。Str…

init 访问器只能初始化时赋值,是真的吗?

前言C# 提供的 init 关键字用于在属性中定义访问器方法&#xff0c;可以让属性仅能在对象初始化的时候被赋值&#xff0c;其他时候只能为只读属性的形式。例如下面代码可以正常执行&#xff1a;public class Demo {public string Name { get; init; } }var demo new Demo { Na…

eclipse实现代码块折叠-类似于VS中的#region……#endregion

背 景 刚才在写代码的时候&#xff0c;写了十几行可以说是重复的代码&#xff1a; 如果整个方法或类中代码多了&#xff0c;感觉它们太TM占地方了&#xff0c;给读者在阅读代码上造成很大的困难&#xff0c;于是想到能不能把他们“浓缩”成一行&#xff0c;脑子里第一个闪现出的…

java定义基础变量语句_java语言基础-变量

一丶变量的基本概念1.什么是变量(1).内存中的一个存储区域(2).该区域有自己的名称(变量名),和类型(数据类型)(3.)该区域的数据可以在同一类型范围内不断变化(定义变量的主要目的是因为数据的不确定性)2.为什么要定义变量用来不断存放同一类型的常量&#xff0c;并可以重复使用3…

C# WPF MVVM模式[经典]案例

01—前言Caliburn.Micro(简称CM)一经推出便备受推崇&#xff0c;作为一款MVVM开发模式的经典框架&#xff0c;越来越多的受到wpf开发者的青睐.我们看一下官方的描述&#xff1a;Caliburn是一个为Xaml平台设计的小型但功能强大的框架。Micro实现了各种UI模式&#xff0c;用于解决…

shell数组

定义数组[rootwy shell]# a(1 2 3 4)显示数组[rootwy shell]# echo ${a[]}1 2 3 4[rootwy shell]# echo ${a[*]}1 2 3 4显示数组中的某个元素[rootwy shell]# echo ${a[0]}1增加元素[rootwy shell]# a[4]9[rootwy shell]# echo ${a[*]}1 2 3 4 9修改元素值 [rootwy shell]# a[2…

LINUX中常用操作命令

LINUX中常用操作命令 引用&#xff1a;http://www.daniubiji.cn/archives/25 Linux简介及Ubuntu安装 常见指令系统管理命令打包压缩相关命令关机/重启机器Linux管道Linux软件包管理vim使用用户及用户组管理文件权限管理Linux简介及Ubuntu安装 Linux&#xff0c;免费开源&#x…

Log4j编写

来自: http://www.blogjava.net/zJun/archive/2006/06/28/55511.html Log4J的配置文件(Configuration File)就是用来设置记录器的级别、存放器和布局的&#xff0c;它可接keyvalue格式的设置或xml格式的设置信息。通过配置&#xff0c;可以创建出Log4J的运行环境。1. 配置文件L…

C# 为什么高手喜欢用StartsWith而不是Substring进行字符串匹配?

字符串的截取匹配操作在开发中非常常见&#xff0c;比如下面这个示例&#xff1a;我要匹配查找出来字符串数组中以“abc”开头的字符串并打印&#xff0c;我下面分别用了两种方式实现&#xff0c;代码如下&#xff1a;using System;namespace ConsoleApp23 {class Program{stat…

Nginx 服务器开启status页面检测服务状态

原文&#xff1a;http://www.cnblogs.com/hanyifeng/p/5830013.html 一、Nginx status monitor 和apache 中服务器状态一样。输出的内容如&#xff1a; 第1列&#xff1a; 当前与http建立的连接数&#xff0c;包括等待的客户端连接&#xff1a;2第2列&#xff1a;接受的客户端连…

在OpenCloudOS 上安装.NET 6

开源操作系统社区 OpenCloudOS 由腾讯与合作伙伴共同倡议发起&#xff0c;是完全中立、全面开放、安全稳定、高性能的操作系统及生态。OpenCloudOS 沉淀了多家厂商在软件和开源生态的优势&#xff0c;继承了腾讯在操作系统和内核层面超过10年的技术积累&#xff0c;在云原生、稳…

java产生的数字发送到页面_JAVA中数字证书的维护及生成方法

Java中的keytool.exe可以用来创建数字证书&#xff0c;所有的数字证书是以一条一条(采用别名区别)的形式存入证书库的中&#xff0c;证书库中的一条证书包含该条证书的私钥&#xff0c;公钥和对应的数字证书的信息。证书库中的一条证书可以导出数字证书文件&#xff0c;数字证书…

IDEA破解 2017 IDEA license server 激活(可用)

进入ide主页面&#xff0c;help-register-license server,然后输入 http://idea.iteblog.com/key.PHP&#xff08;注意&#xff1a;php要小写&#xff09;即可~ 转载于:https://www.cnblogs.com/austinspark-jessylu/p/7232982.html

《ASP.NET Core 6框架揭秘》实例演示[20]:“数据保护”框架基于文件的密钥存储...

《数据加解密与哈希》演示了“数据保护”框架如何用来对数据进行加解密&#xff0c;而“数据保护”框架的核心是“密钥管理”。数据保护框架以XML的形式来存储密钥&#xff0c;默认的IKeyManager实现类型为XmlKeyManager。接下来我们通过模拟代码和实例演示的形式来介绍一下Xml…

使用msui的回到顶部的一个小问题

2019独角兽企业重金招聘Python工程师标准>>> 回到顶部&#xff0c;一直没反应。 zepto加了动画后&#xff0c;依然如此。原生写法&#xff0c;jquery写法&#xff0c;仍然没有反应。 排查了后&#xff0c;发现获取的对象错误。手机端上&#xff0c;滚动到顶部&#…

口袋精灵加速版java_口袋妖怪TCG!口袋对决加速版教程(免ROOT)

原标题&#xff1a;口袋妖怪TCG&#xff01;口袋对决加速版教程(免ROOT)《口袋对决》是一款以口袋精灵对战为核心玩法的集换式卡牌游戏&#xff0c;原汁原味的口袋画风&#xff0c;丰富多彩的各系精灵&#xff0c;策略竞技的属性相克。在5分钟一局的卡牌对战中&#xff0c;你将…