You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ASP.NET Core中使用解密密钥解密防伪令牌?

可以在ASP.NET Core Web API中间件中解密ASP.NET MVC的防伪令牌吗?

答案是可以。ASP.NET MVC的防伪令牌基于machineKey配置的AES解密和SHA1签名验证逻辑,只要持有对应的decryptionKey和validationKey,就能在ASP.NET Core中手动实现解密和验证逻辑,并集成到中间件中。

具体操作步骤

1. 实现旧版防伪令牌的解密与验证工具类

ASP.NET Core没有原生支持旧ASP.NET的machineKey机制,需要手动复现AES解密和HMACSHA1签名验证逻辑:

using System;
using System.Security.Cryptography;
using System.Text;

public class LegacyAntiForgeryDecoder
{
    private readonly byte[] _decryptionKey;
    private readonly byte[] _validationKey;

    public LegacyAntiForgeryDecoder(string decryptionKey, string validationKey)
    {
        // 转换十六进制密钥为字节数组(如果密钥是Base64格式,改用Convert.FromBase64String)
        _decryptionKey = Convert.FromHexString(decryptionKey);
        _validationKey = Convert.FromHexString(validationKey);
    }

    public string DecryptAndValidateToken(string encryptedToken)
    {
        // ASP.NET MVC防伪令牌格式:[SHA1签名(20字节)][AES加密数据],均为十六进制字符串
        var signatureByteLength = 20;
        var signatureHexLength = signatureByteLength * 2;
        
        if (encryptedToken.Length < signatureHexLength)
            throw new InvalidOperationException("无效的防伪令牌");

        // 分离签名和加密数据
        var signatureHex = encryptedToken.Substring(0, signatureHexLength);
        var encryptedDataHex = encryptedToken.Substring(signatureHexLength);
        var signature = Convert.FromHexString(signatureHex);
        var encryptedData = Convert.FromHexString(encryptedDataHex);

        // 验证签名:用validationKey计算加密数据的HMACSHA1哈希,与令牌中的签名比对
        using var hmac = new HMACSHA1(_validationKey);
        var computedSignature = hmac.ComputeHash(encryptedData);
        if (!CompareByteArrays(signature, computedSignature))
            throw new InvalidOperationException("防伪令牌签名验证失败");

        // AES解密:ASP.NET MVC使用CBC模式,IV是加密数据的前16字节
        if (encryptedData.Length < 16)
            throw new InvalidOperationException("无效的加密数据");

        var iv = new byte[16];
        Array.Copy(encryptedData, 0, iv, 0, 16);
        var cipherText = new byte[encryptedData.Length - 16];
        Array.Copy(encryptedData, 16, cipherText, 0, cipherText.Length);

        using var aes = Aes.Create();
        aes.Key = _decryptionKey;
        aes.IV = iv;
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;

        using var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
        var decryptedBytes = decryptor.TransformFinalBlock(cipherText, 0, cipherText.Length);

        return Encoding.UTF8.GetString(decryptedBytes);
    }

    // 安全的字节数组比对(避免计时攻击)
    private bool CompareByteArrays(byte[] a, byte[] b)
    {
        if (a.Length != b.Length)
            return false;

        var result = 0;
        for (int i = 0; i < a.Length; i++)
        {
            result |= a[i] ^ b[i];
        }
        return result == 0;
    }
}

2. 创建中间件处理请求中的防伪令牌

编写中间件提取请求中的防伪令牌,调用工具类解密验证,并将结果存入HttpContext供后续使用:

using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

public class LegacyAntiForgeryMiddleware
{
    private readonly RequestDelegate _next;
    private readonly LegacyAntiForgeryDecoder _decoder;

    public LegacyAntiForgeryMiddleware(RequestDelegate next, LegacyAntiForgeryDecoder decoder)
    {
        _next = next;
        _decoder = decoder;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // 从Cookie或表单字段读取防伪令牌(根据实际存储位置调整)
        string encryptedToken = null;
        if (context.Request.Cookies.TryGetValue("__RequestVerificationToken", out var cookieToken))
        {
            encryptedToken = cookieToken;
        }
        else if (context.Request.HasFormContentType && context.Request.Form.TryGetValue("__RequestVerificationToken", out var formToken))
        {
            encryptedToken = formToken;
        }

        if (!string.IsNullOrEmpty(encryptedToken))
        {
            try
            {
                var decryptedToken = _decoder.DecryptAndValidateToken(encryptedToken);
                // 将解密后的令牌存入HttpContext.Items,供后续控制器/中间件使用
                context.Items["LegacyDecryptedAntiForgeryToken"] = decryptedToken;
            }
            catch (Exception)
            {
                // 验证失败返回400错误
                context.Response.StatusCode = StatusCodes.Status400BadRequest;
                await context.Response.WriteAsync("无效的防伪令牌");
                return;
            }
        }

        await _next(context);
    }
}

3. 在ASP.NET Core中注册服务与中间件

在Program.cs中注册工具类和中间件,注意从安全配置源读取密钥(避免硬编码):

var builder = WebApplication.CreateBuilder(args);

// 注册LegacyAntiForgeryDecoder,从配置文件或环境变量读取密钥
builder.Services.AddSingleton(new LegacyAntiForgeryDecoder(
    decryptionKey: builder.Configuration["MachineKey:DecryptionKey"],
    validationKey: builder.Configuration["MachineKey:ValidationKey"]
));

var app = builder.Build();

// 注册中间件(注意顺序,建议放在认证中间件之前)
app.UseMiddleware<LegacyAntiForgeryMiddleware>();

// 其他中间件配置...

app.Run();

注意事项

  • 密钥格式确认:如果machineKey中的密钥是Base64编码而非十六进制,需将Convert.FromHexString替换为Convert.FromBase64String。
  • 安全风险:必须验证签名,不能只解密令牌,否则可能遭遇篡改攻击。
  • 密钥存储:不要硬编码密钥,建议使用Azure Key Vault、环境变量或.NET机密管理器存储敏感密钥。

内容的提问来源于stack exchange,提问作者Wasyster

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 19:32:02