我发现自己想要获取当前应用程序的 ASP.NET 机器密钥。当然,如果在配置文件中指定了机器密钥,这很容易,但是如果将其设置为自动生成,那么似乎在任何地方都没有公共方法可以获取它。
基本上我想要它,这样我就可以为自己编写一个加密/MACed cookie,就像 ASP.NET Forms Authentication 提供程序一样。
有没有人有任何指示或想法?
我发现自己想要获取当前应用程序的 ASP.NET 机器密钥。当然,如果在配置文件中指定了机器密钥,这很容易,但是如果将其设置为自动生成,那么似乎在任何地方都没有公共方法可以获取它。
基本上我想要它,这样我就可以为自己编写一个加密/MACed cookie,就像 ASP.NET Forms Authentication 提供程序一样。
有没有人有任何指示或想法?
好奇先生也很想拿到机器钥匙。上的属性MachineKeySection
不好,因为它们在初始化之后被清零,这发生在你可以用反射读取它们之前。
在当前的 4.5 框架中进行了一些挖掘之后,事实证明自动生成的密钥存储在HttpApplication.s_autogenKeys
字节数组中。验证密钥是前 64 个字节,后面是解密密钥的 24 个字节。
<httpRuntime targetFramework="4.5">
如果您没有选择加入 4.5 框架中的新加密内容,也就是说,您没有设置web.config
(如果您有使用以前版本的框架创建的应用程序就是这种情况),那么您可以像这样的键:
byte[] autogenKeys = (byte[])typeof(HttpRuntime).GetField("s_autogenKeys", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
int validationKeySize = 64;
int decryptionKeySize = 24;
byte[] validationKey = new byte[validationKeySize];
byte[] decryptionKey = new byte[decryptionKeySize];
Buffer.BlockCopy(autogenKeys, 0, validationKey, 0, validationKeySize);
Buffer.BlockCopy(autogenKeys, validationKeySize, decryptionKey, 0, decryptionKeySize);
// This is the IsolateApps bit, which is set for both keys
int pathHash = StringComparer.InvariantCultureIgnoreCase.GetHashCode(HttpRuntime.AppDomainAppVirtualPath);
validationKey[0] = (byte)(pathHash & 0xff);
validationKey[1] = (byte)((pathHash & 0xff00) >> 8);
validationKey[2] = (byte)((pathHash & 0xff0000) >> 16);
validationKey[3] = (byte)((pathHash & 0xff000000) >> 24);
decryptionKey[0] = (byte)(pathHash & 0xff);
decryptionKey[1] = (byte)((pathHash & 0xff00) >> 8);
decryptionKey[2] = (byte)((pathHash & 0xff0000) >> 16);
decryptionKey[3] = (byte)((pathHash & 0xff000000) >> 24);
两个键的默认值为AutoGenerate,IsolateApps
; 该IsolateApps
位要求您将应用程序路径哈希的前四个字节复制到密钥的开头。
如果您选择了fx4.5 中的加密改进,那么您将不得不挖掘MachineKeyMasterKeyProvider以获取有效密钥。
HttpApplication
通过调用 from 中的本机方法来获取webengine4.dll
其密钥SetAutogenKeys()
。我们也可以自己调用 DLL。我们只需要知道我们的应用程序路径。
假设我们要为根应用程序“ /
”获取自动生成的密钥。
使用 LinqPad:
[DllImport(@"C:\Windows\Microsoft.NET\Framework\v4.0.30319\webengine4.dll")]
internal static extern int EcbCallISAPI(IntPtr pECB, int iFunction, byte[] bufferIn, int sizeIn, byte[] bufferOut, int sizeOut);
void Main()
{
string appPath = "/";
byte[] genKeys = new byte[1024];
byte[] autogenKeys = new byte[1024];
int res = EcbCallISAPI(IntPtr.Zero, 4, genKeys, genKeys.Length, autogenKeys, autogenKeys.Length);
if (res == 1) {
// Same as above
int validationKeySize = 64;
int decryptionKeySize = 24;
byte[] validationKey = new byte[validationKeySize];
byte[] decryptionKey = new byte[decryptionKeySize];
Buffer.BlockCopy(autogenKeys, 0, validationKey, 0, validationKeySize);
Buffer.BlockCopy(autogenKeys, validationKeySize, decryptionKey, 0, decryptionKeySize);
int pathHash = StringComparer.InvariantCultureIgnoreCase.GetHashCode(appPath);
validationKey[0] = (byte)(pathHash & 0xff);
validationKey[1] = (byte)((pathHash & 0xff00) >> 8);
validationKey[2] = (byte)((pathHash & 0xff0000) >> 16);
validationKey[3] = (byte)((pathHash & 0xff000000) >> 24);
decryptionKey[0] = (byte)(pathHash & 0xff);
decryptionKey[1] = (byte)((pathHash & 0xff00) >> 8);
decryptionKey[2] = (byte)((pathHash & 0xff0000) >> 16);
decryptionKey[3] = (byte)((pathHash & 0xff000000) >> 24);
Console.WriteLine("DecryptionKey: {0}", decryptionKey.Aggregate(new StringBuilder(), (acc, c) => acc.AppendFormat("{0:x2}", c), acc => acc.ToString()));
Console.WriteLine("ValidationKey: {0}", validationKey.Aggregate(new StringBuilder(), (acc, c) => acc.AppendFormat("{0:x2}", c), acc => acc.ToString()));
}
}
MachineKeyMasterKeyProvider
可以通过使用内部构造函数实例化 来访问新的 fx4.5 内容的键,然后传入autogenKeys
如上面代码中获得的字节数组。提供者有方法GetEncryptionKey
和GetValidationKey
获取实际密钥。
如果您使用的是 .NET 4,则有MachineKey类。它不会让您对实际密钥进行原始访问,但它确实提供了使用与 FormsAuthentication 类相同的算法对数据进行编码和解码的方法,以及使用 HMAC 添加验证的选项。
对于.Net 4.5,这里是代码
//using System.Reflection
//using System.Web.Configuration
byte[] autogenKeys = (byte[])typeof(HttpRuntime).GetField("s_autogenKeys", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
Type t = typeof(System.Web.Security.DefaultAuthenticationEventArgs).Assembly.GetType("System.Web.Security.Cryptography.MachineKeyMasterKeyProvider");
ConstructorInfo ctor = t.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)[0];
Type ckey = typeof(System.Web.Security.DefaultAuthenticationEventArgs).Assembly.GetType("System.Web.Security.Cryptography.CryptographicKey");
ConstructorInfo ckeyCtor = ckey.GetConstructors(BindingFlags.Instance | BindingFlags.Public)[0];
Object ckeyobj = ckeyCtor.Invoke(new object[] { autogenKeys });
object o = ctor.Invoke(new object[] { new MachineKeySection(), null, null, ckeyobj, null });
var encKey = t.GetMethod("GetEncryptionKey").Invoke(o, null);
byte[] encBytes = ckey.GetMethod("GetKeyMaterial").Invoke(encKey, null) as byte[];
var vldKey = t.GetMethod("GetValidationKey").Invoke(o, null);
byte[] vldBytes = ckey.GetMethod("GetKeyMaterial").Invoke(vldKey, null) as byte[];
string decryptionKey = BitConverter.ToString(encBytes);
decryptionKey = decryptionKey.Replace("-", "");
string validationKey = BitConverter.ToString(vldBytes);
validationKey = validationKey.Replace("-", "");
谢谢好奇先生,
根据您的指示,我得到了这个:
private byte[] _validationKey;
private byte[] _decryptionKey;
public static byte[] GetKey(object provider, string name)
{
var validationKey = provider.GetType().GetMethod(name).Invoke(provider, new object[0]);
return (byte[])validationKey.GetType().GetMethod("GetKeyMaterial").Invoke(validationKey, new object[0]);
}
protected override void OnLoad(EventArgs e)
{
var machineKey = typeof(MachineKeySection).GetMethods(BindingFlags.Static | BindingFlags.NonPublic).Single(a => a.Name == "GetApplicationConfig").Invoke(null, new object[0]);
var type = Assembly.Load("System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a").GetTypes().Single(a => a.Name == "MachineKeyMasterKeyProvider");
var instance = type.Assembly.CreateInstance(
type.FullName, false,
BindingFlags.Instance | BindingFlags.NonPublic,
null, new object[] { machineKey, null, null, null, null }, null, null);
var validationKey = type.GetMethod("GetValidationKey").Invoke(instance, new object[0]);
var key = (byte[])validationKey.GetType().GetMethod("GetKeyMaterial").Invoke(validationKey, new object[0]);
_validationKey = GetKey(instance, "GetValidationKey");
_decryptionKey = GetKey(instance, "GetEncryptionKey");
}
如果 ASP.NET 表单身份验证提供程序可以访问它,那么您是否尝试过查看提供程序源代码?(我认为这是正确的位置,ScottGu 关于该主题的原始博客文章在更新 MSDN 后链接已断开)
我想出了这个作为 4.5 后 .NET 的上述答案的组合。将下面的代码放入名为 mk.aspx 的文件中,然后浏览到它以获取密钥。一定要在之后立即删除它,因为这是邪恶的。
<%@ Import Namespace="System.Reflection" %>
<%@ Import Namespace="System" %>
<%@ Import Namespace="System.Web" %>
<%@ Import Namespace="System.Web.Configuration" %>
<%@ Page Language="C#"%>
<%
byte[] autogenKeys = (byte[])typeof(HttpRuntime).GetField("s_autogenKeys", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
Type t = typeof(System.Web.Security.DefaultAuthenticationEventArgs).Assembly.GetType("System.Web.Security.Cryptography.MachineKeyMasterKeyProvider");
ConstructorInfo ctor = t.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)[0];
Type ckey = typeof(System.Web.Security.DefaultAuthenticationEventArgs).Assembly.GetType("System.Web.Security.Cryptography.CryptographicKey");
ConstructorInfo ckeyCtor = ckey.GetConstructors(BindingFlags.Instance | BindingFlags.Public)[0];
Object ckeyobj = ckeyCtor.Invoke(new object[] { autogenKeys });
object o = ctor.Invoke(new object[] { new MachineKeySection(), null, null, ckeyobj, null });
var encKey = t.GetMethod("GetEncryptionKey").Invoke(o, null);
byte[] encBytes = ckey.GetMethod("GetKeyMaterial").Invoke(encKey, null) as byte[];
var vldKey = t.GetMethod("GetValidationKey").Invoke(o, null);
byte[] vldBytes = ckey.GetMethod("GetKeyMaterial").Invoke(vldKey, null) as byte[];
string decryptionKey = BitConverter.ToString(encBytes);
decryptionKey = decryptionKey.Replace("-", "");
string validationKey = BitConverter.ToString(vldBytes);
validationKey = validationKey.Replace("-", "");
%>
<machineKey
validationKey="<%=validationKey%>"
decryptionKey="<%=decryptionKey%>"
/>
你真的需要钥匙吗?还是只是为了加密和解密数据?
System.Web.Security.FormsAuthentication (.NET 2.0) 具有公共加密/解密方法。这些使用 System.Web.Configuration.MachineKeySection EncryptOrDecryptData、ByteArrayToHexString 和 HexStringToByteArray 来加密和解密数据。
EncryptOrDecryptData 根据需要处理从配置文件/AutoGenerate 加载/配置密钥数据。
Encrypt And Decrypt 应该可以通过源代码下载或反射器获得,并且可以很容易地转换为您的目的。
我遇到了同样的问题,需要从正在运行的 Web 应用程序(不使用 .NET 4.5 加密功能)中获取机器密钥,我无法对其进行代码更改,因此我创建了一个简单的 .aspx 文件来提取密钥并转储它到一个文件,然后将其放在应用程序根目录中并使用浏览器访问它(无需触摸正在运行的应用程序)
<%@ Page Language="C#"
var runTimeType = typeof(System.Web.HttpRuntime);
var autogenKeysField = runTimeType.GetField("s_autogenKeys", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
var autogenKeys = (byte[])autogenKeysField.GetValue(null);
var machineKeySection = new System.Web.Configuration.MachineKeySection();
var autogenKeyProperty = typeof(System.Web.Configuration.MachineKeySection).GetProperty("AutogenKey", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
var decryptionKeyField = typeof(System.Web.Configuration.MachineKeySection).GetField("_DecryptionKey", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
var validationKeyField = typeof(System.Web.Configuration.MachineKeySection).GetField("_ValidationKey", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
// This needs to be done to make machineKeySection refresh it's data
var touch = (bool)autogenKeyProperty.GetValue(machineKeySection);
var decryptionKey = (byte[])decryptionKeyField.GetValue(machineKeySection);
var validationKey = (byte[])validationKeyField.GetValue(machineKeySection);
var autogenKeyString = BitConverter.ToString(autogenKeys).Replace("-", string.Empty);
var encryptionKeyString = BitConverter.ToString(decryptionKey).Replace("-", string.Empty);
var validationKeyString = BitConverter.ToString(validationKey).Replace("-", string.Empty);
using (var writer = new System.IO.StreamWriter("c:/somewhere/withwriteaccess/MachineKey.config")) {
writer.Write(string.Format("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<machineKey decryptionKey=\"{0}\" validationKey=\"{1}\" />", encryptionKeyString, validationKeyString));
}
%>
将以下配置信息添加到您的 web.config 文件中。确保用您自己的信息替换信息。
<system.web>
<machineKey validationKey="E4451576F51E0562D91A1748DF7AB3027FEF3C2CCAC46D756C833E1AF20C7BAEFFACF97C7081ADA4648918E0B56BF27D1699A6EB2D9B6967A562CAD14767F163"
decryptionKey="6159C46C9E288028ED26F5A65CED7317A83CB3485DE8C592" validation="HMACSHA256" decryption="AES" />
</system.web>
验证密钥和解密密钥,验证和解密应根据您的服务器和协议而有所不同。