让我们考虑一个众所周知的ASP.NET Core场景。首先,我们添加中间件:
public void Configure(IApplicationBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationScheme = "MyCookie",
CookieName = "MyCookie",
LoginPath = new PathString("/Home/Login/"),
AccessDeniedPath = new PathString("/Home/AccessDenied/"),
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
//...
}
然后序列化一个校长:
await HttpContext.Authentication.SignInAsync("MyCookie", principal);
在这两次调用之后,加密的cookie将存储在客户端。你可以在任何浏览器devtools中看到cookie(在我的例子中它是分块的):
使用应用程序代码中的cookie不是问题(而不是问题)。
我的问题是:如何在应用程序之外解密cookie ?我想需要一个私钥,如何获得它?
我检查了docs,发现只有常用词:
这将创建一个加密的cookie并将其添加到当前 响应。必须在配置期间指定AuthenticationScheme 也可在调用SignInAsync时使用。
使用的加密是ASP.NET的数据保护 系统。如果您在多台计算机上托管,负载平衡或 使用Web场,您需要配置数据保护 使用相同的密钥环和应用程序标识符。
那么,是否有可能解密身份验证cookie,如果是这样的话?
更新#1: 基于Ron C great answer and comments,我最终得到了代码:
public class Startup
{
//constructor is omitted...
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection().PersistKeysToFileSystem(
new DirectoryInfo(@"C:\temp-keys\"));
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationScheme = "MyCookie",
CookieName = "MyCookie",
LoginPath = new PathString("/Home/Index/"),
AccessDeniedPath = new PathString("/Home/AccessDenied/"),
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
}
public class HomeController : Controller
{
public async Task<IActionResult> Index()
{
await HttpContext.Authentication.SignInAsync("MyCookie", new ClaimsPrincipal());
return View();
}
public IActionResult DecryptCookie()
{
var provider = DataProtectionProvider.Create(new DirectoryInfo(@"C:\temp-keys\"));
string cookieValue = HttpContext.Request.Cookies["MyCookie"];
var dataProtector = provider.CreateProtector(
typeof(CookieAuthenticationMiddleware).FullName, "MyCookie", "v2");
UTF8Encoding specialUtf8Encoding = new UTF8Encoding(false, true);
byte[] protectedBytes = Base64UrlTextEncoder.Decode(cookieValue);
byte[] plainBytes = dataProtector.Unprotect(protectedBytes);
string plainText = specialUtf8Encoding.GetString(plainBytes);
return Content(plainText);
}
}
不幸的是,这段代码总是在Unprotect
方法调用上产生异常:
Microsoft.AspNetCore.DataProtection.dll中的CryptographicException: 附加信息:有效负载无效。
我在几台机器上测试了此代码的不同变体而没有正面结果。可能我犯了一个错误,但在哪里?
更新#2:我的错误是DataProtectionProvider
尚未在UseCookieAuthentication
中设置。再次感谢@RonC。
答案 0 :(得分:22)
值得注意的是,您无需访问密钥即可解密身份验证Cookie。您只需使用使用正确目的参数创建的右IDataProtector
和子目的参数。
根据CookieAuthenticationMiddleware
源代码https://github.com/aspnet/Security/blob/rel/1.1.1/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationMiddleware.cs#L4,您需要传递的目的是typeof(CookieAuthenticationMiddleware)
。由于他们将其他参数传递给IDataProtector
,您需要匹配它们。因此,这行代码应该为您提供可用于解密身份验证cookie的IDataProtector
:
var dataProtector = provider.CreateProtector(typeof(CookieAuthenticationMiddleware).FullName, Options.AuthenticationScheme, "v2");
请注意,Options.AuthenticationScheme
在这种情况下只是“MyCookie”,因为它是在startup.cs文件的Configure
方法中设置的。
以下是两种不同方式解密身份验证Cookie的示例操作方法:
public IActionResult DecryptCookie() {
//Get the encrypted cookie value
string cookieValue = HttpContext.Request.Cookies["MyCookie"];
//Get a data protector to use with either approach
var dataProtector = provider.CreateProtector(typeof(CookieAuthenticationMiddleware).FullName, "MyCookie", "v2");
//Get the decrypted cookie as plain text
UTF8Encoding specialUtf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
byte[] protectedBytes = Base64UrlTextEncoder.Decode(cookieValue);
byte[] plainBytes = dataProtector.Unprotect(protectedBytes);
string plainText = specialUtf8Encoding.GetString(plainBytes);
//Get the decrypted cookie as a Authentication Ticket
TicketDataFormat ticketDataFormat = new TicketDataFormat(dataProtector);
AuthenticationTicket ticket = ticketDataFormat.Unprotect(cookieValue);
return View();
}
此方法使用注释为构造函数的IDataProtectionProvider
provider
。
如果要在应用程序之间共享cookie,则可能决定将数据保护密钥保留在目录中。这可以通过将以下内容添加到startup.cs文件的ConfigureServices
方法来完成:
services.AddDataProtection().PersistKeysToFileSystem(
new DirectoryInfo(@"C:\temp-keys\"));
要小心但因为密钥没有加密所以由你来保护它们!如果你绝对必须,只将密钥保存到目录中(或者如果你只是想了解系统的工作原理)。您还需要指定使用这些密钥的cookie DataProtectionProvider
。这可以通过startup.cs类的UseCookieAuthentication
方法中的Configure
配置来完成,如下所示:
app.UseCookieAuthentication(new CookieAuthenticationOptions() {
DataProtectionProvider = DataProtectionProvider.Create(new DirectoryInfo(@"C:\temp-keys\")),
AuthenticationScheme = "MyCookie",
CookieName = "MyCookie",
LoginPath = new PathString("/Home/Login"),
AccessDeniedPath = new PathString("/Home/AccessDenied"),
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
完成配置。您现在可以使用以下代码解密身份验证cookie:
public IActionResult DecryptCookie() {
ViewData["Message"] = "This is the decrypt page";
var user = HttpContext.User; //User will be set to the ClaimsPrincipal
//Get the encrypted cookie value
string cookieValue = HttpContext.Request.Cookies["MyCookie"];
var provider = DataProtectionProvider.Create(new DirectoryInfo(@"C:\temp-keys\"));
//Get a data protector to use with either approach
var dataProtector = provider.CreateProtector(typeof(CookieAuthenticationMiddleware).FullName, "MyCookie", "v2");
//Get the decrypted cookie as plain text
UTF8Encoding specialUtf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
byte[] protectedBytes = Base64UrlTextEncoder.Decode(cookieValue);
byte[] plainBytes = dataProtector.Unprotect(protectedBytes);
string plainText = specialUtf8Encoding.GetString(plainBytes);
//Get teh decrypted cookies as a Authentication Ticket
TicketDataFormat ticketDataFormat = new TicketDataFormat(dataProtector);
AuthenticationTicket ticket = ticketDataFormat.Unprotect(cookieValue);
return View();
}
您可以在此处详细了解后一种情况:https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/compatibility/cookie-sharing
答案 1 :(得分:6)
请参阅下面的.NET Core 2帮助程序方法以从Cookie中获取声明:
private IEnumerable<Claim> GetClaimFromCookie(HttpContext httpContext, string cookieName, string cookieSchema)
{
// Get the encrypted cookie value
var opt = httpContext.RequestServices.GetRequiredService<IOptionsMonitor<CookieAuthenticationOptions>>();
var cookie = opt.CurrentValue.CookieManager.GetRequestCookie(httpContext, cookieName);
// Decrypt if found
if (!string.IsNullOrEmpty(cookie))
{
var dataProtector = opt.CurrentValue.DataProtectionProvider.CreateProtector("Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", cookieSchema, "v2");
var ticketDataFormat = new TicketDataFormat(dataProtector);
var ticket = ticketDataFormat.Unprotect(cookie);
return ticket.Principal.Claims;
}
return null;
}
正如@Cirem所指出的那样,创建保护程序的狡猾方法正是Microsoft的操作方式(请参阅their code here)。因此,它可能会在将来的版本中更改。
答案 2 :(得分:1)
ASP.NET Core 2.2的另一个变体:
var cookieManager = new ChunkingCookieManager();
var cookie = cookieManager.GetRequestCookie(HttpContext, ".AspNetCore.Identity.Application");
var dataProtector = dataProtectionProvider.CreateProtector("Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", "Identity.Application", "v2");
//Get the decrypted cookie as plain text
UTF8Encoding specialUtf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
byte[] protectedBytes = Base64UrlTextEncoder.Decode(cookie);
byte[] plainBytes = dataProtector.Unprotect(protectedBytes);
string plainText = specialUtf8Encoding.GetString(plainBytes);
//Get teh decrypted cookies as a Authentication Ticket
TicketDataFormat ticketDataFormat = new TicketDataFormat(dataProtector);
AuthenticationTicket ticket = ticketDataFormat.Unprotect(cookie);
答案 3 :(得分:0)
我刚刚在Classic ASP.net(4.6.1)中工作了。请注意以下必需的安装:
Microsoft.Owin.Security.Interop
(将带有一堆依赖项-注意,由于异常,我使用版本3.0.1,但这可能不是必需的。)Microsfot.AspNetCore.DataProtection
(将带有很多依赖项)框架定义了以下常量:
PROVIDER_NAME = "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware"
SCHEME_NAME = "Identity.Application"
COOKIE_NAME = ".AspNetCore.Identity.Application"
(可以自定义)以下常量是特定于配置的,但是在应用程序之间必须相同。
APP_NAME = "Auth.Test.App"
SHARED_KEY_DIR = "C:\\app-keyring"
过程:
This article有助于在双方进行此设置,特别是在正确配置.Net Core方面。因此,我们将其留给读者练习。
设置完这些后,在 4.6.1解密端上,下面的代码将产生ClaimsIdentity
在(例如).Net Core 3.0中设置的值:
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Owin.Security.Interop;
using System.IO;
using System.Security.Claims;
using System.Web;
public static ClaimsIdentity GetClaimsIdentity(HttpContext context)
{
//Get the encrypted cookie value
var cookie = context.Request.Cookies[Constants.COOKIE_NAME];
if (cookie == null) {
return null;
}
var cookieValue = cookie.Value;
//Get a data protector to use with either approach
var keysDir = new DirectoryInfo(Constants.SHARED_KEY_DIR);
if (!keysDir.Exists) { keysDir.Create(); }
var provider = DataProtectionProvider.Create(keysDir,
options => options.SetApplicationName(Constants.APP_NAME));
var dataProtector = provider.CreateProtector(Constants.PROVIDER_NAME, Constants.SCHEME_NAME, "v2");
//Get the decrypted cookie as a Authentication Ticket
var shim = new DataProtectorShim(dataProtector);
var ticketDataFormat = new AspNetTicketDataFormat(shim);
var ticket = ticketDataFormat.Unprotect(cookieValue);
return ticket.Identity;
}