如何在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
相关产品推荐
相关产品推荐

