WCF: Matching a specific WS-Security scheme (Signature, Encrypt, UserPass) - asp.net

I'm trying to match a specific WS-Security specification from a vendor. The following works in SOAP UI, which I am now trying to recreate in C# WCF (targeting .NET 4.7.2 or 4.8):
Username/Password element
Timestamp: 9000ms
Signature (cert A):
Sign with certificate
Prepend signature element
Sign timestamp and username token
Encryption (cert B):
Create encrypted key
Encrypt body, timestamp, and username token
Everything I've tried so far seems to encrypt everything in the WS-Security header except for the timestamp.
References followed:
WCF - Separate x509 for Signing and Encryption
SOAP UI Setup - Signature:
SOAP UI Setup - Encryption:

You can try to use IClientMessageInspectortor add a header to the message. In the BeforeSendRequest method, you can add a custom header to the outgoing message.Here is a demo:
public class CustomMessageInspector : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
return;
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
MessageHeader header = MessageHeader.CreateHeader("Testreply", "http://Test", "Test");
request.Headers.Add(header);
return null;
}
}
[AttributeUsage(AttributeTargets.Interface)]
public class CustContractBehaviorAttribute : Attribute, IContractBehavior
{
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new CustomMessageInspector());
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
return;
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
return;
}
}
Add CustContractBehaviorAttribute to apply it:

Related

Adding User data to the JWT payload for spring security OAUth2

I am using a spring security OAuth2 using JWT tokens for some time but now I need to add 2 user defined values to the JWT token.
So when I added an additional set of parameters to the request /oauth/token?grant_type=client_credentials&user_value=1234567890.
The user_value above was for demonstrative purposes. I can trace it all the way into my CustomTokenEnhancer (I connected this as a way to pass this information all the way through). All the request parameters are visible through OAuth2Authentication authentication which is passed to my CustomTokenEnhancer.
Now I can add this information to the additional information which I see returned to me as part of the token request. See below.
{
"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicGhpLWFwaSJdLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwib3JnYW5pemF0aW9uIjoicGhpLXVzZXJtZ3RuIiwidXNlcl90b2tlbiI6IjEyMzQ1Njc4OTAiLCJleHAiOjE0ODczNjc2NzEsImF1dGhvcml0aWVzIjpbIlJPTEVfQ0xJRU5UIl0sImp0aSI6IjFlZDMzZTAxLTc1ZGUtNDNjZC1hMzk2LTFkMzk2N2Y1NDQ5OCIsImNsaWVudF9pZCI6InBoaS11c2VyIn0.p628BNaaGljypEcGXZMkstNeTN-221qzzNQQ0npxDLTszWaXkgXqsBnBbKf9XMEtWTeCQkIszC9ne1Ei2X5IWTskhLT9Rko-8K7Jq-mXUc6HJZW-3tGV5rRer8Eyyw1wysW9Jiyp7sPkN-TIx12A70f_LHm6PrRR4ECppHWADs-2DvYA30p8omT1_RTt2WlqC40mopUN2TBPkb1WulVpOUEpcP358Ox8oVP8VQRSkLGZKB_b0KZAK9KGjLg6WNh8RghZaBuYuJQpITe_0XEBs_JfwrHhcK1IGaoYwSS7IGp3Cima9OMljdzayDKRqlfSl3WhaBuFmD1S37p-OVQL0A",
"token_type":"bearer",
"expires_in":8967,
"scope":"read write",
"user_value":"1234567890",
"jti":"1ed33e01-75de-43cd-a396-1d3967f54498"
}
But I don't want these values to be visible this way. I want them to be added to the encrypted token.
I spent some time looking and it isn't clear how i actually add that. This should be possible, shouldn't it?
Inside your own TokenEnhancer you have to encode it again:
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
// Generate additional Information [...]
// Write it to the token
((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(addInfo);
// Encode Token to JWT
String encoded = super.encode(accessToken, authentication);
// Set JWT as value of the token
((DefaultOAuth2AccessToken) accessToken).setValue(encoded);
return accessToken;
}
You could solve this with the JwtHelper methods, but I just extended JwtAccessTokenConverter, so I could just use encode and decode.
When instantiating your Token enhancer, you have to add the keystore information:
private CustomTokenEnhancer jwtCustomEnhancer() {
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "***".toCharArray());
CustomTokenEnhancer converter = new CustomTokenEnhancer();
converter.setKeyPair(keyStoreKeyFactory.getKeyPair("jwt"));
return converter;
}
I did something similar passing the value as a granted authority with help of user details service (not the token enhancer). At the client side, I wrote an extractor to retrieve the values from the principal injected by spring as type OAuth2Authentication. The following code is in Scala, but you may easily adapt to Java:
/**
* Mix-in to implicitly extract entity or identity from the principal.
*/
trait AuthorityExtractor {
def _contextName(implicit principal: OAuth2Authentication) = id(principal, "CONTEXT_")
def _entityId(implicit principal: OAuth2Authentication) = id(principal, "ENTITY_ID_")
def _userId(implicit principal: OAuth2Authentication) = id(principal, "USER_ID_")
def _identityId(implicit principal: OAuth2Authentication) = id(principal, "SELF_ID_")
private def id(principal: OAuth2Authentication, prefix: String) = {
import collection.JavaConversions._
principal
.getAuthorities
.filter(_.toString.startsWith(prefix))
.map(_.toString.substring(prefix.length))
.headOption.getOrElse("")
}
}
I extend JwtAccessTokenConverter class like that:
public class FooJwtAccessTokenConverter extends JwtAccessTokenConverter {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
DefaultOAuth2AccessToken fooAccessToken = new DefaultOAuth2AccessToken(accessToken);
fooAccessToken.getAdditionalInformation().put("foo_property", "foo");
return super.enhance(scaAccessToken, authentication);
}
In my AuthotizationServerConfig I create this:
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter())
.authenticationManager(authenticationManager);
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
ScaJwtAccessTokenConverter accessTokenConverter = new ScaJwtAccessTokenConverter();
accessTokenConverter.setSigningKey("familia-mgpe"); // Parte da string de validação do token JWT.
return accessTokenConverter;
}

How to generate API key from .net core mvc service? (with Authentication in middleware)

I have a .NET core MVC rest service that restricts access to the API by validating a key.
It uses a middleware function that inspects the request for a valid key (before routing to the api function) and returns an unauthorized response if it is not valid.
My problem is how can I generate the key? Since all requests go through this middleware that checks if the key is valid, I need a way to generate the key.
I know it is common to have a separate identity server but I believe it is overkill for my situation, where a valid key gives access to the entire API. There is no permissions structure.
I could examine the incoming request for some indication that the user is trying to authenticate with a username+password and return the key, but this does not seem correct.
Is there a way I can allow a single service call to skip the middleware?
The middleware function:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace PatrolLiveRestService.Middleware
{
public class KeyValidatorMiddleware
{
private readonly RequestDelegate _next;
//private IContactsRepository ContactsRepo { get; set; }
public KeyValidatorMiddleware(RequestDelegate next)//, IContactsRepository _repo)
{
_next = next;
//ContactsRepo = _repo;
}
public async Task Invoke(HttpContext context)
{
if (!context.Request.Headers.Keys.Contains("api-key"))
{
context.Response.StatusCode = 400; //Bad Request
await context.Response.WriteAsync("API Key is missing");
return;
}
if (!context.Request.Headers.Keys.Contains("device-id"))
{
context.Response.StatusCode = 400; //Bad Request
await context.Response.WriteAsync("Device ID is missing");
return;
}
string apiKey = context.Request.Headers["api-key"];
int deviceId;
if (!int.TryParse(context.Request.Headers["device-id"], out deviceId))
{
context.Response.StatusCode = 401; //UnAuthorized
await context.Response.WriteAsync("Invalid Device ID");
return;
}
if (!Common.ServiceCommon.IsKeyValid(apiKey, deviceId))
{
context.Response.StatusCode = 401; //UnAuthorized
await context.Response.WriteAsync("Invalid API Key");
return;
}
await _next.Invoke(context);
}
}
#region ExtensionMethod
public static class UserKeyValidatorsExtension
{
public static IApplicationBuilder ApplyUserKeyValidation(this IApplicationBuilder app)
{
app.UseMiddleware<KeyValidatorMiddleware>();
return app;
}
}
#endregion
}
Adding the middleware in Startup.cs Configure
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
app.ApplyUserKeyValidation();
...
}
I see three ways around:
check context.Request.Url inside your KeyValidatorMiddleware (e.g. for path /no-need-for-api-key-endpoint don't force api-key header presence
alter your KeyValidatorMiddleware - instead of rejecting requests without api-key header, just set the context.User property to a ClaimsPrincipal with claims being device id and api key for valid incoming requests and filter out requests without valid authorization on controller level via [Authorize] attribute
do not implement it as middleware, implement it as action filter instead

How to pass header in Azure endpoint..?

I am using Azure API , URL getting below error please help on this issue. please share codesnip, how to change in web.config and endpoints.
The HTTP request is unauthorized with client authentication scheme
'Anonymous'. The authentication header received from the server was
'AzureApiManagementKey
realm="https:/azure.azure-api.net/MethodName",name="Ocp-Apim-Subscription-Key",type="header"'.
I know this is a very old question still, my answer would help someone faces the same issue.
The solution is to create a custom endpoint behavior where you add a custom message handler to the binding parameters.
In the custom message handler, please add your request headers. After this, use any of the binding technique (like basichttpsbinding or NetHttpsBinding) with security mode as "Transport" and MessageEncoding as "Text" for creating soap client object. Add custom endpoint behavior to the soap client.
public class CustomEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
bindingParameters.Add(new Func<HttpClientHandler, HttpMessageHandler>(x =>
{
return new CustomMessageHandler(x);
}));
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
public void Validate(ServiceEndpoint endpoint) { }
}
public class CustomMessageHandler : DelegatingHandler
{
public CustomMessageHandler(HttpClientHandler handler)
{
InnerHandler = handler;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
request.Headers.Add("xxxx", "abcde");
return base.SendAsync(request, cancellationToken);
}
}
The console app to consume the service.
static async Task Main(string[] args)
{
var client = GetSOAPClient();
try
{
var result = await client.MyOperation().ConfigureAwait(false);
if(result.Body != null && result.Body.status == "Success")
{
Console.WriteLine(result.Body.myValue);
}
}
catch (Exception ex)
{
Console.WriteLine(ex?.Message);
}
Console.ReadKey();
}
static MyServiceClient GetSOAPClient()
{
NetHttpsBinding binding = new NetHttpsBinding();
binding.Security.Mode = BasicHttpsSecurityMode.Transport;
binding.MessageEncoding = NetHttpMessageEncoding.Text;
EndpointAddress ea = new EndpointAddress(new Uri("https://myazureurl"));
var client = new MyServiceClient(binding, ea);
client.Endpoint.EndpointBehaviors.Add(new CustomEndpointBehavior());
return client;
}
}
This is complaining that your Subscription key is wrong. If you check the response body, it will give you a readable message of what the real problem is. Double check you are entering the correct subscription key for your Azure API access.
You get your subscription key from the Developer Portal under your profile menu. You can see an example of the subscription key being used in this article under the section "Call an operation from the developer portal": https://learn.microsoft.com/en-us/azure/api-management/api-management-get-started
Also, the 'The HTTP request is unauthorized with client authentication scheme 'Anonymous'.' part of the message is a red herring and a separate problem with how responses work.

Best practice for deploying spring boot application on Amazon

I've devloped a chat bot application using the Facebook Messenger platform.
I used Spring Boot with embedded Tomcat for the web platform.
The application should run on Amazon aws, open to the WWW, and to be used as a webhook for recieving callbacks from Messenger over https.
I need an advice how to secure the application, so it won't be hacked or flooded with requests that are not coming from Facebook.
I thought to make the application require secured (ssl) connection, but using the "security.require_ssl=true" in application.properties didn't do the work. Perhaps I don't know what is the meaning of this and how to configure it propertly.
Is there a best practice how to block requests which are not https requests? Or a way to block requests which are coming outside Messenger in the application level?
Thank you very much!
EDIT
In the meantime, I blocked requests from other IPs in application layer using the handler interceptor:
#Configuration
public class MyWebApplicationInitializer implements WebApplicationInitializer, WebMvcConfigurer{
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptor() {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (! (request.getRemoteAddr().equals("173.252.88.66") || request.getRemoteAddr().equals("127.0.0.1")|| request.getRemoteAddr().equals("0:0:0:0:0:0:0:1"))){
logger.warn("Request is not coming from authorized remote address: " + request.getRemoteAddr()+". Rejecting");
response.getWriter().write("Unauthorized Address");
response.setStatus(401);
return false;
} else {
return true;
}
}
}
You should check the X-Hub-signature HTTP header available in the requests sent by Facebook to your webhook URL.
In your case, you may define a filter or interceptor for the verification of the signature. You can also do it in your controller as in the this example I found in RealTimeUpdateController.java from the spring social project.
private boolean verifySignature(String payload, String signature) throws Exception {
if (!signature.startsWith("sha1=")) {
return false;
}
String expected = signature.substring(5);
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
SecretKeySpec signingKey = new SecretKeySpec(applicationSecret.getBytes(), HMAC_SHA1_ALGORITHM);
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(payload.getBytes());
String actual = new String(Hex.encode(rawHmac));
return expected.equals(actual);
}
a lot to say so I am sure I will miss some points.
setting SSL is a first good thing but make sure you get a certificate. lets encrypt is a good thing if you dont want to pay for SSL certificate.
Just seeing aws provides an alternative to letsencrypt
Security Group You can see Security Group as something similar to a firewall so you can control which port is opened, external and internal flows.
Look at IAM which control who and how can get access to your AWS account
obvious : change your password. do not let default password for installation you could make on the instance
read some of https://aws.amazon.com/security/security-resources/ to get more information about what you can do
it won't be hacked or flooded with requests
sorry to say but most probably it will be - It does not need to be an advanced hacker to run scanner and scan IPs and check open ports / brute force login etc ...
Thanks to Guy Bouallet help I added the signature check.
I added it in my controller and not in the interceptor, to avoid the problem of How to read data twice in spring which seems a little complicated.
So here is it:
#RequestMapping(path = "/")
public void doHandleCallback(#RequestBody String body, #RequestHeader(value = "X-Hub-Signature") String signature) throws IOException {
if (!verifyRequestSignature(body.getBytes(), signature)){
logger.error ("Signature mismatch.");
throw new MismatchSignatureException(signature);
}
MessengerCallback callback = mapper.readValue(body, MessengerCallback.class);
logger.info("Incoming Callback: " + body );
for (EventData entry : callback.getEntry()) {
for (ReceivedMessagingObject message : entry.getMessaging()) {
if (message.isMessage() || message.isPostback()) {
doHandleMessage(message);
}
else if (message.isDelivery()){
doHandleDelivery(message);
}
}
}
}
private boolean verifyRequestSignature(byte[] payload, String signature) {
if (!signature.startsWith("sha1="))
return false;
String expected = signature.substring(5);
System.out.println("Expected signature: " + expected); //for debugging purposes
String hashResult = HmacUtils.hmacSha1Hex(APP_SECRET.getBytes(), payload);
System.out.println("Calculated signature: " + hashResult);
if (hashResult.equals(expected)) {
return true;
} else {
return false;
}
}
And this is the Exception handling class:
#ResponseStatus(value=HttpStatus.BAD_REQUEST, reason="Request Signature mismatch")
public class MismatchSignatureException extends RuntimeException {
private String signature;
public MismatchSignatureException(String signature) {
this.signature = signature;
}
#Override
public String getMessage() {
return "Signature mismatch: " + signature;
}

Getting User Id in Web Api handler when using Cachecow

I have a MVC Web Api project and am logging all requests and responses using a MessageHandler. When an api request comes in, the bearer token in the header lets Asp.Net do its thing and authenticates that user. The message handler therefore knows who the user is and we write that to a log file.
Now, to speed up things I'm caching with Cachecow. So I've added the cachecow handler after the MessageHandler and when a second request comes in, from a caching point of view everything works fine. The controller code is never hit and the response is returned from the cache.
However, the MessageHandler does not have a value for the User.Identity so I cannot tell who made the request.
I need to log all requests and identify who made them even when the code in the controllers is not hit.
I think one workaround is to force the api requests to pass the bearer token and user id in the header. That way I can check the user id claim and use that to log who made the request.
protected override async Task OutgoingMessageAsync(string correlationId, string requestInfo, byte[] message, string responseTimeMilliseconds)
{
await Task.Run(() =>
Debug.WriteLine(string.Format("{0} - Response: {1}\r\n{2}", correlationId, requestInfo, Encoding.UTF8.GetString(message))));
);
}
User identity is null when getting response from cache.
?HttpContext.Current.User.Identity
{System.Security.Claims.ClaimsIdentity}
[System.Security.Claims.ClaimsIdentity]: {System.Security.Claims.ClaimsIdentity}
AuthenticationType: null
IsAuthenticated: false
Name: null
Any ideas?
In authentication process, set object:
System.Threading.Thread.CurrentPrincipal = YourUserInformationObject;
This object need implement "System.Security.Principal.IPrincipal" Example
public class YourUserInformation : IPrincipal
{
public Int32 Id { get; set; }
public String NameUser { get; set; }
public IIdentity Identity { get; private set; }
public YourUserInformation()
{
this.Identity = new GenericIdentity(NameUser ?? "");
}
public bool IsInRole(string role) { return false; }
}
In authentication process you save object in System.Threading.Thread.CurrentPrincipal
public void Authentication(AuthorizationContext filterContext)
{
YourUserInformation user = YourMethodGetUserLogin();
System.Threading.Thread.CurrentPrincipal = user ;
}
Well you should create HttpContext from Request and there you will be able to use User.Identity object:
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var context = ((HttpContextBase)request.Properties["MS_HttpContext"]);
var uname = username = context.User.Identity.Name;
var response = await base.SendAsync(request, cancellationToken);
return response;
}
Also check this article: http://arcware.net/logging-web-api-requests/
Hoope this help!
try get in
System.Threading.Thread.CurrentPrincipal

Resources