【问题标题】:Getting the current ASP.NET machine key获取当前的 ASP.NET 机器密钥
【发布时间】:2009-11-18 10:42:27
【问题描述】:

我发现自己想要获取当前应用程序的 ASP.NET 机器密钥。当然,如果在配置文件中指定了机器密钥,这很容易,但如果将其设置为自动生成,则似乎没有任何公共方法可以获取它。

基本上我想要它,这样我就可以为自己编写一个加密/MACed cookie,就像 ASP.NET 表单身份验证提供程序一样。

有人有什么建议或想法吗?

【问题讨论】:

    标签: asp.net machinekey


    【解决方案1】:

    先生。好奇也很想得到机器钥匙。 MachineKeySection 上的属性不好,因为它们会得到 zeroed-out after initialization,这发生在您可以通过反射阅读它们之前。

    在当前的 4.5 框架中进行了一些挖掘后,结果发现自动生成的密钥存储在 HttpApplication.s_autogenKeys 字节数组中。验证密钥是前 64 个字节,后面是解密密钥的 24 个字节。

    如果你没有选择加入 4.5 框架中的新加密内容,也就是说,你没有在你的 web.config 中设置 <httpRuntime targetFramework="4.5">(如果你有一个使用以前版本创建的应用程序就是这种情况框架),然后你得到这样的键:

            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,IsolateAppsIsolateApps 位要求您将应用程序路径哈希的前四个字节复制到密钥的开头。

    如果您选择加入cryptographic improvements in fx4.5,那么您必须在MachineKeyMasterKeyProvider 周围挖掘才能获得有效的密钥。

    在没有 HttpApplication 的情况下获取密钥

    HttpApplication 通过从SetAutogenKeys() 调用webengine4.dll 中的本机方法来获取其密钥。我们也可以自己调用 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 获取密钥

    通过使用internal constructor 实例化MachineKeyMasterKeyProvider,然后传入autogenKeys 字节数组,可以访问新的fx4.5 内容的键,如上面的代码所示。提供者有方法 GetEncryptionKeyGetValidationKey 来获取实际的密钥。

    【讨论】:

    • 当您说应用程序路径时,您是指 IIS 应用程序路径,例如“/”是默认网站?还是您的意思是磁盘上包含程序集的文件夹的绝对路径?
    • @thebringking,您可以使用HttpRuntime.AppDomainAppVirtualPath 获得的 IIS 应用程序路径。
    • 感谢我能够靠近。我正在尝试通过我的 .net 4.5 项目来实现这一点。我能够获取 autogenKeys 的字节,并查看了 MachineMasterKeyProvider 类。您将如何在 .net 4.5 中解决此问题?我正在尝试以与 MachineMasterKeyProvider 相同的方式生成有效的派生密钥。
    • 我应该注意,我的目标是能够提取自动生成密钥或有效的派生,并将其设置在新服务器上,以便现有令牌将验证
    • 我的方法是将 .net framework source 中的相关方法复制到一个新项目中,然后将其缩减到所需的最低加密/解密 cookie 与我使用网络嗅探器获得的相同。跨度>
    【解决方案2】:

    如果您使用的是 .NET 4,则有 MachineKey 类。它不会为您提供对实际密钥的原始访问权限,但它确实提供了使用与 FormsAuthentication 类相同的算法对数据进行编码和解码的方法,以及使用 HMAC 添加验证的选项。

    【讨论】:

      【解决方案3】:

      对于 .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("-", "");
      

      【讨论】:

      • 这对我有用,而不是公认的答案(用例:我需要在 web.config 中硬编码现有的机器密钥,这样用户就不必重新登录应用程序)
      【解决方案4】:

      谢谢好奇先生,

      根据您的指示,我得到了这个:

      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");
      }
      

      【讨论】:

      • 对于 4.5,这是正确的答案。我没有逐字尝试过这段代码,但在看到这个答案之前想出了一个类似的解决方案。我还希望能够在不将它们提交到 web.config 的情况下设置密钥,完整的解决方案在这里:gist.github.com/cmcnab/d2bbed02eb429098ed3656a0729ee40a
      【解决方案5】:

      如果 ASP.NET 表单身份验证提供程序可以访问它,那么您是否尝试过查看 provider source code(我认为这是正确的位置,主题上的ScottGu's original blog post 在更新 MSDN 后链接已断开)

      【讨论】:

      • Forms Auth 提供者可以得到它,因为有内部方法允许它:)
      • 我认为不可能重现这些方法,因为这些内部方法的来源不可用?
      • 它们是可用的,有点,但自动生成的 MAC 密钥的实际后备存储不是,所以即使使用反射器,剪切和粘贴方法,实际密钥本身似乎也无法访问。这让我怀疑我是否错过了什么!
      【解决方案6】:

      我想出了这个作为 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%>"
      />
      

      【讨论】:

        【解决方案7】:

        你真的需要钥匙吗? 还是只是为了加密和解密数据?

        System.Web.Security.FormsAuthentication (.NET 2.0) 具有公共加密/解密方法。 这些使用 System.Web.Configuration.MachineKeySection EncryptOrDecryptData、ByteArrayToHexString 和 HexStringToByteArray 来加密和解密数据。

        EncryptOrDecryptData 根据需要处理从配置文件/AutoGenerate 加载/配置密钥数据。

        Encrypt And Decrypt 应该可以通过源代码下载或反射器获得,并且可以很容易地转换为您的目的。

        【讨论】:

        • 哇,那是很久以前的事了。是的,我确实需要机器密钥。
        • @blowdart:你有没有找到访问密钥的方法?我也在寻找同样的东西。
        【解决方案8】:

        我遇到了同样的问题,需要从正在运行的 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));
        }
        %>
        

        【讨论】:

          【解决方案9】:

          将以下配置信息添加到您的 web.config 文件中。确保将信息替换为您自己的信息。

          <system.web>
          <machineKey validationKey="E4451576F51E0562D91A1748DF7AB3027FEF3C2CCAC46D756C833E1AF20C7BAEFFACF97C7081ADA4648918E0B56BF27D1699A6EB2D9B6967A562CAD14767F163" 
                      decryptionKey="6159C46C9E288028ED26F5A65CED7317A83CB3485DE8C592" validation="HMACSHA256" decryption="AES" />
          </system.web>
          

          validationkey和decryptionkey,validation和decryption应该根据你的服务器和协议而有所不同。

          【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-02-26
          • 2015-05-11
          • 2011-12-28
          • 1970-01-01
          • 2018-01-10
          • 2017-10-01
          相关资源
          最近更新 更多