JWT Encryption Error when using EncryptingCredentials in token generation method - asp.net-core

I implemented JWT token in my ASP.net core 3.1 web api.
In the token generation method when I try to use EncryptingCredentials I got the below error :
IDX10662: The KeyWrap algorithm 'System.String' requires a key size of 'System.Int32' bits. Key '', is of size:'System.Int32'. (Parameter 'Length')
This is my Token generation method :
public async Task<string> GenerateAsync(User user)
{
var secretKey = Encoding.UTF8.GetBytes(_siteSetting.JwtSettings.SecretKey); //"SampleSecretKeyChange"
var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(secretKey), SecurityAlgorithms.HmacSha256Signature);
var encryptionkey = Encoding.UTF8.GetBytes(_siteSetting.JwtSettings.Encryptkey); //"AbcdefgHijklmnopk123456"
var encryptingCredentials = new EncryptingCredentials(new SymmetricSecurityKey(encryptionkey), SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128CbcHmacSha256);
var descriptor = new SecurityTokenDescriptor
{
Issuer = _siteSetting.JwtSettings.Issuer,
Audience = _siteSetting.JwtSettings.Audience,
IssuedAt = DateTime.Now,
NotBefore = DateTime.Now.AddMinutes(_siteSetting.JwtSettings.NotBeforeMinutes),
Expires = DateTime.Now.AddMinutes(_siteSetting.JwtSettings.ExpirationMinutes),
SigningCredentials = signingCredentials,
EncryptingCredentials = encryptingCredentials,
Subject = new ClaimsIdentity(_getClaims(user))
};
var tokenHandler = new JwtSecurityTokenHandler();
var securityToken = tokenHandler.CreateToken(descriptor);
var jwt = tokenHandler.WriteToken(securityToken);
return await Task.FromResult(jwt);
}

After of all research that I did, I found the problem. When you want to use EncryptingCredentials with the conditions that I described the Encryptkey key length must be equal 16 characters.

Related

GetDiscoveryDocumentAsync can not find the authority url - IdentiyServer4

I was trying to replace obsolete IdentityServer methods and types and as I had been warned about, I tried to replace DiscoveryClient() and TokenClient() with appropiate methods like in the examples of the latest identity server docs. When I try to get related endpoints by GetDiscoveryDocumentAsync it returns null even though I could read those values with current code and also get those values on browser.
Besides, when I by-pass the step of discovery and supplying the direct token endpoint RequestTokenAsync returns null because of Not Found exception.
For the sake of clearity of the question I should say that I have not changed anything (its settings or endpoints) in my Identity server project (from which I try to get access token).
Followings are my previous and updated code to achieve what I've described. Any help or suggestion is appreciated. Thanks in advance.
Previous Code (Working):
var testServer = new TestServer(builder);
var client = testServer.CreateClient();
client.BaseAddress = new Uri("http://localhost:5000");
var discoClient = new DiscoveryClient(AuthorityUrl) {Policy = {RequireHttps = false}};
var disco = discoClient.GetAsync().Result;
var tokenClient = new TokenClient(disco.TokenEndpoint, ClientId, ClientSecret);
var tokenResponse = tokenClient.RequestClientCredentialsAsync(Scope).Result;
client.SetBearerToken(tokenResponse.AccessToken);
Updated Code (Not Working):
var testServer = new TestServer(builder);
var client = testServer.CreateClient();
client.BaseAddress = new Uri("http://localhost:5000");
//var discoClient = new DiscoveryClient(AuthorityUrl) {Policy = {RequireHttps = false}};
var disco = client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest()
{ Address = AuthorityUrl, Policy = new DiscoveryPolicy() { RequireHttps = false, Authority = AuthorityUrl} }).Result;
;
if (disco.IsError)
{
throw new Exception(disco.Error);
}
//var tokenClient = new TokenClient(disco.TokenEndpoint, ClientId, ClientSecret);
var tokenClient = client.RequestTokenAsync(new TokenRequest()
{ Address = disco.TokenEndpoint, ClientId = ClientId, ClientSecret = ClientSecret, GrantType = GrantType}).Result;
//var tokenResponse = tokenClient.RequestClientCredentialsAsync(Scope).Result;
client.SetBearerToken(tokenClient.AccessToken);
return client;
Edit:
Updated my code as shown below and still getting the same error.
var testServer = new TestServer(builder);
var client = testServer.CreateClient();
client.BaseAddress = new Uri("http://localhost:5000");
//var discoClient = new DiscoveryClient(AuthorityUrl) {Policy = {RequireHttps = false}};
var disco = await client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest()
{ Address = AuthorityUrl, Policy = new DiscoveryPolicy() { RequireHttps = false, Authority = AuthorityUrl } });
;
if (disco.IsError)
{
throw new Exception(disco.Error);
}
//var tokenClient = new TokenClient(disco.TokenEndpoint, ClientId, ClientSecret);
var tokenClient =await client.RequestTokenAsync(new ClientCredentialsTokenRequest()
{ Address = disco.TokenEndpoint, ClientId = ClientId, ClientSecret = ClientSecret, GrantType = GrantType , Scope = Scope});
client.SetBearerToken(tokenClient.AccessToken);
return client;
Error:
"Error connecting to AuthorityUrl/.well-known/openid-configuration: Not Found"

Cannot apply indexing with [] to an expression of type 'IConfiguration'

i need to create a login with jwt . when i use this code :
private string BuildToken(User user)
{
var claims = new[] {
new Claim(JwtRegisteredClaimNames.Sub, user.DisplayName),
new Claim(JwtRegisteredClaimNames.Email, user.Email),
new Claim(JwtRegisteredClaimNames.Birthdate, user.BirthDate.ToString()),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(_config["Jwt:Issuer"],
_config["Jwt:Issuer"],
claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
it show me this error :
Severity Code Description Project File Line Suppression State
Error CS0021 Cannot apply indexing with [] to an expression of type 'IConfiguration' StoreFinal C:\Users\Mr-Programer\Desktop\New folder\StoreFinal\StoreFinal\Areas\Admin\Controllers\LoginController.cs 65 Active
show me in error in this line : var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
i add this using in namespace and it solved by this :
using IConfiguration = Microsoft.Extensions.Configuration.IConfiguration;
Add using Microsoft.Extensions.Configuration

How to renew a SAML assertion with ADFS?

I am able to succesfully request a SAML SecurityToken from ADFS with a user name and password using the following code:
private GenericXmlSecurityToken IssueToken(string userName, string password)
{
var relyingPartyIdentifier = new EndpointReference("https://mywebsite.net");
var stsAddress = "https://myadfs.com/adfs/services/trust/13/usernamemixed";
var wsBinding = new WS2007HttpBinding(SecurityMode.TransportWithMessageCredential);
wsBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
wsBinding.Security.Message.EstablishSecurityContext = false;
wsBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
var trustChannelFactory = new WSTrustChannelFactory(wsBinding, stsAddress);
trustChannelFactory.Credentials.SupportInteractive = false;
trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;
trustChannelFactory.Credentials.UserName.Password = password;
trustChannelFactory.Credentials.UserName.UserName = userName;
var tokenRequest = new RequestSecurityToken()
{
RequestType = RequestTypes.Issue,
AppliesTo = relyingPartyIdentifier,
KeyType = KeyTypes.Symmetric,
TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1"
};
tokenRequest.Claims.Add(new RequestClaim(ClaimTypes.Name));
tokenRequest.Claims.Add(new RequestClaim(ClaimTypes.Email));
var channel = trustChannelFactory.CreateChannel();
var token = (GenericXmlSecurityToken)channel.Issue(tokenRequest);
return token;
}
However, that token will expire in an hour. So my question is, how can I issue a new token based on the initial token, such that I don't need to remember the username and password?
I tried the following code:
public GenericXmlSecurityToken RenewToken(SecurityToken token)
{
var relyingPartyIdentifier = new EndpointReference("https://mywebsite.net");
var stsAddress = "https://myadfs.com/adfs/services/trust/13/usernamemixed";
var wsBinding = new WS2007FederationHttpBinding(WSFederationHttpSecurityMode.TransportWithMessageCredential);
wsBinding.Security.Message.EstablishSecurityContext = false;
wsBinding.Security.Message.IssuerAddress = new EndpointAddress(new Uri(stsAddress));
wsBinding.Security.Message.IssuerBinding = new WS2007HttpBinding(SecurityMode.TransportWithMessageCredential);
wsBinding.Security.Message.IssuerMetadataAddress = new EndpointAddress("https://myadfs.com/adfs/services/trust/mex");
var trustChannelFactory = new WSTrustChannelFactory(wsBinding, stsAddress);
trustChannelFactory.Credentials.SupportInteractive = false;
trustChannelFactory.Credentials.UserName.UserName = Settings.UserName;
trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;
var tokenRequest = new RequestSecurityToken()
{
RequestType = RequestTypes.Renew,
RenewTarget = new SecurityTokenElement(token),
AppliesTo = relyingPartyIdentifier,
KeyType = KeyTypes.Symmetric,
TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1",
};
tokenRequest.Claims.Add(new RequestClaim(ClaimTypes.Name));
tokenRequest.Claims.Add(new RequestClaim(ClaimTypes.Email));
var channel = trustChannelFactory.CreateChannel();
// This call fails with:
// SecurityNegotiationException: Secure channel cannot be opened because security negotiation with the remote endpoint has failed. This may be due to absent or incorrectly specified EndpointIdentity in the EndpointAddress used to create the channel. Please verify the EndpointIdentity specified or implied by the EndpointAddress correctly identifies the remote endpoint. '
// Inner Exception: FaultException: An error occurred when verifying security for the message.
var newToken = (GenericXmlSecurityToken)channel.Issue(tokenRequest);
return newToken;
}
But the call to channel.Issue fails with the following exception:
SecurityNegotiationException: Secure channel cannot be opened because security negotiation with the remote endpoint has failed. This may be due to absent or incorrectly specified EndpointIdentity in the EndpointAddress used to create the channel. Please verify the EndpointIdentity specified or implied by the EndpointAddress correctly identifies the remote endpoint. '
Inner Exception: FaultException: An error occurred when verifying security for the message.
I am completely clueless as how to get a new token using the initial token.

Web api token based authentication:- Failed to decode token from base64 string to get user name and password

I am using Web Api Token Based Authentication using OWIN Middleware; the token is generated successfully but i can't decode it; e.g. i cannot extract user name and password from it;
Here is my configuration
my start up code
var oAuthAuthorizationServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(oAuthAuthorizationServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
my code that is ued to send the token is
static async Task RunAsync(JObject token)
{
using (var client = new HttpClient())
{
client.Timeout = new TimeSpan(1000000000000);
client.BaseAddress = new Uri("http://localhost/SampleApp/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(token["token_type"].ToString(),
token["access_token"].ToString());
}}
my authetication code
var authenticationSchema = httpContext.Request.Headers["Authorization"];
if (!String.IsNullOrWhiteSpace(authenticationSchema))
authentication = AuthenticationHeaderValue.Parse(authenticationSchema);
if (authentication != null)
{
var unencoded = Convert.FromBase64String(authentication.Parameter);
var userpw = Encoding.GetEncoding("iso-8859- 1").GetString(unencoded);
var creds = userpw.Split(':');
return new Tuple<string, string>(creds[0], creds[1]);
}
and the code failed when trying to decode the code from base64 string
note:- my sample token is
3K8vHKHA2ZsKfKbvzUbo4a2sat2JLzvvyxCZ0KSD6s1wUS3t3oDPXuQ89aTmGpsG4ZL8O0cr8M9EUeZGtdM6FBwR7gLFcLZkTaimFGKyyZMNce9trQavVTzs6gam6qach1rPTLv_gIYGgPmM-401PZsr89BIXw4acTpJL3KbXs8y7PQ-o-eTV2IA8euCVkqC02iEnAzmS0SwhBouISCC-HvcNpE2aNixg4JXEt8EslU
you can see the attached for the exception
As far as I can see from the code, access token is sent plain to server; but you need to encode the access token on the client side like:
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(token["token_type"].ToString(),
Convert.ToBase64String(Encoding.GetEncoding("iso-8859-1").GetBytes(token["access_token"].ToString())));
Then you can convert access token from base64 string on the server side. The access token string value you provided is not a valid Base64 string, so as expressed in the exception message.

How to pass a certificate to WSTrust to get Saml Token

Here is an example of getting tokem using WSTrustChannelFactory. From here.
var stsBinding = new WS2007HttpBinding();
stsBinding.Security.Mode = SecurityMode.TransportWithMessageCredential;
stsBinding.Security.Message.EstablishSecurityContext = false;
stsBinding.Security.Message.NegotiateServiceCredential = false;
stsBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
WSTrustChannelFactory trustChannelFactory = new WSTrustChannelFactory(
stsBinding
, new EndpointAddress(tokenurl)
);
trustChannelFactory.TrustVersion = System.ServiceModel.Security.TrustVersion.WSTrust13;
X509Store myStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
myStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection coll = myStore.Certificates.Find(X509FindType.FindBySerialNumber, "MycertSerialNumber", true);
X509Certificate2 cert = coll[0];
trustChannelFactory.Credentials.ClientCertificate.Certificate = cert;
WSTrustChannel channel = (WSTrustChannel)trustChannelFactory.CreateChannel();
RequestSecurityToken rst = new RequestSecurityToken(RequestTypes.Issue, keyType);
rst.AppliesTo = new EndpointAddress(realm);
RequestSecurityTokenResponse rstr = null;
rst.TokenType = SecurityTokenTypes.Saml;
SecurityToken token = channel.Issue(rst, out rstr);
Now I don't have a username/password but the provider has given me certificate .pfx file.
How do I pass it to the WSTrushChannelFactory? I have tried using CertificateBinding but no success.
Updated Code above: 11/05/2014:
Getting this error: ID3242: The security token could not be authenticated or authorized.
Use the ClientCertificate property:
var stsBinding = new WS2007HttpBinding();
stsBinding.Security.Mode = SecurityMode.TransportWithMessageCredential;
stsBinding.Security.Message.EstablishSecurityContext = false;
stsBinding.Security.Message.NegotiateServiceCredential = false;
// select the authentication mode of Client Certificate
stsBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
var wifChannelFactory = new WSTrustChannelFactory(stsBinding, stsEndpoint);
wifChannelFactory.TrustVersion = TrustVersion.WSTrust13;
// Supply the credentials
wifChannelFactory.Credentials.ClientCertificate.Certificate = config.Certificate;
The PFX you can import to your certificate store via the certmgr.msc snapin. Make sure that the account your application is running as has access to the private key. You can reference it in the store using the x509certificate2 classes.
Here you go.
private static SecurityToken RequestSecurityToken()
{
// set up the ws-trust channel factory
var factory = new WSTrustChannelFactory(
new UserNameWSTrustBinding(
SecurityMode.TransportWithMessageCredential),
_idpAddress);
factory.TrustVersion = TrustVersion.WSTrust13;
var authCertificate = X509.LocalMachine.My.Thumbprint.Find(Properties.Settings.Default.RassCertificateThumbprint).FirstOrDefault();
if (authCertificate == null)
throw new InternalException(String.Format("No atuhentication certificate found in store with thumbprint {0}.", Properties.Settings.Default.ClientCertificateThumbprint));
// overenie je na zaklade certifikatu RASS
factory.Credentials.ClientCertificate.Certificate = authCertificate;
// create token request
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
KeyType = KeyTypes.Symmetric,
AppliesTo = new EndpointReference(_serviceAddress.AbsoluteUri)
};
// request token and return
return factory.CreateChannel().Issue(rst);
}
BTW: #Mitch is right about access to the private key. I just took your method and replaced few lines of code.