mirror of
https://github.com/bitwarden/server.git
synced 2025-07-03 09:02:48 -05:00
[PM-1188] Server owner auth migration (#2825)
* [PM-1188] add sso project to auth * [PM-1188] move sso api models to auth * [PM-1188] fix sso api model namespace & imports * [PM-1188] move core files to auth * [PM-1188] fix core sso namespace & models * [PM-1188] move sso repository files to auth * [PM-1188] fix sso repo files namespace & imports * [PM-1188] move sso sql files to auth folder * [PM-1188] move sso test files to auth folders * [PM-1188] fix sso tests namespace & imports * [PM-1188] move auth api files to auth folder * [PM-1188] fix auth api files namespace & imports * [PM-1188] move auth core files to auth folder * [PM-1188] fix auth core files namespace & imports * [PM-1188] move auth email templates to auth folder * [PM-1188] move auth email folder back into shared directory * [PM-1188] fix auth email names * [PM-1188] move auth core models to auth folder * [PM-1188] fix auth model namespace & imports * [PM-1188] add entire Identity project to auth codeowners * [PM-1188] fix auth orm files namespace & imports * [PM-1188] move auth orm files to auth folder * [PM-1188] move auth sql files to auth folder * [PM-1188] move auth tests to auth folder * [PM-1188] fix auth test files namespace & imports * [PM-1188] move emergency access api files to auth folder * [PM-1188] fix emergencyaccess api files namespace & imports * [PM-1188] move emergency access core files to auth folder * [PM-1188] fix emergency access core files namespace & imports * [PM-1188] move emergency access orm files to auth folder * [PM-1188] fix emergency access orm files namespace & imports * [PM-1188] move emergency access sql files to auth folder * [PM-1188] move emergencyaccess test files to auth folder * [PM-1188] fix emergency access test files namespace & imports * [PM-1188] move captcha files to auth folder * [PM-1188] fix captcha files namespace & imports * [PM-1188] move auth admin files into auth folder * [PM-1188] fix admin auth files namespace & imports - configure mvc to look in auth folders for views * [PM-1188] remove extra imports and formatting * [PM-1188] fix ef auth model imports * [PM-1188] fix DatabaseContextModelSnapshot paths * [PM-1188] fix grant import in ef * [PM-1188] update sqlproj * [PM-1188] move missed sqlproj files * [PM-1188] move auth ef models out of auth folder * [PM-1188] fix auth ef models namespace * [PM-1188] remove auth ef models unused imports * [PM-1188] fix imports for auth ef models * [PM-1188] fix more ef model imports * [PM-1188] fix file encodings
This commit is contained in:
@ -1,36 +0,0 @@
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Bit.Core.Utilities;
|
||||
|
||||
public class CaptchaProtectedAttribute : ActionFilterAttribute
|
||||
{
|
||||
public string ModelParameterName { get; set; } = "model";
|
||||
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
var currentContext = context.HttpContext.RequestServices.GetRequiredService<ICurrentContext>();
|
||||
var captchaValidationService = context.HttpContext.RequestServices.GetRequiredService<ICaptchaValidationService>();
|
||||
|
||||
if (captchaValidationService.RequireCaptchaValidation(currentContext, null))
|
||||
{
|
||||
var captchaResponse = (context.ActionArguments[ModelParameterName] as ICaptchaProtectedModel)?.CaptchaResponse;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(captchaResponse))
|
||||
{
|
||||
throw new BadRequestException(captchaValidationService.SiteKeyResponseKeyName, captchaValidationService.SiteKey);
|
||||
}
|
||||
|
||||
var captchaValidationResponse = captchaValidationService.ValidateCaptchaResponseAsync(captchaResponse,
|
||||
currentContext.IpAddress, null).GetAwaiter().GetResult();
|
||||
if (!captchaValidationResponse.Success || captchaValidationResponse.IsBot)
|
||||
{
|
||||
throw new BadRequestException("Captcha is invalid. Please refresh and try again");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ using Azure;
|
||||
using Azure.Storage.Blobs;
|
||||
using Azure.Storage.Blobs.Models;
|
||||
using Azure.Storage.Queues.Models;
|
||||
using Bit.Core.Auth.Enums;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
|
@ -1,277 +0,0 @@
|
||||
/*
|
||||
Original source modified from https://github.com/duosecurity/duo_api_csharp
|
||||
|
||||
=============================================================================
|
||||
=============================================================================
|
||||
|
||||
Copyright (c) 2018 Duo Security
|
||||
All rights reserved
|
||||
*/
|
||||
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using Bit.Core.Models.Api.Response.Duo;
|
||||
|
||||
namespace Bit.Core.Utilities;
|
||||
|
||||
public class DuoApi
|
||||
{
|
||||
private const string UrlScheme = "https";
|
||||
private const string UserAgent = "Bitwarden_DuoAPICSharp/1.0 (.NET Core)";
|
||||
|
||||
private readonly string _host;
|
||||
private readonly string _ikey;
|
||||
private readonly string _skey;
|
||||
|
||||
private readonly HttpClient _httpClient = new();
|
||||
|
||||
public DuoApi(string ikey, string skey, string host)
|
||||
{
|
||||
_ikey = ikey;
|
||||
_skey = skey;
|
||||
_host = host;
|
||||
|
||||
if (!ValidHost(host))
|
||||
{
|
||||
throw new DuoException("Invalid Duo host configured.", new ArgumentException(nameof(host)));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ValidHost(string host)
|
||||
{
|
||||
if (Uri.TryCreate($"https://{host}", UriKind.Absolute, out var uri))
|
||||
{
|
||||
return (string.IsNullOrWhiteSpace(uri.PathAndQuery) || uri.PathAndQuery == "/") &&
|
||||
uri.Host.StartsWith("api-") &&
|
||||
(uri.Host.EndsWith(".duosecurity.com") || uri.Host.EndsWith(".duofederal.com"));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string CanonicalizeParams(Dictionary<string, string> parameters)
|
||||
{
|
||||
var ret = new List<string>();
|
||||
foreach (var pair in parameters)
|
||||
{
|
||||
var p = string.Format("{0}={1}", HttpUtility.UrlEncode(pair.Key), HttpUtility.UrlEncode(pair.Value));
|
||||
// Signatures require upper-case hex digits.
|
||||
p = Regex.Replace(p, "(%[0-9A-Fa-f][0-9A-Fa-f])", c => c.Value.ToUpperInvariant());
|
||||
// Escape only the expected characters.
|
||||
p = Regex.Replace(p, "([!'()*])", c => "%" + Convert.ToByte(c.Value[0]).ToString("X"));
|
||||
p = p.Replace("%7E", "~");
|
||||
// UrlEncode converts space (" ") to "+". The
|
||||
// signature algorithm requires "%20" instead. Actual
|
||||
// + has already been replaced with %2B.
|
||||
p = p.Replace("+", "%20");
|
||||
ret.Add(p);
|
||||
}
|
||||
|
||||
ret.Sort(StringComparer.Ordinal);
|
||||
return string.Join("&", ret.ToArray());
|
||||
}
|
||||
|
||||
protected string CanonicalizeRequest(string method, string path, string canonParams, string date)
|
||||
{
|
||||
string[] lines = {
|
||||
date,
|
||||
method.ToUpperInvariant(),
|
||||
_host.ToLower(),
|
||||
path,
|
||||
canonParams,
|
||||
};
|
||||
return string.Join("\n", lines);
|
||||
}
|
||||
|
||||
public string Sign(string method, string path, string canonParams, string date)
|
||||
{
|
||||
var canon = CanonicalizeRequest(method, path, canonParams, date);
|
||||
var sig = HmacSign(canon);
|
||||
var auth = string.Concat(_ikey, ':', sig);
|
||||
return string.Concat("Basic ", Encode64(auth));
|
||||
}
|
||||
|
||||
/// <param name="timeout">The request timeout, in milliseconds.
|
||||
/// Specify 0 to use the system-default timeout. Use caution if
|
||||
/// you choose to specify a custom timeout - some API
|
||||
/// calls (particularly in the Auth APIs) will not
|
||||
/// return a response until an out-of-band authentication process
|
||||
/// has completed. In some cases, this may take as much as a
|
||||
/// small number of minutes.</param>
|
||||
private async Task<(string result, HttpStatusCode statusCode)> ApiCall(string method, string path, Dictionary<string, string> parameters, int timeout)
|
||||
{
|
||||
if (parameters == null)
|
||||
{
|
||||
parameters = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
var canonParams = CanonicalizeParams(parameters);
|
||||
var query = string.Empty;
|
||||
if (!method.Equals("POST") && !method.Equals("PUT"))
|
||||
{
|
||||
if (parameters.Count > 0)
|
||||
{
|
||||
query = "?" + canonParams;
|
||||
}
|
||||
}
|
||||
var url = $"{UrlScheme}://{_host}{path}{query}";
|
||||
|
||||
var dateString = RFC822UtcNow();
|
||||
var auth = Sign(method, path, canonParams, dateString);
|
||||
|
||||
var request = new HttpRequestMessage
|
||||
{
|
||||
Method = new HttpMethod(method),
|
||||
RequestUri = new Uri(url),
|
||||
};
|
||||
request.Headers.Add("Authorization", auth);
|
||||
request.Headers.Add("X-Duo-Date", dateString);
|
||||
request.Headers.UserAgent.ParseAdd(UserAgent);
|
||||
|
||||
if (timeout > 0)
|
||||
{
|
||||
_httpClient.Timeout = TimeSpan.FromMilliseconds(timeout);
|
||||
}
|
||||
|
||||
if (method.Equals("POST") || method.Equals("PUT"))
|
||||
{
|
||||
request.Content = new StringContent(canonParams, Encoding.UTF8, "application/x-www-form-urlencoded");
|
||||
}
|
||||
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
var statusCode = response.StatusCode;
|
||||
return (result, statusCode);
|
||||
}
|
||||
|
||||
public async Task<Response> JSONApiCall(string method, string path, Dictionary<string, string> parameters = null)
|
||||
{
|
||||
return await JSONApiCall(method, path, parameters, 0);
|
||||
}
|
||||
|
||||
/// <param name="timeout">The request timeout, in milliseconds.
|
||||
/// Specify 0 to use the system-default timeout. Use caution if
|
||||
/// you choose to specify a custom timeout - some API
|
||||
/// calls (particularly in the Auth APIs) will not
|
||||
/// return a response until an out-of-band authentication process
|
||||
/// has completed. In some cases, this may take as much as a
|
||||
/// small number of minutes.</param>
|
||||
private async Task<Response> JSONApiCall(string method, string path, Dictionary<string, string> parameters, int timeout)
|
||||
{
|
||||
var (res, statusCode) = await ApiCall(method, path, parameters, timeout);
|
||||
try
|
||||
{
|
||||
var obj = JsonSerializer.Deserialize<DuoResponseModel>(res);
|
||||
if (obj.Stat == "OK")
|
||||
{
|
||||
return obj.Response;
|
||||
}
|
||||
|
||||
throw new ApiException(obj.Code ?? 0, (int)statusCode, obj.Message, obj.MessageDetail);
|
||||
}
|
||||
catch (ApiException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new BadResponseException((int)statusCode, e);
|
||||
}
|
||||
}
|
||||
|
||||
private int? ToNullableInt(string s)
|
||||
{
|
||||
int i;
|
||||
if (int.TryParse(s, out i))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private string HmacSign(string data)
|
||||
{
|
||||
var keyBytes = Encoding.ASCII.GetBytes(_skey);
|
||||
var dataBytes = Encoding.ASCII.GetBytes(data);
|
||||
|
||||
using (var hmac = new HMACSHA1(keyBytes))
|
||||
{
|
||||
var hash = hmac.ComputeHash(dataBytes);
|
||||
var hex = BitConverter.ToString(hash);
|
||||
return hex.Replace("-", string.Empty).ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
private static string Encode64(string plaintext)
|
||||
{
|
||||
var plaintextBytes = Encoding.ASCII.GetBytes(plaintext);
|
||||
return Convert.ToBase64String(plaintextBytes);
|
||||
}
|
||||
|
||||
private static string RFC822UtcNow()
|
||||
{
|
||||
// Can't use the "zzzz" format because it adds a ":"
|
||||
// between the offset's hours and minutes.
|
||||
var dateString = DateTime.UtcNow.ToString("ddd, dd MMM yyyy HH:mm:ss", CultureInfo.InvariantCulture);
|
||||
var offset = 0;
|
||||
var zone = "+" + offset.ToString(CultureInfo.InvariantCulture).PadLeft(2, '0');
|
||||
dateString += " " + zone.PadRight(5, '0');
|
||||
return dateString;
|
||||
}
|
||||
}
|
||||
|
||||
public class DuoException : Exception
|
||||
{
|
||||
public int HttpStatus { get; private set; }
|
||||
|
||||
public DuoException(string message, Exception inner)
|
||||
: base(message, inner)
|
||||
{ }
|
||||
|
||||
public DuoException(int httpStatus, string message, Exception inner)
|
||||
: base(message, inner)
|
||||
{
|
||||
HttpStatus = httpStatus;
|
||||
}
|
||||
}
|
||||
|
||||
public class ApiException : DuoException
|
||||
{
|
||||
public int Code { get; private set; }
|
||||
public string ApiMessage { get; private set; }
|
||||
public string ApiMessageDetail { get; private set; }
|
||||
|
||||
public ApiException(int code, int httpStatus, string apiMessage, string apiMessageDetail)
|
||||
: base(httpStatus, FormatMessage(code, apiMessage, apiMessageDetail), null)
|
||||
{
|
||||
Code = code;
|
||||
ApiMessage = apiMessage;
|
||||
ApiMessageDetail = apiMessageDetail;
|
||||
}
|
||||
|
||||
private static string FormatMessage(int code, string apiMessage, string apiMessageDetail)
|
||||
{
|
||||
return string.Format("Duo API Error {0}: '{1}' ('{2}')", code, apiMessage, apiMessageDetail);
|
||||
}
|
||||
}
|
||||
|
||||
public class BadResponseException : DuoException
|
||||
{
|
||||
public BadResponseException(int httpStatus, Exception inner)
|
||||
: base(httpStatus, FormatMessage(httpStatus, inner), inner)
|
||||
{ }
|
||||
|
||||
private static string FormatMessage(int httpStatus, Exception inner)
|
||||
{
|
||||
var innerMessage = "(null)";
|
||||
if (inner != null)
|
||||
{
|
||||
innerMessage = string.Format("'{0}'", inner.Message);
|
||||
}
|
||||
return string.Format("Got error {0} with HTTP Status {1}", innerMessage, httpStatus);
|
||||
}
|
||||
}
|
@ -1,240 +0,0 @@
|
||||
/*
|
||||
Original source modified from https://github.com/duosecurity/duo_dotnet
|
||||
|
||||
=============================================================================
|
||||
=============================================================================
|
||||
|
||||
ref: https://github.com/duosecurity/duo_dotnet/blob/master/LICENSE
|
||||
|
||||
Copyright (c) 2011, Duo Security, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Bit.Core.Utilities.Duo;
|
||||
|
||||
public static class DuoWeb
|
||||
{
|
||||
private const string DuoProfix = "TX";
|
||||
private const string AppPrefix = "APP";
|
||||
private const string AuthPrefix = "AUTH";
|
||||
private const int DuoExpire = 300;
|
||||
private const int AppExpire = 3600;
|
||||
private const int IKeyLength = 20;
|
||||
private const int SKeyLength = 40;
|
||||
private const int AKeyLength = 40;
|
||||
|
||||
public static string ErrorUser = "ERR|The username passed to sign_request() is invalid.";
|
||||
public static string ErrorIKey = "ERR|The Duo integration key passed to sign_request() is invalid.";
|
||||
public static string ErrorSKey = "ERR|The Duo secret key passed to sign_request() is invalid.";
|
||||
public static string ErrorAKey = "ERR|The application secret key passed to sign_request() must be at least " +
|
||||
"40 characters.";
|
||||
public static string ErrorUnknown = "ERR|An unknown error has occurred.";
|
||||
|
||||
// throw on invalid bytes
|
||||
private static Encoding _encoding = new UTF8Encoding(false, true);
|
||||
private static DateTime _epoc = new DateTime(1970, 1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Generate a signed request for Duo authentication.
|
||||
/// The returned value should be passed into the Duo.init() call
|
||||
/// in the rendered web page used for Duo authentication.
|
||||
/// </summary>
|
||||
/// <param name="ikey">Duo integration key</param>
|
||||
/// <param name="skey">Duo secret key</param>
|
||||
/// <param name="akey">Application secret key</param>
|
||||
/// <param name="username">Primary-authenticated username</param>
|
||||
/// <param name="currentTime">(optional) The current UTC time</param>
|
||||
/// <returns>signed request</returns>
|
||||
public static string SignRequest(string ikey, string skey, string akey, string username,
|
||||
DateTime? currentTime = null)
|
||||
{
|
||||
string duoSig;
|
||||
string appSig;
|
||||
|
||||
var currentTimeValue = currentTime ?? DateTime.UtcNow;
|
||||
|
||||
if (username == string.Empty)
|
||||
{
|
||||
return ErrorUser;
|
||||
}
|
||||
if (username.Contains("|"))
|
||||
{
|
||||
return ErrorUser;
|
||||
}
|
||||
if (ikey.Length != IKeyLength)
|
||||
{
|
||||
return ErrorIKey;
|
||||
}
|
||||
if (skey.Length != SKeyLength)
|
||||
{
|
||||
return ErrorSKey;
|
||||
}
|
||||
if (akey.Length < AKeyLength)
|
||||
{
|
||||
return ErrorAKey;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
duoSig = SignVals(skey, username, ikey, DuoProfix, DuoExpire, currentTimeValue);
|
||||
appSig = SignVals(akey, username, ikey, AppPrefix, AppExpire, currentTimeValue);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return ErrorUnknown;
|
||||
}
|
||||
|
||||
return $"{duoSig}:{appSig}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate the signed response returned from Duo.
|
||||
/// Returns the username of the authenticated user, or null.
|
||||
/// </summary>
|
||||
/// <param name="ikey">Duo integration key</param>
|
||||
/// <param name="skey">Duo secret key</param>
|
||||
/// <param name="akey">Application secret key</param>
|
||||
/// <param name="sigResponse">The signed response POST'ed to the server</param>
|
||||
/// <param name="currentTime">(optional) The current UTC time</param>
|
||||
/// <returns>authenticated username, or null</returns>
|
||||
public static string VerifyResponse(string ikey, string skey, string akey, string sigResponse,
|
||||
DateTime? currentTime = null)
|
||||
{
|
||||
string authUser = null;
|
||||
string appUser = null;
|
||||
var currentTimeValue = currentTime ?? DateTime.UtcNow;
|
||||
|
||||
try
|
||||
{
|
||||
var sigs = sigResponse.Split(':');
|
||||
var authSig = sigs[0];
|
||||
var appSig = sigs[1];
|
||||
|
||||
authUser = ParseVals(skey, authSig, AuthPrefix, ikey, currentTimeValue);
|
||||
appUser = ParseVals(akey, appSig, AppPrefix, ikey, currentTimeValue);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (authUser != appUser)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return authUser;
|
||||
}
|
||||
|
||||
private static string SignVals(string key, string username, string ikey, string prefix, long expire,
|
||||
DateTime currentTime)
|
||||
{
|
||||
var ts = (long)(currentTime - _epoc).TotalSeconds;
|
||||
expire = ts + expire;
|
||||
var val = $"{username}|{ikey}|{expire.ToString()}";
|
||||
var cookie = $"{prefix}|{Encode64(val)}";
|
||||
var sig = Sign(key, cookie);
|
||||
return $"{cookie}|{sig}";
|
||||
}
|
||||
|
||||
private static string ParseVals(string key, string val, string prefix, string ikey, DateTime currentTime)
|
||||
{
|
||||
var ts = (long)(currentTime - _epoc).TotalSeconds;
|
||||
|
||||
var parts = val.Split('|');
|
||||
if (parts.Length != 3)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var uPrefix = parts[0];
|
||||
var uB64 = parts[1];
|
||||
var uSig = parts[2];
|
||||
|
||||
var sig = Sign(key, $"{uPrefix}|{uB64}");
|
||||
if (Sign(key, sig) != Sign(key, uSig))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (uPrefix != prefix)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var cookie = Decode64(uB64);
|
||||
var cookieParts = cookie.Split('|');
|
||||
if (cookieParts.Length != 3)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var username = cookieParts[0];
|
||||
var uIKey = cookieParts[1];
|
||||
var expire = cookieParts[2];
|
||||
|
||||
if (uIKey != ikey)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var expireTs = Convert.ToInt32(expire);
|
||||
if (ts >= expireTs)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return username;
|
||||
}
|
||||
|
||||
private static string Sign(string skey, string data)
|
||||
{
|
||||
var keyBytes = Encoding.ASCII.GetBytes(skey);
|
||||
var dataBytes = Encoding.ASCII.GetBytes(data);
|
||||
|
||||
using (var hmac = new HMACSHA1(keyBytes))
|
||||
{
|
||||
var hash = hmac.ComputeHash(dataBytes);
|
||||
var hex = BitConverter.ToString(hash);
|
||||
return hex.Replace("-", "").ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
private static string Encode64(string plaintext)
|
||||
{
|
||||
var plaintextBytes = _encoding.GetBytes(plaintext);
|
||||
return Convert.ToBase64String(plaintextBytes);
|
||||
}
|
||||
|
||||
private static string Decode64(string encoded)
|
||||
{
|
||||
var plaintextBytes = Convert.FromBase64String(encoded);
|
||||
return _encoding.GetString(plaintextBytes);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user