WSS4J SOAP Signature validation without truststore - soap

I need to validate a signed SOAP message, extract the certificate and authenticate the certificate against a LDAP directory, which makes a trust store unnecessary. I have being using the WSS4J for a while now, but always with a local trust store. Taking a look on the official documentation and googling around, I couldn't find any reference to a scenario similar to mine. I was wondering if it would be possible to keep using the WSS4J in that case.

Yes you can use WSS4J for this use-case. WSS4J uses the SignatureTrustValidator by default to validate trust in signing certificates:
https://github.com/apache/ws-wss4j/blob/master/ws-security-dom/src/main/java/org/apache/wss4j/dom/validate/SignatureTrustValidator.java
You can plug your own implementation in there instead via:
https://github.com/apache/ws-wss4j/blob/66ab5fdbeeda0e0cbd6e317272dadd4417f6be91/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java#L863
If you are using CXF with WSS4J, there is a custom configuration constant that you can set that points to the Validator implementation for Signatures.

I faced the same problem and solved it in this way:
#EnableWs
#Configuration
public class WsConfiguration extends WsConfigurerAdapter {
#Bean
public Wss4jSecurityInterceptor securityInterceptor() throws Exception {
Wss4jSecurityInterceptor securityInterceptor = new Wss4jSecurityInterceptor();
securityInterceptor.setValidationActions("Signature");
WSSConfig wssConfig = WSSConfig.getNewInstance();
wssConfig.setValidator(new QName("http://www.w3.org/2000/09/xmldsig#", "Signature"), MySignatureTrustValidator.class);
securityInterceptor.setWssConfig(wssConfig);
//the rest of configuration
}
Note, that MySignatureTrustValidator must implement Validator

Related

Keycloak integration to external identity provider fails when validation tokens with JWKS URL

I'm configuring an external identity provider in my Keycloak instance and trying to get it to validate the tokens using a external JWKS URL. Using the converted PEM from JWKS works fine, the using the URL is not working.
The token validation fails upon login with the following message:
[org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider] (default task-4) Failed to make identity provider oauth callback: org.keycloak.broker.provider.IdentityBrokerException: token signature validation failed
I debugged the Keycloak server get more on the problem and found a "problem" in class JWKSUtils:
/**
* #author Marek Posolda
*/
public class JWKSUtils {
//...
public static Map<String, KeyWrapper> getKeyWrappersForUse(JSONWebKeySet keySet, JWK.Use requestedUse) {
Map<String, KeyWrapper> result = new HashMap<>();
for (JWK jwk : keySet.getKeys()) {
JWKParser parser = JWKParser.create(jwk);
if (jwk.getPublicKeyUse().equals(requestedUse.asString()) && parser.isKeyTypeSupported(jwk.getKeyType())) {
KeyWrapper keyWrapper = new KeyWrapper();
keyWrapper.setKid(jwk.getKeyId());
keyWrapper.setAlgorithm(jwk.getAlgorithm());
keyWrapper.setType(jwk.getKeyType());
keyWrapper.setUse(getKeyUse(jwk.getPublicKeyUse()));
keyWrapper.setVerifyKey(parser.toPublicKey());
result.put(keyWrapper.getKid(), keyWrapper);
}
}
return result;
}
//...
}
The if fails with a NullPointerException because the call jwk.getPublicKeyUse() returns null.
But I found out that it's null because the JWKS URL returns a single key without the attribute use, which is optional according to the specification. [https://www.rfc-editor.org/rfc/rfc7517#section-4.2]
Keycloak only accepts JWKS URLs that return all keys with the attribute use defined. But the IdP I'm trying to connect does not return that attribute in the key.
Given that situation, to who should I file an issue, the IdP or to Keycloak? Or is there something I'm doing wrong in the configuration?
I filed an issue with Keycloak about this exact problem in August 2019.
Their answer:
Consuming keys without validating alg and use is dangerous as such
Keycloak requires these to be present.
In my case, I contacted the IdP and they were able to populate the "use" parameter. If that is not an option, then you're pretty much stuck with your workaround.

How to validate RestTemplate response?

Spring supports annotation based validation at the controller level.
(1) Is it necessary to do such validations also at the RestTemplate level for responses from REST calls?
If the answer is Yes:
(2) Will there be support for that at the RestTemplate to validate responses from rest calls sometime in the future?
If the answer is No:
(3) why?
It is 2020 now and I still do not see the requested feature in place.
The #Valid is nice to automatically validate e.g. a posted RequestBody.
But for the validation of the body of a ResponseEntity fetched via RestTemplate, I do not see any fancy equivalent.
So the only option I know, is to do it on your own taken from here. Input is the class of your RequestEntitys body. input is the body itself.
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Input>> violations = validator.validate(input);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
So to answer your question:
Yes I would validate the response!
future (2020) has not brought the feature you and I miss
For the reason why this is missing, I also have no answer.
For me, the questions are quite big. :). As my understanding, you would like to ask the validation in REST service that Spring can support.
1. Is it necessary to do such validations also at the RestTemplate level for responses from REST calls?
Actually, it depends on your apps or your business. You can do at Controller or you can do in Service Level or even you can do your custom validation. For me, no one forces you to do anything.
However, as my experience, we should do the validation for sure. So my answer here is YES.
2. Will there be support for that at the RestTemplate to validate responses from rest calls sometime in the future?
I assume that you would like the detail of the validation?! Right?
Spring supports many things to do the validation. For the simple way, you can use PathVariable or RequestParameter.For example:
#GetMapping("/test/{name}")
private String test(#PathVariable(value = "name", required = true) String name){
//...
}
Spring will validate all requests, and respond with 400 Bad Request when the required parameter is missing or has a wrong type...
Spring also supports the JSR 303 Bean Validation: http://beanvalidation.org/1.0/spec/ For example in here:
public class MessageBean {
#NotNull
private String title;
#NotNull
private String message;
// getters/setters/etc
}
Or you would like to do the Custom User Response like:
#ExceptionHandler
#ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleException(MethodArgumentNotValidException exception) {
//....
return ErrorResponse.builder().message(errorMsg).build();
}
Some more details in here: https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-validation
So, it depends on the business which requires us to do the validation at any level.
3. If the answer is No: (3) why?
No need to answer this. :)
Hope that helps

Spring WS remove flexible URL, Restricting WSDL URL and service URL

I'm trying to make a Spring Boot Soap WebService application, and was following the Get Started (https://spring.io/guides/gs/producing-web-service/) example to learn how to do this.
I've created what I want, but I have two URL problems with this setup and I could not find what configuration should I change to fix this :
WSDL URL basic is localhost:8080/ws/countries.wsdl but anything like localhost:8080/ws/whatever/countries.wsdl is correct
service URL for SoapUI request is localhost:8080/ws but anything like localhost:8080/ws/whatever is correct
I know that this is a feature for Spring WS, but I want a fixed URL (without 'whatever' in both cases) and could not find what to change for this
There is no straight forward way to restrict the way you want.
SOAP service is not URL based.
SOAP message body describe the endpoint.
The thing you wanted is possible following way.
Changing URL mapping in ServletRegistrationBean to restrict URL access
Existing /ws/* mapping is the reason why all the /ws/whatever url successfully responded.
Change as new ServletRegistrationBean(servlet, "/ws");
Effect will be you can not request other than /ws URL
Now the problem is, you can not get WSDL by this mapping.
Solution to get WSDL
The DefaultWsdl11Definition is actually generating WSDL from XSD on every request.
Save countries.wsdl to resource folder as static WSDL file.
Remove DefaultWsdl11Definition bean.
Create a new SimpleWsdl11Definition bean as like
#Bean(name = "countries")
public SimpleWsdl11Definition orders() {
SimpleWsdl11Definition wsdl11Definition = new SimpleWsdl11Definition();
wsdl11Definition.setWsdl(new ClassPathResource("countries.wsdl"));
return wsdl11Definition;
}
Now add another static URL mapping in ServletRegistrationBean. As it will be finally look like new ServletRegistrationBean(servlet, "/ws", "/ws/countries.wsdl");
This practice is good for development phase as you can publish easily the changed definition. But it is recommended to use static-wsdl for production environment. Details ** here
Just change
return new ServletRegistrationBean(servlet, "/ws/*");
for example to
return new ServletRegistrationBean(servlet, new String[]{
"/ws/v1/countries.wsdl",
"/ws/v2/countries.wsdl"
});

Keycloak Custom Validation Output messages

I'm using jboss keycloak 1.5 final version.
I developed my custom user federation provider interfacing with keycloak properties and my user enterprise database.
My need is to send up to user the login interface custom error messages based on particular specific error related to my legacy user db.
I saw keycloak themes have a resources folder by which i can localize and add new messages. Then i can reference them by angular js using
$myMessage
notation. The problem is i want to rise up a message from keycloak server. My user federation provider implements UserFederationProvider interface. So i should have to override:
#Override
public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel credential) {
LOGGER.info("validCredentials(realm, credential)");
return CredentialValidationOutput.failed();
}
which seems to be the method i was looking for just because CredentialValidationOutput contains custom messages to be sent as validation output. The problem is this method is never called.
Why?
I'll post the answer found on my own.
It's necessary to develop your own Authenticator. For example refer to Keycloak UsernameAndForm and UsernameAndFormFactory implementation.
You can find them on Keycloak github source code:
https://github.com/keycloak/keycloak/tree/master/services/src/main/java/org/keycloak/authentication/authenticators/browser
The main validation method are:
public boolean validateUserAndPassword(AuthenticationFlowContext context, MultivaluedMap<String, String> inputData) {
...
}
public boolean validatePassword(AuthenticationFlowContext context, UserModel user, MultivaluedMap<String, String> inputData) {
...
}
From your custom user federation provider you can throw your custom exception and catch them in the two methods above adding:
catch (YourCustomException ex){
...
Response challengeResponse = context.form()
.setError("YOUR ERROR MESSAGE", me.getMandator()).createLogin();
context.failureChallenge(AuthenticationFlowError.INVALID_USER, challengeResponse);
return false;
}
Of course in your project you have to add
META-INF/service/org.keycloak.authentication.AuthenticatorFactory
In which you specify the full qualified name of your AuthenticatorFactory.
For a valid guide make reference to Keycloak User Guide 1.6.1 Final. Chapter 33.3

Why can't I get HAL support to work in grails 2.3.8?

I am following the directions in the docs, here:
http://grails.org/doc/2.3.8/guide/webServices.html#hypermedia
Why won't grails produce HAL-formatted output, as shown in the documentation?
I have a domain object which I have mapped with the #Resource annotation:
#Resource(uri='/documentCatalogs', formats = ['json', 'xml'], readOnly = true)
class DocumentCatalog {
String entityType
String actionCode
...
}
...and in my conf/spring/resources.groovy, I have configured the HAL JSON renderer beans:
import com.cscinfo.platform.api.formslibrary.DocumentCatalog
import grails.rest.render.hal.HalJsonCollectionRenderer
import grails.rest.render.hal.HalJsonRenderer
// Place your Spring DSL code here
beans = {
halDocumentCatalogRenderer(HalJsonRenderer, DocumentCatalog)
halDocumentCatalogCollectionRenderer(HalJsonCollectionRenderer, DocumentCatalog)
}
Using the debugger, I confirmed that the initialize() method on HalJsonRenderer is called and that it is constructed with the correct targetType.
I send a rest call using Postman:
http://localhost:8080/formslibrary/documentCatalogs/3
Accept application/hal+json
And I get back a response which is regular JSON and doesn't contain any links:
{
"class": "com.cscinfo.platform.api.formslibrary.DocumentCatalog",
"id": 3,
"actionCode": "WITH",
"entityType": "LLP",
...
}
What did I miss? Is there some plugin or configuration setting I have to enable for this behavior? Is there some additional mapping property somewhere that's not documented?
Figured it out! There are multiple aspects of the fix...
I had to add "hal" as one of the listed formats in the #Resource annotation:
#Resource(uri='/documentCatalogs', formats = ['json', 'xml', 'hal'])
Some hunting around in the debugger revealed that Grails will blithely ignore the Accept header, based on the UserAgent string that is sent from the client. (In my case, since I'm using Postman, it was the Google Chrome UA string.)
One workaround for the Accept header issue is to add ".hal" to the end of the URL:
http://localhost:8080/formslibrary/documentCatalogs/3.hal
This isn't a very good solution IMO, since the HAL URLs generated by the renderer don't end in ".hal" by default.
A better solution is to fix Grails' handling of the accept header by updating the config. In Config.groovy, you will see a line that says:
grails.mime.disable.accept.header.userAgents = ['Gecko', 'WebKit', 'Presto', 'Trident']
Change it to:
grails.mime.disable.accept.header.userAgents = ['None']
This forces Grails to honor the Accept header, regardless of the user agent.
Hope this helps somebody else who's hitting the same issue.
P.S. It's really helpful to put a breakpoint in the ResponseMimeTypesApi#getMimeTypesFormatAware(...) method.