在.NET Core中使用MachineKey
.NET骚操作 人气:2在.NET Core中使用MachineKey
姐妹篇:《ASP.NET Cookie是怎么生成的》
姐妹篇:《.NET Core验证ASP.NET密码》
在上篇文章中,我介绍了Cookie
是基于MachineKey
生成的,MachineKey
决定了Cookie
生成的算法和密钥,并如果使用多台服务器做负载均衡时,必须指定一致的MachineKey
。
但在.NET Core
中,官方似乎并没有提供MachineKey
实现,这为兼容.NET Framework
的Cookie
造成了许多障碍。
今天我将深入探索MachineKey
这个类,看看里面到底藏了什么东西,本文的最后我将使用.NET Core
来解密一个ASP.NET MVC
生成的Cookie
。
认识MachineKey
在.NET Framework
中,machineKey
首先需要一个配置,写在app.config
或者web.config
中,格式一般如下:
<machineKey validationKey="128个hex字符" decryptionKey="64个hex字符" validation="SHA1" decryption="AES" />
网上能找到可以直接生成随机
MachineKey
的网站:https://www.developerfusion.com/tools/generatemachinekey/但
MachineKey
的validationKey
和decryptionKey
的内容只要符合长度和hex
要求,都是可以随意指定的,所以machineKey
生成器的意义其实不大。
探索MachineKey
打开MachineKey
的源代码如下所示(有删减):
public static class MachineKey {
public static byte[] Unprotect(byte[] protectedData, params string[] purposes) {
// ...有删减
return Unprotect(AspNetCryptoServiceProvider.Instance, protectedData, purposes);
}
// Internal method for unit testing.
internal static byte[] Unprotect(ICryptoServiceProvider cryptoServiceProvider, byte[] protectedData, string[] purposes) {
// If the user is calling this method, we want to use the ICryptoServiceProvider
// regardless of whether or not it's the default provider.
Purpose derivedPurpose = Purpose.User_MachineKey_Protect.AppendSpecificPurposes(purposes);
ICryptoService cryptoService = cryptoServiceProvider.GetCryptoService(derivedPurpose);
return cryptoService.Unprotect(protectedData);
}
}
具体代码可参见:https://referencesource.microsoft.com/#system.web/Security/MachineKey.cs,209
可见它本质是使用了AspNetCryptoServiceProvider.Instance
,然后调用其GetCryptoService
方法,然后获取一个cryptoService
,最后调用Unprotect
,注意其中还使用了一个Purpose
的类,依赖非常多。
AspNetCryptoServiceProvider
其中AspNetCryptoServiceProvider.Instance
的定义如下(有删减和整合):
internal sealed class AspNetCryptoServiceProvider : ICryptoServiceProvider {
private static readonly Lazy<AspNetCryptoServiceProvider> _singleton = new Lazy<AspNetCryptoServiceProvider>(GetSingletonCryptoServiceProvider);
internal static AspNetCryptoServiceProvider Instance {
get {
return _singleton.Value;
}
}
private static AspNetCryptoServiceProvider GetSingletonCryptoServiceProvider() {
// Provides all of the necessary dependencies for an application-level
// AspNetCryptoServiceProvider.
MachineKeySection machineKeySection = MachineKeySection.GetApplicationConfig();
return new AspNetCryptoServiceProvider(
machineKeySection: machineKeySection,
cryptoAlgorithmFactory: new MachineKeyCryptoAlgorithmFactory(machineKeySection),
masterKeyProvider: new MachineKeyMasterKeyProvider(machineKeySection),
dataProtectorFactory: new MachineKeyDataProtectorFactory(machineKeySection),
keyDerivationFunction: SP800_108.DeriveKey);
}
}
具体代码可见:https://referencesource.microsoft.com/#system.web/Security/Cryptography/AspNetCryptoServiceProvider.cs,68dbd1c184ea4e88
可见它本质是依赖于AspNetCryptoServiceProvider
,它使用了MachineKeyCryptoAlgorithmFactory
、MachineKeyMasterKeyProvider
、MachineKeyDataProtectorFactory
,以及一个看上去有点奇怪的SP800_108.DeriveKey
。
AspNetCryptoServiceProvider
的GetCryptoService
方法如下:
public ICryptoService GetCryptoService(Purpose purpose, CryptoServiceOptions options = CryptoServiceOptions.None) {
ICryptoService cryptoService;
if (_isDataProtectorEnabled && options == CryptoServiceOptions.None) {
// We can only use DataProtector if it's configured and the caller didn't ask for any special behavior like cacheability
cryptoService = GetDataProtectorCryptoService(purpose);
}
else {
// Otherwise we fall back to using the <machineKey> algorithms for cryptography
cryptoService = GetNetFXCryptoService(purpose, options);
}
// always homogenize errors returned from the crypto service
return new HomogenizingCryptoServiceWrapper(cryptoService);
}
private NetFXCryptoService GetNetFXCryptoService(Purpose purpose, CryptoServiceOptions options) {
// Extract the encryption and validation keys from the provided Purpose object
CryptographicKey encryptionKey = purpose.GetDerivedEncryptionKey(_masterKeyProvider, _keyDerivationFunction);
CryptographicKey validationKey = purpose.GetDerivedValidationKey(_masterKeyProvider, _keyDerivationFunction);
// and return the ICryptoService
// (predictable IV turned on if the caller requested cacheable output)
return new NetFXCryptoService(_cryptoAlgorithmFactory, encryptionKey, validationKey, predictableIV: (options == CryptoServiceOptions.CacheableOutput));
}
注意其中有一个判断,我结合dnSpy
做了认真的调试,发现它默认走的是GetNetFXCryptoService
,也就是注释中所谓的<machineKey>
算法。
然后GetNetFXCryptoService
方法依赖于_masterKeyProvider
和_keyDerivationFunction
用来生成两个CryptographicKey
,这两个就是之前所说的MachineKeyMasterKeyProvider
和MachineKeyDataProtectorFactory
。
注意其中还有一个HomogenizingCryptoServiceWrapper
类,故名思义,它的作用应该是统一管理加密解释过程中的报错,实际也确实如此,我不作深入,有兴趣的读者可以看看原始代码在这:https://referencesource.microsoft.com/#system.web/Security/Cryptography/HomogenizingCryptoServiceWrapper.cs,25
最后调用NetFXCryptoService
来执行Unprotect
任务。
NetFXCryptoService
这个是重点了,源代码如下(有删减):
internal sealed class NetFXCryptoService : ICryptoService {
private readonly ICryptoAlgorithmFactory _cryptoAlgorithmFactory;
private readonly CryptographicKey _encryptionKey;
private readonly bool _predictableIV;
private readonly CryptographicKey _validationKey;
// ...有删减
// [UNPROTECT]
// INPUT: protectedData
// OUTPUT: clearData
// ALGORITHM:
// 1) Assume protectedData := IV || Enc(Kenc, IV, clearData) || Sign(Kval, IV || Enc(Kenc, IV, clearData))
// 2) Validate the signature over the payload and strip it from the end
// 3) Strip off the IV from the beginning of the payload
// 4) Decrypt what remains of the payload, and return it as clearData
public byte[] Unprotect(byte[] protectedData) {
// ...有删减
using (SymmetricAlgorithm decryptionAlgorithm = _cryptoAlgorithmFactory.GetEncryptionAlgorithm()) {
// 省略约100行代码
加载全部内容
- 猜你喜欢
- 用户评论