/// <summary> /// 验证身份 验证签名的有效性 /// </summary> /// <param name="encodeJwt"></param> /// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值, </param> public bool ValiToken(string encodeJwt, Func <Dictionary <string, string>, bool> validatePayLoad = null) { var success = true; var jwtArr = encodeJwt.Split('.'); if (jwtArr.Length < 3)//数据格式都不对直接pass { return(false); } var header = JsonConvert.DeserializeObject <Dictionary <string, string> >(Base64UrlEncoder.Decode(jwtArr[0])); var payLoad = JsonConvert.DeserializeObject <Dictionary <string, string> >(Base64UrlEncoder.Decode(jwtArr[1])); //配置文件中取出来的签名秘钥 var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_options.Value.IssuerSigningKey)); //验证签名是否正确(把用户传递的签名部分取出来和服务器生成的签名匹配即可) success = success && string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1]))))); if (!success) { return(success);//签名不正确直接返回 } //其次验证是否在有效期内(也应该必须) var now = ToUnixEpochDate(DateTime.UtcNow); success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString())); //不需要自定义验证不传或者传递null即可 if (validatePayLoad == null) { return(true); } //再其次 进行自定义的验证 success = success && validatePayLoad(payLoad); return(success); }
public bool ValidToken(string token, out Dictionary <string, string> info) { info = null; bool success = true; if (!token.Contains(".")) { return(false); } var jwtArr = token.Split("."); if (jwtArr.Length < 3) { return(false); } string headerStr = jwtArr[0].Replace("Bearer ", ""); var header = JsonConvert.DeserializeObject <Dictionary <string, string> >(Base64UrlEncoder.Decode(headerStr)); var payLoad = JsonConvert.DeserializeObject <Dictionary <string, string> >(Base64UrlEncoder.Decode(jwtArr[1])); var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_jwtConfig.Value.IssuerSigningKey)); success = success && string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(headerStr, ".", jwtArr[1]))))); if (!success) { return(success); } var now = ToUnixEpochDate(DateTime.UtcNow); success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString())); info = payLoad; return(success); }
/// <summary> /// This method is used to convert the plain text to Encrypted/Un-Readable Text format. /// </summary> /// <param name="PlainText">Plain Text to Encrypt before transferring over the network.</param> /// <returns>Cipher Text</returns> public static string EncryptPlainTextToCipherText(string PlainText) { //Getting the bytes of Input String. byte[] toEncryptedArray = UTF8Encoding.UTF8.GetBytes(PlainText); MD5CryptoServiceProvider objMD5CryptoService = new MD5CryptoServiceProvider(); //Gettting the bytes from the Security Key and Passing it to compute the Corresponding Hash Value. byte[] securityKeyArray = objMD5CryptoService.ComputeHash(UTF8Encoding.UTF8.GetBytes(_securityKey)); //De-allocatinng the memory after doing the Job. objMD5CryptoService.Clear(); var objTripleDESCryptoService = new TripleDESCryptoServiceProvider(); //Assigning the Security key to the TripleDES Service Provider. objTripleDESCryptoService.Key = securityKeyArray; //Mode of the Crypto service is Electronic Code Book. objTripleDESCryptoService.Mode = CipherMode.ECB; //Padding Mode is PKCS7 if there is any extra byte is added. objTripleDESCryptoService.Padding = PaddingMode.PKCS7; var objCrytpoTransform = objTripleDESCryptoService.CreateEncryptor(); //Transform the bytes array to resultArray byte[] resultArray = objCrytpoTransform.TransformFinalBlock(toEncryptedArray, 0, toEncryptedArray.Length); //Releasing the Memory Occupied by TripleDES Service Provider for Encryption. objTripleDESCryptoService.Clear(); //Convert and return the encrypted data/byte into string format. var result = Convert.ToBase64String(resultArray, 0, resultArray.Length); return(Base64UrlEncoder.Encode(result)); }
/// <summary> /// 校验 /// </summary> /// <param name="encodeJwt">加密后的Jwt令牌</param> /// <param name="options">Jwt选项配置</param> /// <param name="validatePayload">校验负载</param> public Code Validate(string encodeJwt, JwtOptions options, Func <IDictionary <string, string>, JwtOptions, Code> validatePayload) { if (string.IsNullOrWhiteSpace(options.Secret)) { throw new ArgumentNullException(nameof(options.Secret), $@"{nameof(options.Secret)}为Null或空字符串。请在""appsettings.json""配置""{nameof(JwtOptions)}""节点及其子节点""{nameof(JwtOptions.Secret)}"""); } var jwtArray = encodeJwt.Split('.'); if (jwtArray.Length < 3) { return(Code.HttpRequestError); } var header = JsonUtil.ToObject <Dictionary <string, string> >(Base64UrlEncoder.Decode(jwtArray[0])); var payload = JsonUtil.ToObject <Dictionary <string, string> >(Base64UrlEncoder.Decode(jwtArray[1])); // 首先验证签名是否正确 var hs256 = new HMACSHA256(Encoding.UTF8.GetBytes(options.Secret)); var sign = Base64UrlEncoder.Encode( hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArray[0], ".", jwtArray[1])))); // 签名不正确直接返回 if (!string.Equals(jwtArray[2], sign)) { return(Code.HttpRequestError); } // 其次验证是否在有效期内 var now = ToUnixEpochDate(DateTime.UtcNow); if (!(now >= long.Parse(payload["nbf"].ToString()) && now < long.Parse(payload["exp"].ToString()))) { return(Code.TokenInvalid); } // 进行自定义验证 return(validatePayload(payload, options)); }
public bool Validate(string encodeJwt) { bool success = true; try { string[] JWTarr = encodeJwt.Split('.'); Dictionary <string, object> header = JsonConvert.DeserializeObject <Dictionary <string, object> >(Base64UrlEncoder.Decode(JWTarr[0])); Dictionary <string, object> payLoad = JsonConvert.DeserializeObject <Dictionary <string, object> >(Base64UrlEncoder.Decode(JWTarr[1])); var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(securityKey)); success = success && string.Equals(JWTarr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(JWTarr[0], ".", JWTarr[1]))))); if (!success) { return(success); } var now = ToUnixEpochDate(DateTime.UtcNow); success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString())); } catch { return(false); } return(success); }
public static JsonWebKeySet GetJwks() { KeyVaultClient keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback( (authority, resource, scope) => KeyVaultUtil.GetToken(authority, resource, scope))); var key = Task.Run(() => keyVaultClient.GetKeyAsync(SignKey())).ConfigureAwait(false).GetAwaiter().GetResult(); var e = Base64UrlEncoder.Encode(key.Key.E); var n = Base64UrlEncoder.Encode(key.Key.E); var jsonWebKey = new Microsoft.IdentityModel.Tokens.JsonWebKey() { Kid = KEY, Kty = "RSA", E = Base64UrlEncoder.Encode(key.Key.E), N = Base64UrlEncoder.Encode(key.Key.N), Alg = "RS256" }; JsonWebKeySet jsonWebKeySet = new JsonWebKeySet(); jsonWebKeySet.Keys.Add(jsonWebKey); return(jsonWebKeySet); }
public async Task <ActionResult> LoginExt(String token) { try { var decoded = Base64UrlEncoder.DecodeBytes(token); var dp = _dataProtectionProvider.Create("ExternalLogin"); var decBytes = dp.Unprotect(decoded); var strResult = Encoding.UTF8.GetString(decBytes); var xdata = strResult.Split('\b'); if (xdata.Length != 2) { return(new HttpStatusCodeResult(400)); } var user = await UserManager.FindAsync(new UserLoginInfo("ExternalId", xdata[0].ToUpperInvariant())); if (user == null) { return(new HttpStatusCodeResult(400)); } if (!DateTime.TryParseExact(xdata[1], "o", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out DateTime date)) { return(new HttpStatusCodeResult(400)); } if ((DateTime.UtcNow - date).TotalSeconds > 300) // 5 minutes { return(new HttpStatusCodeResult(400)); } await SignInManager.SignInAsync(user, true, true); return(RedirectToLocal("~/")); } catch (Exception ex) { return(new HttpStatusCodeResult(400)); } }
/// <inheritdoc/> public object Create(string algorithm, params object[] args) { if (IsSupportedAlgorithm(algorithm, args)) { if (algorithm.Equals("EdDSA", StringComparison.OrdinalIgnoreCase)) { var keyMaterial = args[0] as JsonWebKey; if (keyMaterial != null) { //TODO: Probably should check a case where both are defined (or some other combinations). AsymmetricKeyParameter?keyParameter = null; if (keyMaterial.X != null) { var decodedPublicBytes = Base64UrlEncoder.DecodeBytes(keyMaterial.X); keyParameter = new Ed25519PublicKeyParameters(decodedPublicBytes, 0); } else if (keyMaterial.D != null) { var decodedPrivateBytes = Base64UrlEncoder.DecodeBytes(keyMaterial.D); keyParameter = new Ed25519PrivateKeyParameters(decodedPrivateBytes, 0); } else { throw new ArgumentException("Key material needs to be provided"); } var securityKey = new BouncyCastleEdDsaSecurityKey(keyParameter, keyMaterial.Crv, this); return(new BouncyCastleEdDsaSignatureProvider(securityKey, algorithm)); } throw new ArgumentException($"The key material argument in position args[0] expected is \"{typeof(JsonWebKey)}\"."); } } throw new NotSupportedException(); }
/// <summary> /// 验证身份 验证签名的有效性, /// </summary> /// <param name="encodeJwt"></param> /// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值, </param> /// <returns></returns> public bool ValidateToken(string accessToken) { try { string[] tokenSplitArray = accessToken.Split('.'); var header = JsonConvert.DeserializeObject <Dictionary <string, object> >(Base64UrlEncoder.Decode(tokenSplitArray[0])); var payLoad = JsonConvert.DeserializeObject <Dictionary <string, object> >(Base64UrlEncoder.Decode(tokenSplitArray[1])); var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_securityKey)); //验证签名是否正确 if (!tokenSplitArray[2].Equals(Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(tokenSplitArray[0], ".", tokenSplitArray[1])))))) { return(false); } //验证是否在有效期内 long totalSeconds = ToUnixEpochDate(DateTime.UtcNow); if (!(totalSeconds >= long.Parse(payLoad["nbf"].ToString()) && totalSeconds < long.Parse(payLoad["exp"].ToString()))) { return(false); } //验证发布者和受众是否一致,如果是在实际环境中,sso有多个系统需要登录,那么可以给每个系统分配一个audience,维护一个列表来校验 if (!(payLoad["iss"].Equals(_issuer) && payLoad["aud"].Equals(_audience))) { return(false); } return(true); } catch (Exception ex) { _logger.LogWarning(ex.Message); return(false); } }
/// <summary> /// DecodeNewPayload method implementation /// </summary> public Dictionary <string, string> DecodeNewPayload(string rawBLOBJwt) { if (string.IsNullOrWhiteSpace(rawBLOBJwt)) { throw new ArgumentNullException(nameof(rawBLOBJwt)); } var jwtParts = rawBLOBJwt.Split('.'); if (jwtParts.Length != 3) { throw new ArgumentException("The JWT does not have the 3 expected components"); } var json = Base64UrlEncoder.Decode(jwtParts[1]); var jObject = JObject.Parse(json); var parameters = new Dictionary <string, string>(); var versionnumber = ((string)jObject["no"] ?? "0"); var nextupdate = ((string)jObject["nextUpdate"] ?? "1970-01-01"); parameters.Add("Number", versionnumber); parameters.Add("NextUpdate", nextupdate); return(parameters); }
public void AddSigningCredential_with_invalid_crv_value_should_throw_exception(string curveOid, string alg, string crv) { IServiceCollection services = new ServiceCollection(); IIdentityServerBuilder identityServerBuilder = new IdentityServerBuilder(services); var key = new ECDsaSecurityKey(ECDsa.Create( ECCurve.CreateFromOid(Oid.FromOidValue(curveOid, OidGroup.All)))); var parameters = key.ECDsa.ExportParameters(true); var jsonWebKeyFromECDsa = new JsonWebKey() { Kty = JsonWebAlgorithmsKeyTypes.EllipticCurve, Use = "sig", Kid = key.KeyId, KeyId = key.KeyId, X = Base64UrlEncoder.Encode(parameters.Q.X), Y = Base64UrlEncoder.Encode(parameters.Q.Y), D = Base64UrlEncoder.Encode(parameters.D), Crv = crv.Replace("-", string.Empty), Alg = SecurityAlgorithms.EcdsaSha256 }; Assert.Throws <InvalidOperationException>(() => identityServerBuilder.AddSigningCredential(jsonWebKeyFromECDsa, alg)); }
public IActionResult Login() { var stateBytes = new byte[16]; RandomNumberGenerator.Create().GetBytes(stateBytes); var state = Base64UrlEncoder.Encode(stateBytes); Response.Cookies.Append("state", state, new CookieOptions { HttpOnly = true, Expires = DateTimeOffset.Now.AddMinutes(10), IsEssential = true }); var deleteCookieOptions = new CookieOptions { Expires = DateTimeOffset.Now.AddDays(-100), HttpOnly = true, IsEssential = true }; if (Request.Cookies.ContainsKey("key")) { Response.Cookies.Append("key", "", deleteCookieOptions); } if (Request.Cookies.ContainsKey("username")) { Response.Cookies.Append("username", "", deleteCookieOptions); } if (Request.Cookies.ContainsKey("avatar")) { Response.Cookies.Append("avatar", "", deleteCookieOptions); } var uri = discordHttp.GetRedirectUri(state); return(Redirect(uri)); }
private async Task CreateSigningKey() { if (await _outbackDbContext.Secrets.AnyAsync()) { return; } var secret = ECDsa.Create(ECCurve.NamedCurves.nistP256); var ecdSaKey = new ECDsaSecurityKey(secret) { KeyId = _randomStringGenerator.GetRandomString(20) }; var publicKey = ecdSaKey.ECDsa.ExportParameters(true); var cryptographyParameters = new CryptographyParameters { EncodedD = Base64UrlEncoder.Encode(publicKey.D), EncodedX = Base64UrlEncoder.Encode(publicKey.Q.X), EncodedY = Base64UrlEncoder.Encode(publicKey.Q.Y), KeyId = ecdSaKey.KeyId }; var outbackSecret = new OutbackSecret { ActiveSigningKey = true, CryptographyData = JsonSerializer.Serialize(cryptographyParameters), Expires = DateTime.UtcNow.AddYears(100), PublicKeyCryptographyType = PublicKeyCryptographyType.EC_NistP256, }; await _outbackDbContext.Secrets.AddAsync(outbackSecret); await _outbackDbContext.SaveChangesAsync(); }
public async Task InvokeCryptographyEndpointAsync_RsaSecurityKeysAreCorrectlyExposed() { // Arrange var parameters = new RSAParameters { D = Convert.FromBase64String("Uj6NrYBnyddhlJefYEP2nleCntAKlWyIttJC4cJnNxNN+OT2fQXhpTXRwW4R5YIS3HDqK/Fg2yoYm+OTVntAAgRFKveRx/WKwFo6UpnJc5u3lElhFa7IfosO9qXjErpX9ruAVqipekDLwQ++KmVVdgH4PK/o//nEx5zklGCdlEJURZYJPs9/7g1cx3UwvPp8jM7LgZL5OZRNyI3Jz4efrwiI2/vd8P28lAbpv/Ao4NwUDq/WKEnZ8JYSjLEKnZCfbX1ZEwf0Ic48jEKHmi1WEwpru1fMPoYfakrsY/VEfatPiDs8a5HABP/KaXcM4AZsr7HbzqAaNycV2xgdZimGcQ=="), DP = Convert.FromBase64String("hi1e+0eQ/iYrfT4zpZVbx3dyfA7Ch/aujMt6nGMF+1LGaut86vDHM2JI0Gc2BKc+uPEu2bNAorhSmuSyGpfGYl0MYFQoVF/jyiGpzYPmhYpL5yLuN9jWAqNwjfstuRDLU9zTEfZnr3OSN85rZcgT7NUxlY8im1Y2TWYxGiEXw9E="), DQ = Convert.FromBase64String("laVNkWIbnSuGo7nAxyUSdL2sXU3GZWwItwzTG0IK/0woFjArtCxGgNXW+V+GhxT7iHGAVJJSBvJ65TXrUYuBmoWj2CsoUs2mzK8ax4zg3CXrU61esCsGUoS2owR4FXlhYPmoVnglGu89bH72eXKixZsuF7vKW19nG703BXYEaEU="), Exponent = Convert.FromBase64String("AQAB"), InverseQ = Convert.FromBase64String("dhzLDS4F5WYHX+vH4+uL3Ei/K5lxw2A/dBHGtbS2X54gm7vARl+FrptOFFwIjjmsLuTjttAq9K1EP/XZIq8bjW6dXJ/IytnobIPSFkclEeQlMi4/2VDMG5915J0DwnKO9M+B8F3JViUyMv0pvb+ub+HHDVFkIr7zooCmY25i77Q="), Modulus = Convert.FromBase64String("kXv7Pxf6mSf7mu6mPAOAoKAXl5kU7Q3h9zevC5i4Mm5bMk17XCh7ZvVxDzGA+1JmyxOX6sw3gMUl31FtIFlDhis8VnXKAPn8i1zrmebq+7QKzpE2GpoIpXjXbkPaHG/DbC67M1bux7/dE7lSUSifHRRLsbMUC2D4UahJ6miH2iPFNFyoa6CLtwosD8tIJKwmZ9r9zfqc9BrVGu24lZySjTSRttpLaTkgkBjxHmYhinKNEtj9wUfi1S1wPJUvf+roc6o+7jeBBV3EXJCsb6XCCXI7/e3umWp19odeRShXLQNQbNuuVC7yre4iidUDrWJ1jiaB06svUG+fVEi4FCMvEQ=="), P = Convert.FromBase64String("xQGczmp4qD7Sez/ZqgW+O4cciTHvSqJqJUSdDd2l1Pd/szQ8avvzorrbSWOIULyv6eJb32+HuyLgy6rTSJ6THFobAnUv4ZTR7EGK26AJmP/BhD+3G+n21+4fzfbAxpHihkCYmO8aEl8fm/r4qPVXmCzFoXDZLMNIxFsdEXiFRS0="), Q = Convert.FromBase64String("vQy5C++AzF+TRh6qwbKzOqt87ZHEHidIAh6ivRNewjzIgCWXpseVl7DimY1YdViOnw1VI7xY+EyiyTanq5caTqqB3KcDm2t40bJfrZuUcn/5puRIh1bKNDwIMLsuNCrjHmDlNbocqpYMOh0Pgw7ARNbqrnPjWsYGJPuMNFpax/U=") }; var server = CreateAuthorizationServer(options => { options.SigningCredentials.Clear(); options.SigningCredentials.AddKey(new RsaSecurityKey(parameters)); }); var client = new OpenIdConnectClient(server.CreateClient()); // Act var response = await client.GetAsync(CryptographyEndpoint); var key = response[OpenIdConnectConstants.Parameters.Keys].First; // Assert Assert.Null(key[JsonWebKeyParameterNames.D]); Assert.Null(key[JsonWebKeyParameterNames.DP]); Assert.Null(key[JsonWebKeyParameterNames.DQ]); Assert.Null(key[JsonWebKeyParameterNames.P]); Assert.Null(key[JsonWebKeyParameterNames.Q]); Assert.Equal(parameters.Exponent, Base64UrlEncoder.DecodeBytes((string)key[JsonWebKeyParameterNames.E])); Assert.Equal(parameters.Modulus, Base64UrlEncoder.DecodeBytes((string)key[JsonWebKeyParameterNames.N])); }
/// <summary> /// Creates a JsonWebToken (JWS or JWE). /// </summary> /// <param name="payload">A JObject that represents the JWT token payload.</param> /// <param name="signingCredentials">Defines the security key and algorithm that will be used to sign the JWT.</param> /// <param name="encryptingCredentials">Defines the security key and algorithm that will be used to encrypt the JWT.</param> /// <returns>A JWT in compact serialization format.</returns> private string CreateJsonWebToken(JObject payload, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials) { if (payload == null) { throw LogHelper.LogArgumentNullException(nameof(payload)); } string rawHeader; if (!JsonWebTokenManager.KeyToHeaderCache.TryGetValue(JsonWebTokenManager.GetHeaderCacheKey(signingCredentials), out rawHeader)) { var header = signingCredentials == null ? new JObject() : new JObject { { JwtHeaderParameterNames.Alg, signingCredentials.Algorithm }, { JwtHeaderParameterNames.Kid, signingCredentials.Key.KeyId }, { JwtHeaderParameterNames.Typ, JwtConstants.HeaderType } }; rawHeader = Base64UrlEncoder.Encode(Encoding.UTF8.GetBytes(header.ToString(Newtonsoft.Json.Formatting.None))); JsonWebTokenManager.KeyToHeaderCache.TryAdd(JsonWebTokenManager.GetHeaderCacheKey(signingCredentials), rawHeader); } string rawPayload = Base64UrlEncoder.Encode(Encoding.UTF8.GetBytes(payload.ToString(Newtonsoft.Json.Formatting.None))); string rawSignature = signingCredentials == null ? string.Empty : JwtTokenUtilities.CreateEncodedSignature(string.Concat(rawHeader, ".", rawPayload), signingCredentials); var rawData = rawHeader + "." + rawPayload + "." + rawSignature; if (encryptingCredentials != null) { return(EncryptToken(rawData, encryptingCredentials)); } else { return(rawData); } }
// This method helps to create the JWT. // I tried using a JWT library, but could not get it to work. That is why // this method replicates the funcionality to build a JWT token. // The important part to understand here is that the values that are added to the payload // will be received by Saasler and stored as properties of the user. Ex: name, plan_id, etc. public static string CreateJWT( string yourUserId, string yourUserEmail, string yourUserToken) { var header = new { alg = "HS256", //typ = "JWT" <- This is to commented so it generates a JWT similar to the one the Ruby library generates. }; // The header of the JWT token. var headerPart = Base64UrlEncoder.Encode(JsonConvert.SerializeObject(header)); // Defining the JWT token payload var iat_ = DateTime.Now; var exp_ = iat_.AddMinutes(2); var payload = new { iat = Math.Truncate(DateTimeToUnixTimestamp(iat_)), exp = Math.Truncate(DateTimeToUnixTimestamp(exp_)), // you can add other attributes here. // They will be used in the future to make filters. id = yourUserId, email = yourUserEmail, token = yourUserToken, }; var payloadPart = Base64UrlEncoder.Encode(JsonConvert.SerializeObject(payload)); // This MUST be the JWT SECRET generated for your account. // This will allow Saasler to decode the JWT TOKEN var secret = "SAASLER_JWT_SECRET"; var sha256 = new HMACSHA256(Encoding.UTF8.GetBytes(secret)); var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes($"{headerPart}.{payloadPart}")); var hash = Base64UrlEncoder.Encode(hashBytes); var jwt = $"{headerPart}.{payloadPart}.{hash}"; return(jwt); }
protected override void OnInitialized() { string iss = "https://login.microsoftonline.com/common/v2.0"; string aud = "6cb04018-a3f5-46a7-b995-940c78f5aef3"; string preferred_username = configuration.GetSection("JWTSettings")["DefaultSubject"]; string sub = "AAAAAAAAAAAAAAAAAAAAAIkzqFVrSaSaFHy782bbtaQ"; string name = "John Doe"; string ver = "2.0"; string nonce = "12345"; string oid = "00000000-0000-0000-66f3-3332eca7ea81"; string tid = "9122040d-6c67-4c5b-b112-36a304b66dad"; string aio = "Df2UVXL1ix!lMCWMSOJBcFatzcGfvFGhjKv8q5g0x732dR5MB5BisvGQO7YWByjd8iQDLq!eGbIDakyp5mnOrcdqHeYSnltepQmRp6AIZ8jY"; string host = configuration.GetSection("JWTSettings")["HostEnvironment"]; SigningCertThumbprint = configuration.GetSection("JWTSettings")["SigningCertThumbprint"]; jwt = new B2EToken { Version = ver, Audience = aud, IssuedAt = DateTime.UtcNow.ToString(), iat = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), NotBefore = DateTime.UtcNow.ToString(), nbf = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), Expiration = DateTime.UtcNow.AddMinutes(60).ToString(), exp = DateTimeOffset.UtcNow.AddMinutes(60).ToUnixTimeSeconds().ToString(), Issuer = iss, sub = sub, name = name, preferred_username = preferred_username, oid = oid, tid = tid, nonce = nonce, aio = aio, }; //One way to handle Windows-based certs if (Environment.OSVersion.Platform.ToString() == "Win32NT") { SigningCredentials = new Lazy <X509SigningCredentials>(() => { X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser); certStore.Open(OpenFlags.ReadOnly); X509Certificate2Collection certCollection = certStore.Certificates.Find( X509FindType.FindByThumbprint, SigningCertThumbprint, false); // Get the first cert with the thumbprint if (certCollection.Count > 0) { SigningCertHash = Base64UrlEncoder.Encode(certCollection[0].GetCertHash()); return(new X509SigningCredentials(certCollection[0])); } throw new Exception("Certificate not found"); }); } //And another way to handle Linux certs if (Environment.OSVersion.Platform.ToString() == "Unix") { var bytes = System.IO.File.ReadAllBytes($"/var/ssl/private/{SigningCertThumbprint}.p12"); var cert = new X509Certificate2(bytes); SigningCertHash = Base64UrlEncoder.Encode(cert.GetCertHash()); SigningCredentials = new Lazy <X509SigningCredentials>(() => { if (cert != null) { return(new X509SigningCredentials(cert)); } throw new Exception("Certificate not found"); }); } }
public static SecurityKeyIdentifier GetKeyIdentifier(this SecurityKey key) { if (key == null) { throw new ArgumentNullException(nameof(key)); } SecurityKeyIdentifier identifier = null; X509Certificate2 certificate = null; var x509SecurityKey = key as X509SecurityKey; if (x509SecurityKey != null) { certificate = x509SecurityKey.Certificate; } var x509AsymmetricSecurityKey = key as X509AsymmetricSecurityKey; if (x509AsymmetricSecurityKey != null) { // The X.509 certificate is not directly accessible when using X509AsymmetricSecurityKey. // Reflection is the only way to get the certificate used to create the security key. var field = typeof(X509AsymmetricSecurityKey).GetField( name: "certificate", bindingAttr: BindingFlags.Instance | BindingFlags.NonPublic); Debug.Assert(field != null, "The 'certificate' field shouldn't be missing."); certificate = (X509Certificate2)field.GetValue(x509AsymmetricSecurityKey); } if (certificate != null) { identifier = new SecurityKeyIdentifier { new X509IssuerSerialKeyIdentifierClause(x509SecurityKey.Certificate), new X509RawDataKeyIdentifierClause(x509SecurityKey.Certificate), new X509ThumbprintKeyIdentifierClause(x509SecurityKey.Certificate), new LocalIdKeyIdentifierClause(x509SecurityKey.Certificate.Thumbprint.ToUpperInvariant()), new NamedKeySecurityKeyIdentifierClause(JwtHeaderParameterNames.X5t, x509SecurityKey.Certificate.Thumbprint.ToUpperInvariant()) }; } if (identifier == null) { // Create an empty security key identifier. identifier = new SecurityKeyIdentifier(); var rsaSecurityKey = key as RsaSecurityKey; if (rsaSecurityKey != null) { // Resolve the underlying algorithm from the security key. var algorithm = (RSA)rsaSecurityKey.GetAsymmetricAlgorithm( algorithm: SecurityAlgorithms.RsaSha256Signature, requiresPrivateKey: false); Debug.Assert(algorithm != null, "SecurityKey.GetAsymmetricAlgorithm() shouldn't return a null algorithm."); // Export the RSA public key to extract a key identifier based on the modulus component. var parameters = algorithm.ExportParameters(includePrivateParameters: false); Debug.Assert(parameters.Modulus != null, "RSA.ExportParameters() shouldn't return a null modulus."); // Only use the 40 first chars of the base64url-encoded modulus. var kid = Base64UrlEncoder.Encode(parameters.Modulus); kid = kid.Substring(0, Math.Min(kid.Length, 40)).ToUpperInvariant(); identifier.Add(new RsaKeyIdentifierClause(algorithm)); identifier.Add(new LocalIdKeyIdentifierClause(kid)); identifier.Add(new NamedKeySecurityKeyIdentifierClause(JwtHeaderParameterNames.Kid, kid)); } } // Mark the security key identifier as read-only to // ensure it can't be altered during a request. identifier.MakeReadOnly(); return(identifier); }
private StorageQueueMetricDefinition GenerateBogusAzureStorageQueueMetricDefinition() { var bogusAzureMetricConfiguration = GenerateBogusAzureMetricConfiguration(); var bogusGenerator = new Faker <StorageQueueMetricDefinition>() .StrictMode(ensureRulesForAllProperties: true) .RuleFor(metricDefinition => metricDefinition.Name, faker => faker.Name.FirstName()) .RuleFor(metricDefinition => metricDefinition.Description, faker => faker.Lorem.Sentence(wordCount: 6)) .RuleFor(metricDefinition => metricDefinition.ResourceType, faker => ResourceType.StorageQueue) .RuleFor(metricDefinition => metricDefinition.AccountName, faker => faker.Name.LastName()) .RuleFor(metricDefinition => metricDefinition.QueueName, faker => faker.Name.FirstName()) .RuleFor(metricDefinition => metricDefinition.SasToken, faker => $"?sig={Base64UrlEncoder.Encode(faker.Lorem.Sentence(wordCount: 3))}") .RuleFor(metricDefinition => metricDefinition.AzureMetricConfiguration, faker => bogusAzureMetricConfiguration); return(bogusGenerator.Generate()); }
public static string EncodeUrl(string textoBase64) { return(Base64UrlEncoder.Encode(textoBase64)); }
public static string DecodeUrl(string codificadoUrlBase64) { return(Base64UrlEncoder.Decode(codificadoUrlBase64)); }
/// <summary> /// converts encoded base64 string to plain text /// </summary> /// <returns>decoded plain text string</returns> public override string ToString() { return(Base64UrlEncoder.Decode(myBase64String.Reverse())); }
private async Task <bool> InvokeTokenEndpointAsync() { if (!string.Equals(Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) { Options.Logger.LogError("The token request was rejected because an invalid " + "HTTP method was received: {Method}.", Request.Method); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "A malformed token request has been received: make sure to use POST." })); } // See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization if (string.IsNullOrEmpty(Request.ContentType)) { Options.Logger.LogError("The token request was rejected because the " + "mandatory 'Content-Type' header was missing."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "A malformed token request has been received: " + "the mandatory 'Content-Type' header was missing from the POST request." })); } // May have media/type; charset=utf-8, allow partial match. if (!Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase)) { Options.Logger.LogError("The token request was rejected because an invalid 'Content-Type' " + "header was received: {ContentType}.", Request.ContentType); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "A malformed token request has been received: " + "the 'Content-Type' header contained an unexcepted value. " + "Make sure to use 'application/x-www-form-urlencoded'." })); } var request = new OpenIdConnectRequest(await Request.ReadFormAsync()); // Note: set the message type before invoking the ExtractTokenRequest event. request.SetProperty(OpenIdConnectConstants.Properties.MessageType, OpenIdConnectConstants.MessageTypes.Token); // Store the token request in the OWIN context. Context.SetOpenIdConnectRequest(request); var @event = new ExtractTokenRequestContext(Context, Options, request); await Options.Provider.ExtractTokenRequest(@event); if (@event.HandledResponse) { return(true); } else if (@event.Skipped) { return(false); } else if (@event.IsRejected) { Options.Logger.LogError("The token request was rejected with the following error: {Error} ; {Description}", /* Error: */ @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, /* Description: */ @event.ErrorDescription); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = @event.ErrorDescription, ErrorUri = @event.ErrorUri })); } // Reject token requests missing the mandatory grant_type parameter. if (string.IsNullOrEmpty(request.GrantType)) { Options.Logger.LogError("The token request was rejected because the grant type was missing."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "The mandatory 'grant_type' parameter was missing.", })); } // Reject grant_type=authorization_code requests if the authorization endpoint is disabled. else if (request.IsAuthorizationCodeGrantType() && !Options.AuthorizationEndpointPath.HasValue) { Options.Logger.LogError("The token request was rejected because the authorization code grant was disabled."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.UnsupportedGrantType, ErrorDescription = "The authorization code grant is not allowed by this authorization server." })); } // Reject grant_type=authorization_code requests missing the authorization code. // See https://tools.ietf.org/html/rfc6749#section-4.1.3 else if (request.IsAuthorizationCodeGrantType() && string.IsNullOrEmpty(request.Code)) { Options.Logger.LogError("The token request was rejected because the authorization code was missing."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "The mandatory 'code' parameter was missing." })); } // Reject grant_type=refresh_token requests missing the refresh token. // See https://tools.ietf.org/html/rfc6749#section-6 else if (request.IsRefreshTokenGrantType() && string.IsNullOrEmpty(request.RefreshToken)) { Options.Logger.LogError("The token request was rejected because the refresh token was missing."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "The mandatory 'refresh_token' parameter was missing." })); } // Reject grant_type=password requests missing username or password. // See https://tools.ietf.org/html/rfc6749#section-4.3.2 else if (request.IsPasswordGrantType() && (string.IsNullOrEmpty(request.Username) || string.IsNullOrEmpty(request.Password))) { Options.Logger.LogError("The token request was rejected because the resource owner credentials were missing."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "The mandatory 'username' and/or 'password' parameters " + "was/were missing from the request message." })); } // When client_id and client_secret are both null, try to extract them from the Authorization header. // See http://tools.ietf.org/html/rfc6749#section-2.3.1 and // http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication if (string.IsNullOrEmpty(request.ClientId) && string.IsNullOrEmpty(request.ClientSecret)) { var header = Request.Headers.Get("Authorization"); if (!string.IsNullOrEmpty(header) && header.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase)) { try { var value = header.Substring("Basic ".Length).Trim(); var data = Encoding.UTF8.GetString(Convert.FromBase64String(value)); var index = data.IndexOf(':'); if (index >= 0) { request.ClientId = data.Substring(0, index); request.ClientSecret = data.Substring(index + 1); } } catch (FormatException) { } catch (ArgumentException) { } } } var context = new ValidateTokenRequestContext(Context, Options, request); await Options.Provider.ValidateTokenRequest(context); // If the validation context was set as fully validated, // mark the OpenID Connect request as confidential. if (context.IsValidated) { request.SetProperty(OpenIdConnectConstants.Properties.ConfidentialityLevel, OpenIdConnectConstants.ConfidentialityLevels.Private); } if (context.HandledResponse) { return(true); } else if (context.Skipped) { return(false); } else if (context.IsRejected) { Options.Logger.LogError("The token request was rejected with the following error: {Error} ; {Description}", /* Error: */ context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, /* Description: */ context.ErrorDescription); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = context.ErrorDescription, ErrorUri = context.ErrorUri })); } // Reject grant_type=client_credentials requests if validation was skipped. else if (context.IsSkipped && request.IsClientCredentialsGrantType()) { Options.Logger.LogError("The token request must be fully validated to use the client_credentials grant type."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Client authentication is required when using client_credentials." })); } // Ensure that the client_id has been set from the ValidateTokenRequest event. else if (context.IsValidated && string.IsNullOrEmpty(request.ClientId)) { Options.Logger.LogError("The token request was validated but the client_id was not set."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.ServerError, ErrorDescription = "An internal server error occurred." })); } // At this stage, client_id cannot be null for grant_type=authorization_code requests, // as it must either be set in the ValidateTokenRequest notification // by the developer or manually flowed by non-confidential client applications. // See https://tools.ietf.org/html/rfc6749#section-4.1.3 if (request.IsAuthorizationCodeGrantType() && string.IsNullOrEmpty(request.ClientId)) { Options.Logger.LogError("The token request was rejected because the mandatory 'client_id' was missing."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "client_id was missing from the token request" })); } AuthenticationTicket ticket = null; // See http://tools.ietf.org/html/rfc6749#section-4.1 // and http://tools.ietf.org/html/rfc6749#section-4.1.3 (authorization code grant). // See http://tools.ietf.org/html/rfc6749#section-6 (refresh token grant). if (request.IsAuthorizationCodeGrantType() || request.IsRefreshTokenGrantType()) { ticket = request.IsAuthorizationCodeGrantType() ? await DeserializeAuthorizationCodeAsync(request.Code, request) : await DeserializeRefreshTokenAsync(request.RefreshToken, request); if (ticket == null) { Options.Logger.LogError("The token request was rejected because the " + "authorization code or the refresh token was invalid."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Invalid ticket" })); } // If the client was fully authenticated when retrieving its refresh token, // the current request must be rejected if client authentication was not enforced. if (request.IsRefreshTokenGrantType() && !context.IsValidated && ticket.IsConfidential()) { Options.Logger.LogError("The token request was rejected because client authentication " + "was required to use the confidential refresh token."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Client authentication is required to use this ticket" })); } if (ticket.Properties.ExpiresUtc.HasValue && ticket.Properties.ExpiresUtc < Options.SystemClock.UtcNow) { Options.Logger.LogError("The token request was rejected because the " + "authorization code or the refresh token was expired."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Expired ticket" })); } // Note: presenters may be empty during a grant_type=refresh_token request if the refresh token // was issued to a public client but cannot be null for an authorization code grant request. var presenters = ticket.GetPresenters(); if (request.IsAuthorizationCodeGrantType() && !presenters.Any()) { Options.Logger.LogError("The token request was rejected because the authorization " + "code didn't contain any valid presenter."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.ServerError, ErrorDescription = "An internal server error occurred." })); } // Ensure the authorization code/refresh token was issued to the client application making the token request. // Note: when using the refresh token grant, client_id is optional but must validated if present. // As a consequence, this check doesn't depend on the actual status of client authentication. // See https://tools.ietf.org/html/rfc6749#section-6 // and http://openid.net/specs/openid-connect-core-1_0.html#RefreshingAccessToken if (!string.IsNullOrEmpty(request.ClientId) && presenters.Any() && !presenters.Contains(request.ClientId, StringComparer.Ordinal)) { Options.Logger.LogError("The token request was rejected because the authorization " + "code was issued to a different client application."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Ticket does not contain matching client_id" })); } // Validate the redirect_uri flowed by the client application during this token request. // Note: for pure OAuth2 requests, redirect_uri is only mandatory if the authorization request // contained an explicit redirect_uri. OpenID Connect requests MUST include a redirect_uri // but the specifications allow proceeding the token request without returning an error // if the authorization request didn't contain an explicit redirect_uri. // See https://tools.ietf.org/html/rfc6749#section-4.1.3 // and http://openid.net/specs/openid-connect-core-1_0.html#TokenRequestValidation var address = ticket.GetProperty(OpenIdConnectConstants.Properties.RedirectUri); if (request.IsAuthorizationCodeGrantType() && !string.IsNullOrEmpty(address)) { ticket.SetProperty(OpenIdConnectConstants.Properties.RedirectUri, null); if (string.IsNullOrEmpty(request.RedirectUri)) { Options.Logger.LogError("The token request was rejected because the mandatory 'redirect_uri' " + "parameter was missing from the grant_type=authorization_code request."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "redirect_uri was missing from the token request" })); } else if (!string.Equals(address, request.RedirectUri, StringComparison.Ordinal)) { Options.Logger.LogError("The token request was rejected because the 'redirect_uri' " + "parameter didn't correspond to the expected value."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Authorization code does not contain matching redirect_uri" })); } } // If a code challenge was initially sent in the authorization request and associated with the // code, validate the code verifier to ensure the token request is sent by a legit caller. var challenge = ticket.GetProperty(OpenIdConnectConstants.Properties.CodeChallenge); if (request.IsAuthorizationCodeGrantType() && !string.IsNullOrEmpty(challenge)) { ticket.SetProperty(OpenIdConnectConstants.Properties.CodeChallenge, null); // Get the code verifier from the token request. // If it cannot be found, return an invalid_grant error. var verifier = request.CodeVerifier; if (string.IsNullOrEmpty(verifier)) { Options.Logger.LogError("The token request was rejected because the required 'code_verifier' " + "parameter was missing from the grant_type=authorization_code request."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidRequest, ErrorDescription = "The required 'code_verifier' was missing from the token request." })); } // Note: the code challenge method is always validated when receiving the authorization request. var method = ticket.GetProperty(OpenIdConnectConstants.Properties.CodeChallengeMethod); ticket.SetProperty(OpenIdConnectConstants.Properties.CodeChallengeMethod, null); Debug.Assert(string.IsNullOrEmpty(method) || string.Equals(method, OpenIdConnectConstants.CodeChallengeMethods.Plain, StringComparison.Ordinal) || string.Equals(method, OpenIdConnectConstants.CodeChallengeMethods.Sha256, StringComparison.Ordinal), "The specified code challenge method should be supported."); // If the S256 challenge method was used, compute the hash corresponding to the code verifier. if (string.Equals(method, OpenIdConnectConstants.CodeChallengeMethods.Sha256, StringComparison.Ordinal)) { using (var algorithm = SHA256.Create()) { // Compute the SHA-256 hash of the code verifier and encode it using base64-url. // See https://tools.ietf.org/html/rfc7636#section-4.6 for more information. var hash = algorithm.ComputeHash(Encoding.ASCII.GetBytes(request.CodeVerifier)); verifier = Base64UrlEncoder.Encode(hash); } } // Compare the verifier and the code challenge: if the two don't match, return an error. // Note: to prevent timing attacks, a time-constant comparer is always used. if (!OpenIdConnectServerHelpers.AreEqual(verifier, challenge)) { Options.Logger.LogError("The token request was rejected because the 'code_verifier' was invalid."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "The specified 'code_verifier' was invalid." })); } } if (request.IsRefreshTokenGrantType() && !string.IsNullOrEmpty(request.Resource)) { // When an explicit resource parameter has been included in the token request // but was missing from the initial request, the request MUST be rejected. var resources = ticket.GetResources(); if (!resources.Any()) { Options.Logger.LogError("The token request was rejected because the 'resource' parameter was not allowed."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Token request cannot contain a resource parameter " + "if the authorization request didn't contain one" })); } // When an explicit resource parameter has been included in the token request, // the authorization server MUST ensure that it doesn't contain resources // that were not allowed during the initial authorization/token request. else if (!new HashSet <string>(resources).IsSupersetOf(request.GetResources())) { Options.Logger.LogError("The token request was rejected because the 'resource' parameter was not valid."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Token request doesn't contain a valid resource parameter" })); } } if (request.IsRefreshTokenGrantType() && !string.IsNullOrEmpty(request.Scope)) { // When an explicit scope parameter has been included in the token request // but was missing from the initial request, the request MUST be rejected. // See http://tools.ietf.org/html/rfc6749#section-6 var scopes = ticket.GetScopes(); if (!scopes.Any()) { Options.Logger.LogError("The token request was rejected because the 'scope' parameter was not allowed."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Token request cannot contain a scope parameter " + "if the authorization request didn't contain one" })); } // When an explicit scope parameter has been included in the token request, // the authorization server MUST ensure that it doesn't contain scopes // that were not allowed during the initial authorization/token request. // See https://tools.ietf.org/html/rfc6749#section-6 else if (!new HashSet <string>(scopes).IsSupersetOf(request.GetScopes())) { Options.Logger.LogError("The token request was rejected because the 'scope' parameter was not valid."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = "Token request doesn't contain a valid scope parameter" })); } } } var notification = new HandleTokenRequestContext(Context, Options, request, ticket); await Options.Provider.HandleTokenRequest(notification); if (notification.HandledResponse) { return(true); } else if (notification.Skipped) { return(false); } else if (notification.IsRejected) { Options.Logger.LogError("The token request was rejected with the following error: {Error} ; {Description}", /* Error: */ notification.Error ?? OpenIdConnectConstants.Errors.InvalidGrant, /* Description: */ notification.ErrorDescription); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = notification.Error ?? OpenIdConnectConstants.Errors.InvalidGrant, ErrorDescription = notification.ErrorDescription, ErrorUri = notification.ErrorUri })); } // Flow the changes made to the ticket. ticket = notification.Ticket; // Ensure an authentication ticket has been provided or return // an error code indicating that the grant type is not supported. if (ticket == null) { Options.Logger.LogError("The token request was rejected because no authentication " + "ticket was returned by application code."); return(await SendTokenResponseAsync(new OpenIdConnectResponse { Error = OpenIdConnectConstants.Errors.UnsupportedGrantType, ErrorDescription = "The specified grant_type parameter is not supported." })); } return(await HandleSignInAsync(ticket)); }
public override PasetoSecurityToken Verify(PasetoToken token, IEnumerable <SecurityKey> signingKeys) { if (token == null) { throw new ArgumentNullException(nameof(token)); } if (signingKeys == null || !signingKeys.Any()) { throw new ArgumentNullException(nameof(signingKeys)); } var keys = signingKeys.OfType <EdDsaSecurityKey>().ToList(); if (!keys.Any()) { throw new SecurityTokenInvalidSigningKeyException($"PASETO v2 requires key of type {typeof(EdDsaSecurityKey)}"); } if (token.Version != PasetoConstants.Versions.V2) { throw new ArgumentException("Invalid PASETO version"); } if (token.Purpose != PasetoConstants.Purposes.Public) { throw new ArgumentException("Invalid PASETO purpose"); } // decode payload var payload = Base64UrlEncoder.DecodeBytes(token.EncodedPayload); if (payload.Length < 64) { throw new SecurityTokenInvalidSignatureException("Payload does not contain signature"); } // extract signature from payload (rightmost 64 bytes) var signature = new byte[64]; Buffer.BlockCopy(payload, payload.Length - 64, signature, 0, 64); // decode payload JSON var message = new byte[payload.Length - 64]; Buffer.BlockCopy(payload, 0, message, 0, payload.Length - 64); token.SetPayload(Encoding.UTF8.GetString(message)); // pack var signedMessage = PreAuthEncode(new[] { Encoding.UTF8.GetBytes(PublicHeader), message, Base64UrlEncoder.DecodeBytes(token.EncodedFooter ?? string.Empty) }); // verify signature using valid keys foreach (var publicKey in keys) { var signer = new Ed25519Signer(); signer.Init(false, publicKey.KeyParameters); signer.BlockUpdate(signedMessage, 0, signedMessage.Length); var isValidSignature = signer.VerifySignature(signature); if (isValidSignature) { return(new PasetoSecurityToken(token)); } } throw new SecurityTokenInvalidSignatureException("Invalid PASETO signature"); }
/// <summary> /// Adds a specific <see cref="SecurityKey"/> to sign tokens issued by the OpenID Connect server. /// </summary> /// <param name="credentials">The options used to configure the OpenID Connect server.</param> /// <param name="key">The key used to sign security tokens issued by the server.</param> /// <returns>The signing credentials.</returns> public static IList <SigningCredentials> AddKey( [NotNull] this IList <SigningCredentials> credentials, [NotNull] SecurityKey key) { if (credentials == null) { throw new ArgumentNullException(nameof(credentials)); } if (key == null) { throw new ArgumentNullException(nameof(key)); } if (key.IsSupportedAlgorithm(SecurityAlgorithms.RsaSha256Signature)) { var x509SecurityKey = key as X509SecurityKey; if (x509SecurityKey != null) { return(credentials.AddCertificate(x509SecurityKey.Certificate)); } var x509AsymmetricSecurityKey = key as X509AsymmetricSecurityKey; if (x509AsymmetricSecurityKey != null) { // The X.509 certificate is not directly accessible when using X509AsymmetricSecurityKey. // Reflection is the only way to get the certificate used to create the security key. var field = typeof(X509AsymmetricSecurityKey).GetField( name: "certificate", bindingAttr: BindingFlags.Instance | BindingFlags.NonPublic); Debug.Assert(field != null); return(credentials.AddCertificate((X509Certificate2)field.GetValue(x509AsymmetricSecurityKey))); } // Create an empty security key identifier. var identifier = new SecurityKeyIdentifier(); var rsaSecurityKey = key as RsaSecurityKey; if (rsaSecurityKey != null) { // Resolve the underlying algorithm from the security key. var algorithm = (RSA)rsaSecurityKey.GetAsymmetricAlgorithm( algorithm: SecurityAlgorithms.RsaSha256Signature, requiresPrivateKey: false); Debug.Assert(algorithm != null, "SecurityKey.GetAsymmetricAlgorithm() shouldn't return a null algorithm."); // Export the RSA public key to extract a key identifier based on the modulus component. var parameters = algorithm.ExportParameters(includePrivateParameters: false); Debug.Assert(parameters.Modulus != null, "RSA.ExportParameters() shouldn't return a null modulus."); // Only use the 40 first chars of the base64url-encoded modulus. var kid = Base64UrlEncoder.Encode(parameters.Modulus); kid = kid.Substring(0, Math.Min(kid.Length, 40)).ToUpperInvariant(); identifier.Add(new RsaKeyIdentifierClause(algorithm)); identifier.Add(new LocalIdKeyIdentifierClause(kid)); } // Mark the security key identifier as read-only to // ensure it can't be altered during a request. identifier.MakeReadOnly(); credentials.Add(new SigningCredentials(key, SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256Digest, identifier)); return(credentials); } else if (key.IsSupportedAlgorithm(SecurityAlgorithms.HmacSha256Signature)) { // When using an in-memory symmetric key, no identifier clause can be inferred from the key itself. // To prevent the built-in security token handlers from throwing an exception, a default identifier is added. var identifier = new SecurityKeyIdentifier(new LocalIdKeyIdentifierClause("Default")); // Mark the security key identifier as read-only to // ensure it can't be altered during a request. identifier.MakeReadOnly(); credentials.Add(new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature, SecurityAlgorithms.Sha256Digest, identifier)); return(credentials); } throw new InvalidOperationException("A signature algorithm cannot be automatically inferred from the signing key. " + "Consider using 'options.SigningCredentials.Add(SigningCredentials)' instead."); }
private async Task <string> CreateTokenAsync( [NotNull] string type, [NotNull] AuthenticationTicket ticket, [NotNull] OpenIddictOptions options, [NotNull] HttpContext context, [NotNull] OpenIdConnectRequest request, [NotNull] ISecureDataFormat <AuthenticationTicket> format) { Debug.Assert(!(options.DisableTokenRevocation && options.UseReferenceTokens), "Token revocation cannot be disabled when using reference tokens."); Debug.Assert(type == OpenIdConnectConstants.TokenUsages.AccessToken || type == OpenIdConnectConstants.TokenUsages.AuthorizationCode || type == OpenIdConnectConstants.TokenUsages.RefreshToken, "Only authorization codes, access and refresh tokens should be created using this method."); // When sliding expiration is disabled, the expiration date of generated refresh tokens is fixed // and must exactly match the expiration date of the refresh token used in the token request. if (request.IsTokenRequest() && request.IsRefreshTokenGrantType() && !options.UseSlidingExpiration && type == OpenIdConnectConstants.TokenUsages.RefreshToken) { var properties = request.GetProperty <AuthenticationTicket>( OpenIddictConstants.Properties.AuthenticationTicket)?.Properties; Debug.Assert(properties != null, "The authentication properties shouldn't be null."); ticket.Properties.ExpiresUtc = properties.ExpiresUtc; } if (options.DisableTokenRevocation) { return(null); } var descriptor = new OpenIddictTokenDescriptor { AuthorizationId = ticket.GetProperty(OpenIddictConstants.Properties.AuthorizationId), CreationDate = ticket.Properties.IssuedUtc, ExpirationDate = ticket.Properties.ExpiresUtc, Principal = ticket.Principal, Status = OpenIddictConstants.Statuses.Valid, Subject = ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.Subject), Type = type }; foreach (var property in ticket.Properties.Items) { descriptor.Properties.Add(property); } string result = null; // When reference tokens are enabled or when the token is an authorization code or a // refresh token, remove the unnecessary properties from the authentication ticket. if (options.UseReferenceTokens || (type == OpenIdConnectConstants.TokenUsages.AuthorizationCode || type == OpenIdConnectConstants.TokenUsages.RefreshToken)) { ticket.Properties.IssuedUtc = ticket.Properties.ExpiresUtc = null; ticket.RemoveProperty(OpenIddictConstants.Properties.AuthorizationId) .RemoveProperty(OpenIdConnectConstants.Properties.TokenId); } // If reference tokens are enabled, create a new entry for // authorization codes, refresh tokens and access tokens. if (options.UseReferenceTokens) { // Note: the data format is automatically replaced at startup time to ensure // that encrypted tokens stored in the database cannot be considered as // valid tokens if the developer decides to disable reference tokens support. descriptor.Ciphertext = format.Protect(ticket); // Generate a new crypto-secure random identifier that will be // substituted to the ciphertext returned by the data format. var bytes = new byte[256 / 8]; options.RandomNumberGenerator.GetBytes(bytes); result = Base64UrlEncoder.Encode(bytes); // Compute the digest of the generated identifier and use // it as the hashed identifier of the reference token. // Doing that prevents token identifiers stolen from // the database from being used as valid reference tokens. using (var algorithm = SHA256.Create()) { descriptor.Hash = Convert.ToBase64String(algorithm.ComputeHash(bytes)); } } // Otherwise, only create a token metadata entry for authorization codes and refresh tokens. else if (type != OpenIdConnectConstants.TokenUsages.AuthorizationCode && type != OpenIdConnectConstants.TokenUsages.RefreshToken) { return(null); } // If the client application is known, associate it with the token. if (!string.IsNullOrEmpty(request.ClientId)) { var application = await Applications.FindByClientIdAsync(request.ClientId, context.RequestAborted); if (application == null) { throw new InvalidOperationException("The client application cannot be retrieved from the database."); } descriptor.ApplicationId = await Applications.GetIdAsync(application, context.RequestAborted); } // If a null value was returned by CreateAsync(), return immediately. var token = await Tokens.CreateAsync(descriptor, context.RequestAborted); if (token == null) { return(null); } // Throw an exception if the token identifier can't be resolved. var identifier = await Tokens.GetIdAsync(token, context.RequestAborted); if (string.IsNullOrEmpty(identifier)) { throw new InvalidOperationException("The unique key associated with a refresh token cannot be null or empty."); } // Restore the token identifier using the unique // identifier attached with the database entry. ticket.SetTokenId(identifier); // Dynamically set the creation and expiration dates. ticket.Properties.IssuedUtc = descriptor.CreationDate; ticket.Properties.ExpiresUtc = descriptor.ExpirationDate; // Restore the authorization identifier using the identifier attached with the database entry. ticket.SetProperty(OpenIddictConstants.Properties.AuthorizationId, descriptor.AuthorizationId); if (!string.IsNullOrEmpty(result)) { Logger.LogTrace("A new reference token was successfully generated and persisted " + "in the database: {Token} ; {Claims} ; {Properties}.", result, ticket.Principal.Claims, ticket.Properties.Items); } return(result); }
private async Task <AuthenticationTicket> ReceiveTokenAsync( [NotNull] string type, [NotNull] string value, [NotNull] OpenIddictOptions options, [NotNull] HttpContext context, [NotNull] OpenIdConnectRequest request, [NotNull] ISecureDataFormat <AuthenticationTicket> format) { Debug.Assert(!(options.DisableTokenRevocation && options.UseReferenceTokens), "Token revocation cannot be disabled when using reference tokens."); Debug.Assert(type == OpenIdConnectConstants.TokenUsages.AccessToken || type == OpenIdConnectConstants.TokenUsages.AuthorizationCode || type == OpenIdConnectConstants.TokenUsages.RefreshToken, "Only authorization codes, access and refresh tokens should be validated using this method."); string identifier; AuthenticationTicket ticket; TToken token; if (options.UseReferenceTokens) { string hash; try { // Compute the digest of the received token and use it // to retrieve the reference token from the database. using (var algorithm = SHA256.Create()) { hash = Convert.ToBase64String(algorithm.ComputeHash(Base64UrlEncoder.DecodeBytes(value))); } } // Swallow format-related exceptions to ensure badly formed // or tampered tokens don't cause an exception at this stage. catch { return(null); } // Retrieve the token entry from the database. If it // cannot be found, assume the token is not valid. token = await Tokens.FindByHashAsync(hash, context.RequestAborted); if (token == null) { Logger.LogInformation("The reference token corresponding to the '{Hash}' hashed " + "identifier cannot be found in the database.", hash); return(null); } identifier = await Tokens.GetIdAsync(token, context.RequestAborted); if (string.IsNullOrEmpty(identifier)) { Logger.LogWarning("The identifier associated with the received token cannot be retrieved. " + "This may indicate that the token entry is corrupted."); return(null); } // Extract the encrypted payload from the token. If it's null or empty, // assume the token is not a reference token and consider it as invalid. var ciphertext = await Tokens.GetCiphertextAsync(token, context.RequestAborted); if (string.IsNullOrEmpty(ciphertext)) { Logger.LogWarning("The ciphertext associated with the token '{Identifier}' cannot be retrieved. " + "This may indicate that the token is not a reference token.", identifier); return(null); } ticket = format.Unprotect(ciphertext); if (ticket == null) { Logger.LogWarning("The ciphertext associated with the token '{Identifier}' cannot be decrypted. " + "This may indicate that the token entry is corrupted or tampered.", await Tokens.GetIdAsync(token, context.RequestAborted)); return(null); } } else if (type == OpenIdConnectConstants.TokenUsages.AuthorizationCode || type == OpenIdConnectConstants.TokenUsages.RefreshToken) { ticket = format.Unprotect(value); if (ticket == null) { Logger.LogTrace("The received token was invalid or malformed: {Token}.", value); return(null); } // Retrieve the authorization code/refresh token entry from the database. // If it cannot be found, assume the authorization code/refresh token is not valid. token = await Tokens.FindByIdAsync(ticket.GetTokenId(), context.RequestAborted); if (token == null) { Logger.LogInformation("The token '{Identifier}' cannot be found in the database.", ticket.GetTokenId()); return(null); } identifier = await Tokens.GetIdAsync(token, context.RequestAborted); if (string.IsNullOrEmpty(identifier)) { Logger.LogWarning("The identifier associated with the received token cannot be retrieved. " + "This may indicate that the token entry is corrupted."); return(null); } } else { return(null); } // Restore the token identifier using the unique // identifier attached with the database entry. ticket.SetTokenId(identifier); // Dynamically set the creation and expiration dates. ticket.Properties.IssuedUtc = await Tokens.GetCreationDateAsync(token, context.RequestAborted); ticket.Properties.ExpiresUtc = await Tokens.GetExpirationDateAsync(token, context.RequestAborted); // Restore the authorization identifier using the identifier attached with the database entry. ticket.SetProperty(OpenIddictConstants.Properties.AuthorizationId, await Tokens.GetAuthorizationIdAsync(token, context.RequestAborted)); Logger.LogTrace("The token '{Identifier}' was successfully decrypted and " + "retrieved from the database: {Claims} ; {Properties}.", ticket.GetTokenId(), ticket.Principal.Claims, ticket.Properties.Items); return(ticket); }
public async Task <IActionResult> GenerateToken([FromBody] TokenRequestModel model, CancellationToken cancellationToken, [FromFilter] byte[]?addressHash = default) { if (!TryValidateModel(model)) { return(BadRequest(ModelState)); } switch (model.Type) { case TokenRequestType.SignatureRequest: { if (addressHash == default) { return(BadRequest()); } if (string.IsNullOrWhiteSpace(_serverOptions.Value.ServerId)) { return(StatusCode((int)HttpStatusCode.NotImplemented)); } return(Ok(new TokenResponseModel { Token = Base64UrlEncoder.Encode(addressHash) })); } } var query = new ResourceQuery(); var user = _users.Get(query, cancellationToken).SingleOrDefault(); if (user == default) { return(Unauthorized()); } switch (model.Type) { case TokenRequestType.UsernamePassword: break; case TokenRequestType.SignatureRequest: { Debug.Assert(addressHash != default); Debug.Assert(!string.IsNullOrWhiteSpace(_serverOptions.Value.ServerId)); // Challenge = sha256(WyHash64(IP)[8]:ServerId[16]:Nonce[24]) var buffer = Crypto.Nonce(48); for (var i = 0; i < _serverOptions.Value.ServerId.Length; i++) { buffer[i] = (byte)_serverOptions.Value.ServerId[i]; } for (var i = 8; i < addressHash.Length; i++) { buffer[i] = addressHash[i]; } break; } case TokenRequestType.SignatureResponse: { break; } case null: break; default: throw new ArgumentOutOfRangeException(); } var issuedAt = _timestamps(); var expiresAt = issuedAt + _tokenOptions.Value.Lifetime; var claims = new[] { // https://tools.ietf.org/html/rfc7519#section-4.1.2 new Claim(JwtRegisteredClaimNames.Sub, model.Identity ?? string.Empty), // https://tools.ietf.org/html/rfc7519#section-4.1.4 new Claim(JwtRegisteredClaimNames.Exp, expiresAt.ToUnixTimeSeconds().ToString()), // https://tools.ietf.org/html/rfc7519#section-4.1.6 new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), }; var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_tokenOptions.Value?.SigningKey ?? throw new InvalidOperationException( _localizer.GetString("Bearer authentication requires a valid signing key")))); var signing = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); string token; switch (_tokenOptions.Value.Format) { case TokenFormat.JsonWebToken: token = IssueJwtToken(claims, signing); break; case TokenFormat.JsonWebEncryption: var encrypting = await _encryptionKeyStore.GetCredentialsAsync(); token = IssueJweToken(claims, signing, encrypting); break; default: throw new ArgumentOutOfRangeException(); } var response = new TokenResponseModel { Token = token }; return(Ok(response)); }
public void EncodedLength_is_negative___throws_ArgumentOutOfRange(int encodedLength) { var sut = new Base64UrlEncoder(); Assert.Throws <ArgumentOutOfRangeException>(() => sut.GetDecodedLength(encodedLength)); }
public void EncodedLength_given_malformed___throws_MalformedInput(int encodedLength) { var sut = new Base64UrlEncoder(); Assert.Throws <FormatException>(() => sut.GetDecodedLength(encodedLength)); }