What phase does CXF WSS4JOutInterceptor get invoked in? - cxf

What is the phase in which the WSS4JOutInterceptor is invoked?
I needed to make some changes to the headers in my custom interceptor.
But since the header is to be encrypted and signed, I need to make my changes before the WSS4JOutInterceptor gets invoked.
I specified in my custom interceptor that it needs to be added before WSS4J:
super(Phase.PRE_STREAM);
addBefore(WSS4JOutInterceptor.class.getName());
But that did not do it. When my interceptor is invoked, the headers are already signed.
I tried READ, PRE_STREAM, USER_STREAM...in all cases, the soap envelope is either empty, or the headers are already encrypted and signed.
How do I position my interceptor to get invoked before WSS4JOutInterceptor?
Update:
I see that the ordering of the interceptors is as follows:
setup [PolicyOutInterceptor]
pre-logical [MAPAggregatorImpl, HolderOutInterceptor, SwAOutInterceptor, WrapperClassOutInterceptor, SoapHeaderOutFilterInterceptor]
post-logical [SoapPreProtocolOutInterceptor]
prepare-send [MessageSenderInterceptor, GZIPOutInterceptor]
pre-stream [SoapMessageInterceptor, LoggingOutInterceptor, TransformOutInterceptor, AttachmentOutInterceptor, StaxOutInterceptor]
pre-protocol [WSS4JOutInterceptor, MAPCodec]
write [SoapOutInterceptor]
marshal [BareOutInterceptor]
post-protocol [WSS4JOutInterceptorInternal]
post-stream [PolicyVerificationOutInterceptor]
write-ending [SoapOutEndingInterceptor]
pre-protocol-ending [SAAJOutEndingInterceptor]
pre-stream-ending [StaxOutEndingInterceptor]
prepare-send-ending [MessageSenderEndingInterceptor]
My interceptor (SoapMessageInterceptor) is placed before WSS4JOutInterceptor...but the soap envelope already contains the signature.
How do I get to the headers before WSS4J does?

As you can see above, the WSS4JOutInterceptor is in the "PRE_PROTOCOL" phase. If your interceptor is in "PRE_STREAM" then it should run before the WSS4JOutInterceptor. I don't see how the SoapMessageInterceptor in PRE_STREAM would already see the Signature. Perhaps the message already contains a Signature?

I will go ahead and guess that your interceptor will override the default XMLStreamWriter and change the content directly.
This however will not work because WSS4JOutInterceptor are signing the content from SAAJ Tree Object instead of the final XML byte. Hence you cannot change your header before the signature process.

Related

Getting "Signature is invalid." when using Artifact Binding during the artifact consumption step

I have an IdP and an SP setup using the ITfoxtec SAML2 libraries, and everything works great when not using artifact binding, or when not validating signatures. When using artifact binding and validating signatures I'm getting a "Signature is invalid." exception in the ACS when trying to retrieve and bind the actual response/assertion.
It seems to unbind the artifact response fine, then when it goes to retrieve and unbind the artifact from the ArtifactResolutionService it fails, specifically on the last line of this block:
var soapEnvelope = new Saml2SoapEnvelope();
saml2AuthnResponse = new Saml2AuthnResponse(config);
await soapEnvelope.ResolveAsync(httpClient, saml2ArtifactResolve, saml2AuthnResponse);
I've checked that my signature validation certificate is correct and I've dug through the source code but am scratching my head. I've tried to validate the "saml2p:ArtifactResponse" myself but there isn't much out there.
If I put this line before the chunk above everything works as expected as it no longer validates the signature:
config.SignatureValidationCertificates.Clear();
One thing I noticed is that in the 'saml2p:ArtifactResponse' there is a signature inside of that node but not inside the contained 'saml2p:Response' node. Is it possible that the saml2p:Response is being isolated and then a signature check is being performed? I tried to see if it was supposed to be signing the response/assertion in the artifact cache on the IdP side (artifactSaml2AuthnResponseCache), but it doesn't sign response at all. I'm doing this before putting it in the cache just like in the example and just like I do when using POST binding:
var token = saml2AuthnResponse.CreateSecurityToken(relyingParty.Issuer, subjectConfirmationLifetime: 5, issuedTokenLifetime: 60);
artifactSaml2AuthnResponseCache[saml2ArtifactResolve.Artifact] = saml2AuthnResponse;`
EDIT: I have determined that the ArtifactResponse just isn't signed properly. Another tool claims the digest in the XML doesn't match the computed value. This is after stepping through the source and grabbing the XML that the code is trying to validate directly. I can see that the ArtifactResolve is being signed and validated properly (and I checked with the external tool) but the ArtifactResponse isn't. Even in the code it fails at the final validation of the signature (and not at any checks before it).
EDIT 2: Found the problem in the source. The .ToXmlDocument() extension is breaking the signed XML. The final test was done by 'replacing' it in the spot with a new method that just returns the string directly with "envelope.ToString(SaveOptions.DisableFormatting)":
protected virtual XmlDocument ToSoapXml()
{
var envelope = new XElement(Saml2Constants.SoapEnvironmentNamespaceX + Saml2Constants.Message.Envelope);
envelope.Add(GetXContent());
return envelope.ToXmlDocument();
}
protected string ToSoapXmlString()
{
var envelope = new XElement(Saml2Constants.SoapEnvironmentNamespaceX + Saml2Constants.Message.Envelope);
envelope.Add(GetXContent());
return envelope.ToString(SaveOptions.DisableFormatting);//.ToXmlDocument();
}
And directly save that to the SoapResponseXml of the Saml2SoapEnvelope:
protected override Saml2SoapEnvelope BindInternal(Saml2Request saml2Request, string messageName)
{
if (!(saml2Request is Saml2ArtifactResponse))
throw new ArgumentException("Only Saml2ArtifactResponse is supported");
BindInternal(saml2Request);
SoapResponseXml = ToSoapXmlString();// ToSoapXml().OuterXml;
return this;
}
I would initiate a pull request for this change but honestly I'm not that up to speed with Git. I'm also not sure if this is the best way to fix the issue.
Thank you for your question and code to solve the problem. I'll look into the problem.
EDIT: I'm trying to reproduce the error but no luck. The sample is both an IdP an RP, what have you changed to get the error?

Dart Language: encoding (related to HttpRequest and http_server package)

I'm creating a file and writing a String on it with encoding set to LATIN1. However, the finished file is set with a different encoding (us-ascii or utf-8 returned by "file -bi" on Linux, depending on the method I use to get the String).
Here follows the creation method:
new File("/home/username/dart_test/file.xml").create(recursive: true).then((file) {
file.writeAsString(_methodReturnsAString(), mode: FileMode.WRITE, encoding: LATIN1);
});
Any ideas on what could be wrong?
EDIT (RELATED TO ANSWER):
There's no problem on the method described above. The problem was the data that was being provided to the method inside "writeAsString". That data comes from an HttpRequest that was not being processed properly (in fact, the setting of the encoding to ISO-8859-1 was causing the problem).
There's no problem with the method described on the question. Actually, the problem lays in an http request body handler that was not set properly.
So I'm answering my own question in order to help others with the same problem.
Here follows my request handler (from http_server package):
HttpBodyHandler.processRequest(request/*, defaultEncoding: Encoding.getByName("ISO-8859-1")*/).then((body) {
// Do something with body.
}, onError: _printError);
Take a look at the commented out "defaultEncoding". That was the cause. I don't think you can set it if you are not processing any files (blobs) on the request. I don't know if there's any situation where you should set it when just processing some String (I would appreciate if someone could complete this answer with this information).

Nancy transforms a (404) JsonResponse to Html one

I have a Nancy Fx application that acts as a pure API endpoint (application/json only, no text/html or browser etc access intended) and a module that returns e.g. the following:
return
userExists
? Negotiate.WithStatusCode(HttpStatusCode.OK)
: Negotiate.WithStatusCode(HttpStatusCode.NotFound);
However, I noticed one particularity - client's that have an Accept-Header set to 'application/json' and perform a GET request here, do get a text/html response back, even worse - in the .NotFound case a Nancy-specific/own 404 error is returned, in case of .OK an exception occurs due to missing Views.
What makes it even stranger for me is that inside my custom IStatusCodeHandler I "see" that the context.Response is a JsonResponse, somewhere down the pipeline this gets handled and (attempted to be) transformed further to text/html somehow though, and I wonder why.
Is there any way I can prevent the transformation to text/html?
This is because Nancy has a DefaultStatusCodeHandler that handles 500 and 404 responses. It's the last thing that runs in the Nancy pipeline before the host takes over the response.
What you're seeing is because the handler gets a 404 response (albeit a JsonResponse), and it can't know whether it's a hard (a route simply didn't exist) or a soft (a route existed but returned 404) status code, so it transforms it to the default 404 page. You might argue that it should check the accept header before doing so, but right now it isn't.
If you don't want this behavior, you can remove the default status code handler by overriding the InternalConfiguration property in your bootstrapper:
protected override NancyInternalConfiguration InternalConfiguration
{
get
{
return NancyInternalConfiguration
.WithOverrides(config => config.StatusCodeHandlers.Clear());
}
}

CXF wsdl2java, GZip compression, and stub reutilization

I´m using CXF to consume a WebService and, as the responses are quite large, I´m requesting with a gzip "Accept-Encoding" and using GZIPInInterceptor to handle the gziped response. Also my WSDL is very large (360kb) and it takes a long time(+10 seconds) to create the stub, because it has to read and parse the WSDL, so I´m creating the stub once and reusing it.
The problem is, whenever I try to use two different methods the second request gives me an error saying it is expecting the previous request.
To illustrate my problem I created a simple example with this public WebService:
http://www.webservicex.net/BibleWebservice.asmx?WSDL
Without the GZip compression it works fine:
BibleWebserviceSoap bibleService = new BibleWebservice().getBibleWebserviceSoap();
String title = bibleService.getBookTitles();
response.getWriter().write(title);
String johnResponse = bibleService.getBibleWordsbyKeyWord("John");
response.getWriter().write(johnResponse);
I´m able to receive both responses.
Enabling Gzip compression:
BibleWebserviceSoap bibleService = new BibleWebservice().getBibleWebserviceSoap();
//GZIP compression on bibleService
Client client = ClientProxy.getClient(bibleService);
client.getInInterceptors().add(new GZIPInInterceptor());
client.getInFaultInterceptors().add(new GZIPInInterceptor());
// Creating HTTP headers
Map<String, List<String>> headers = new HashMap<String, List<String>>();
headers.put("Accept-Encoding", Arrays.asList("gzip"));
// Add HTTP headers to the web service request
client.getRequestContext().put(Message.PROTOCOL_HEADERS, headers);
String title = bibleService.getBookTitles();
response.getWriter().write(title);
String johnResponse = bibleService.getBibleWordsbyKeyWord("John");
response.getWriter().write(johnResponse);
When I try to receive the second response I´m getting this exception:
org.apache.cxf.interceptor.Fault: Unexpected wrapper element {http://www.webserviceX.NET}GetBookTitlesResponse found. Expected {http://www.webserviceX.NET}GetBibleWordsbyKeyWordResponse.
On my real application I´m getting an exception with the request:
org.apache.cxf.binding.soap.SoapFault: OperationFormatter encountered an invalid Message body. Expected to find node type 'Element' with name 'GetAvailabilityRequest' and namespace 'http://schemas.navitaire.com/WebServices/ServiceContracts/BookingService'. Found node type 'Element' with name 'ns4:PriceItineraryRequest' and namespace 'http://schemas.navitaire.com/WebServices/ServiceContracts/BookingService'
My sample project can be downloaded here:
http://www.sendspace.com/file/plt0m4
Thank you
Instead of setting the protocol headers directly like that, use CXF's GZIPOutInterceptor to handle that.
Either that or reset the PROTOCOL headers for each request. When set like that, the headers map gets updated as the request goes through the chain. In this case, the soapaction gets set. This then gets resent on the second request.

How to know if a Kohana request is an internal one?

I'm writing an API using Kohana. Each external request must be signed by the client to be accepted.
However, I also sometime need to do internal requests by building a Request object and calling execute(). In these cases, the signature is unnecessary since I know the request is safe. So I need to know that the request was internal so that I can skip the signature check.
So is there any way to find out if the request was manually created using a Request object?
Can you use the is_initial() method of the request object? Using this method, you can determine if a request is a sub request.
Kohana 3.2 API, Request - is_initial()
It sounds like you could easily solve this issue by setting some sort of static variable your app can check. If it's not FALSE, then you know it's internal.
This is how I ended up doing it: I've overridden the Request object and added a is_server_side property to it. Now, when I create the request, I just set this to true so that I know it's been created server-side:
$request = Request::factory($url);
$request->is_server_side(true);
$response = $request->execute();
Then later in the controller receiving the request:
if ($this->request->is_server_side()) {
// Skip signature check
} else {
// Do signature check
}
And here is the overridden request class in application/classes/request.php:
<?php defined('SYSPATH') or die('No direct script access.');
class Request extends Kohana_Request {
protected $is_server_side_ = false;
public function is_server_side($v = null) {
if ($v === null) return $this->is_server_side_;
$this->is_server_side_ = $v;
}
}
Looking through Request it looks like your new request would be considered an internal request but does not have any special flags it sets to tell you this. Look at 782 to 832 in Kohana_Request...nothing to help you.
With that, I'd suggest extending the Kohana_Request_Internal to add a flag that shows it as internal and pulling that in your app when you need to check if it is internal/all others.
Maybe you are looking for is_external method:
http://kohanaframework.org/3.2/guide/api/Request#is_external
Kohana 3.3 in the controller :
$this->request->is_initial()
http://kohanaframework.org/3.3/guide-api/Request#is_initial

Resources