从已部署的 Azure 应用服务中提取 MachineKey

2023-12-22

我有一个 ASP.NET 4.6 Web API 服务作为 Azure 应用服务在单个区域的单个应用服务计划中运行。我们正在修改此服务,使其部署在多个区域,并在前面有一个负载均衡器,每个区域都有自己的应用服务计划。因此,我们需要确保在每个应用服务计划上使用相同的计算机密钥,以防止用户在负载均衡器将其定向到不同的服务器时注销。

我们的应用程序已经使用 Azure 在单个应用服务计划上自动提供的机器密钥运行了一段时间。为了避免导致所有客户在过渡期间注销,我计划提取此现有计算机密钥,然后将其部署到其他区域的新应用程序服务计划上。听起来很简单,对吧?

然而提取这个密钥被证明是一个挑战。

我已经尝试过此处列出的解决方案:获取当前 ASP.NET 机器密钥 https://stackoverflow.com/q/1755130/37725

虽然每种方法确实返回某种密钥,但该密钥似乎与实际用于生成不记名令牌或保护刷新令牌票证的密钥不匹配。当我将这些密钥部署到其他服务器时,不记名令牌仍然被视为无效,并且尝试使用现有的刷新令牌会导致invalid_grant回复。

此外,即使我在 web.config 中手动设置机器密钥(或在运行时使用诸如this https://blog.nilayparikh.com/azure/web/set-machinekey-on-azure-web-app/),提取的机器密钥都不与我手动设置的机器密钥匹配,这提供了进一步的证据,表明它们返回的任何内容都不是实际使用的机器密钥。在我的本地开发计算机和 Azure 中都是如此。

作为参考,这是我用来以三种不同方式提取解密和验证密钥的代码(删除了一些安全密码):

[DllImport(@"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\webengine4.dll")]
internal static extern int EcbCallISAPI(IntPtr pECB, int iFunction, byte[] bufferIn, int sizeIn, byte[] bufferOut, int sizeOut);

[Route("machine-key-test")]
public async Task<JObject> GetMachineKeys()
{
    return new JObject(
        new JProperty("A", GetAdminData()),
        new JProperty("B", GetAdminDataNoIsolateApps()),
        new JProperty("C", GetAdminDataPre45()));

    JObject GetAdminData()
    {
        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);

            var decrptionKeyString = decryptionKey.Aggregate(new StringBuilder(), (acc, c) => acc.AppendFormat("{0:x2}", c), acc => acc.ToString());
            var validationKeyString = validationKey.Aggregate(new StringBuilder(), (acc, c) => acc.AppendFormat("{0:x2}", c), acc => acc.ToString());

            return new JObject(
                new JProperty("D", decrptionKeyString),
                new JProperty("V", validationKeyString));
        }


        return null;
    }

    JObject GetAdminDataNoIsolateApps()
    {
        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);

            var decrptionKeyString = decryptionKey.Aggregate(new StringBuilder(), (acc, c) => acc.AppendFormat("{0:x2}", c), acc => acc.ToString());
            var validationKeyString = validationKey.Aggregate(new StringBuilder(), (acc, c) => acc.AppendFormat("{0:x2}", c), acc => acc.ToString());

            return new JObject(
                new JProperty("D", decrptionKeyString),
                new JProperty("V", validationKeyString));
        }


        return null;
    }

    JObject GetAdminDataPre45()
    {
    // https://stackoverflow.com/a/35954339/37725
        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("-", "");

        return new JObject(
            new JProperty("D", decryptionKey),
            new JProperty("V", validationKey));
    }
}

这是我得到的输出的示例:

{
    "A": {
        "D": "b298ba4ef5e8421e178770f50ee5414dd0aa1698afc3169d",
        "V": "b298ba4e3ed466051c60e4c8646ece2546f27e8b9b2e9a569453eaab6b2a4e93bc08a3171ea61972adfd83c97d21bbcfad2acd3e6d35668f5458f8d7c8f55913"
    },
    "B": {
        "D": "dc509c9af5e8421e178770f50ee5414dd0aa1698afc3169d",
        "V": "84246e973ed466051c60e4c8646ece2546f27e8b9b2e9a569453eaab6b2a4e93bc08a3171ea61972adfd83c97d21bbcfad2acd3e6d35668f5458f8d7c8f55913"
    },
    "C": {
        "D": "A2EDFD4ECE75A91F8E38D62B569248B14CE9193DD42E543E0D4BA5C9E2BED912",
        "V": "DC6144A79985DEF712FABC729871A79FF2CF0DD73CBA617C3764D234DA1B63AD"
    }
}

我尝试依次使用这些密钥集,既没有显式设置解密和验证算法,也没有指定与定义的密钥长度相匹配的各种算法组合here https://learn.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/w8h3skw9(v=vs.100),没有运气。

正如我所说,这些密钥都不会与我在我的计算机中手动设置的机器密钥匹配web.config,或者我在代码中手动设置。

我得出的结论是,我要么做了一些微不足道的错误,要么我必须通过将所有服务器上的机器密钥更改为新密钥来强制所有用户注销。我希望有人能够指出我正确的方向。


事实证明我把整个事情过于复杂化了。

如果您使用 Kudu(来自相关应用服务的 Azure 门户中的高级工具部分),那么您可以在以下位置找到一个文件:D:\local\Config\rootweb.config其中包含机器密钥。

我通过回答一个不相关的问题找到了它,所以希望这能为其他人减轻一些痛苦。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从已部署的 Azure 应用服务中提取 MachineKey 的相关文章

随机推荐

  • 如何优化 SQLAlchemy 生成的这个查询?

    我有一个由 SQLAlchemy ORM 生成的查询 它应该检索特定课程的stream items 及其所有部分 资源 内容文本块等 以及发布它们的用户 然而 这个查询似乎非常慢 在我们的生产数据库上花费了几分钟的时间 数据库中有大约 20
  • HBase 单行事务支持中的行锁定

    在 HBase 中 为了提供单行事务支持 它使用行锁定概念 假设 例如 Put p new Put Row Key 该语句将锁定该行 所以 直到我们完成 表 put p 锁不会被释放 所以 在这之间 如果我开始一个新的看跌期权 即 Put
  • Google Colaboratory ImportError:libcublas.so.10.0:无法打开共享对象文件:运行时没有这样的文件或目录

    我正在关注这篇关于 Google Colaboratory 的人工智能农业的文章 https medium com deepquestai ai in agriculture detecting defects in apples b246
  • 如何在 Swift 3 中将文档目录中的图像显示到 UIImageView?

    下面的 Swift 2 示例给出了此错误 String 类型的值没有成员 stringByAppendingPathComponent 对于 Swift 3 我需要更改什么 Apple 正试图让所有人摆脱路径字符串范式 转向 URL 即fi
  • Response.Write() 是否可以与 Razor 一起使用?

    Response Write 是否可以与 Razor 一起使用 我尝试使用 Html RenderAction但我收到错误 CS1502 The best overloaded method match for Microsoft WebP
  • 如何在 WPF 中获取 ListView 的选中行值

    我有一个ListView在 WPF 应用程序中CheckBox 我想将所有选中行的值保存在 WPF 列表中 我怎样才能实现这个目标 我的列表视图
  • 使用自动布局“捏合缩放”

    我遵循了许多在网上找到的 捏合缩放 教程 并阅读了有关自动布局的内容 但我无法让它在我的项目中工作 我的整个项目都使用自动布局 所以我不想将其关闭 我正在将一个巨大的 UIImage 例如 长边 5000px 加载到 UIImageView
  • 在 swagger 中处理多个基本路径

    我正在使用 swagger ui 为我们的客户提供 REST API 的良好文档 在内部 我们有两个不同的 jenkin 构建项目的环境 例如 swagger json 可以在两种环境中访问 http www myhost com xyz
  • 如何用简单的英语解释回调?它们与从一个函数调用另一个函数有何不同?

    如何用简单的英语解释回调 它们与从另一个函数调用一个函数并从调用函数中获取一些上下文有何不同 如何向新手程序员解释它们的威力 我会尽量让这个问题变得简单 回调 是由另一个函数调用的任何函数 该另一个函数将第一个函数作为参数 很多时候 回调
  • Emberjs - 临时禁用属性更改通知

    是否有任何简单的方法可以实现临时禁用一个或多个对象属性的通知 我知道你可以推迟他们beginPropertyChanges and endPropertyChanges 但在我明确启用这些更改之前 我根本不希望收到这些更改的通知 先感谢您
  • 如何按自定义字段日期排序 WordPress 帖子?

    我正在制作一个事件侧边栏部分 仅显示接下来的 3 个事件 我已经让自定义帖子类型和自定义字段全部正常工作 但我似乎可以弄清楚如何按事件的开始日期 这是自定义字段值 对帖子进行排序 有没有一个php函数可以比较日期并将它们组织成一定的顺序 我
  • MeteorJS MongoDB 部署错误

    由于某种原因 当我使用 Meteor 部署时 我的服务器出现以下错误 并且我无法访问这些页面 我遇到以下错误 警告错误 没有可用于查询的副本集主副本 读取首选项主要 我正在使用 Meteor 1 1 0 2 并运行meteor deploy
  • RGB 字节与 HSL 之间的转换?

    有没有RGB转换的算法byte数组到 HSLfloat阵列并再次返回 我已经尝试过找到的那个here https stackoverflow com questions 8838264但它似乎有错误 我使用以下类从 HSL 转换为 RGB
  • 可视化嵌套的 JSON 结构

    考虑这个 JSON 对象 department 1 id 1 name Joe Smith email email protected cdn cgi l email protection id 500 name Bun Sam email
  • 从 using 块内的异常中检测 Dispose()

    我的应用程序中有以下代码 using var database new Database var poll Some database query code foreach Question question in poll Questio
  • 将 Javascript 函数作为参数传递给 C++ 函数

    我用 C 声明我的对象 class Action public QObject Q OBJECT Q PROPERTY QString name READ name public Action QObject 0 QString name
  • Libc共享库如何加载到内存中并在进程之间共享?

    我想了解Libc共享库如何加载到内存中并在进程之间共享 是否有一个 libc 实例加载到内存中并在所有进程之间共享 或者是每个进程的内存中的每个 libc 实例 我不清楚 libc 如何在进程之间共享 谢谢 阿迪亚 libc 的一个实例在所
  • 假镜子。你能帮我解决吗?

    这里是 BFG 9000 每次射击都会摧毁三个相邻的阳台 第 N 个阳台毗邻 第一个 射击后 生存怪物对列昂尼德造成伤害 小说的主要英雄 每个怪物一个单位 进一步后续新拍摄等 直到所有怪物 将会灭亡 需要定义最小损坏量 这可以带走列昂尼德
  • 在 Unity App.Config 文件中包含通用类

    我有一类类型ISimpleCache
  • 从已部署的 Azure 应用服务中提取 MachineKey

    我有一个 ASP NET 4 6 Web API 服务作为 Azure 应用服务在单个区域的单个应用服务计划中运行 我们正在修改此服务 使其部署在多个区域 并在前面有一个负载均衡器 每个区域都有自己的应用服务计划 因此 我们需要确保在每个应