Error/Exception while marshalling signed and encrypted response OpenSAML v3 - opensaml

I have been using OpenSaml V2 for a while, which has been working well, and I recently started the migration. Due to the lack of information, even from the OpenSaml V3 book I have recently purchase, I am having some issues with a few things that were working fine with V2.
I have been using the following method to encrypt the Assertion. This method seems to be working fine.
private EncryptedAssertion createEncryptedAssertion(Assertion assertion) throws SamlException {
try {
Credential keyEncryptionCredential = CredentialSupport.getSimpleCredential(this.encryptingCertificate, this.encryptingPrivateKey);
DataEncryptionParameters encryptionParameters = new DataEncryptionParameters();
encryptionParameters.setAlgorithm(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128);
KeyEncryptionParameters keyEncryptionParameters = new KeyEncryptionParameters();
keyEncryptionParameters.setEncryptionCredential(keyEncryptionCredential);
keyEncryptionParameters.setAlgorithm(EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP);
Encrypter samlEncrypter = new Encrypter(encryptionParameters, keyEncryptionParameters);
samlEncrypter.setKeyPlacement(Encrypter.KeyPlacement.INLINE);
return samlEncrypter.encrypt(assertion);
}
catch(Exception e) {
throw new SamlException(e);
}
}
The problem I am having happens when I try to marshall my response with the encrypted assertion, using the following method:
public String marshall(XMLObject xmlObject, boolean encode) throws SamlException {
try {
ParserPool parserPool = XMLObjectProviderRegistrySupport.getParserPool();
MarshallerFactory marshallerFactory = XMLObjectProviderRegistrySupport.getMarshallerFactory();
Marshaller marshaller = marshallerFactory.getMarshaller(xmlObject);
if(marshaller == null) {
throw new SamlException("Unable to locate marshaller for " + xmlObject.getElementQName()
+ " can not perform marshalling operation");
}
Element element = marshallerFactory.getMarshaller(xmlObject).marshall(xmlObject, parserPool.newDocument());
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3");
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(element);
transformer.transform(source, result);
String xmlString = result.getWriter().toString();
if(encode) {
//return Util.base64EncodeMessage(xmlString);
}
return xmlString;
}
catch(Exception e) {
throw new SamlException(e);
}
}
When I try to marshall my response with encrypted assertion, I get the following exception:
Caused by: org.opensaml.core.xml.io.MarshallingException: Unable to root namespaces of cached DOM element, {http://www.w3.org/2001/04/xmlenc#}EncryptionMethod
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.prepareForAdoption(AbstractXMLObjectMarshaller.java:427)
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.marshall(AbstractXMLObjectMarshaller.java:144)
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.marshallChildElements(AbstractXMLObjectMarshaller.java:271)
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.marshallInto(AbstractXMLObjectMarshaller.java:212)
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.marshall(AbstractXMLObjectMarshaller.java:162)
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.marshallChildElements(AbstractXMLObjectMarshaller.java:271)
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.marshallInto(AbstractXMLObjectMarshaller.java:212)
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.marshall(AbstractXMLObjectMarshaller.java:162)
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.marshallChildElements(AbstractXMLObjectMarshaller.java:271)
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.marshallInto(AbstractXMLObjectMarshaller.java:212)
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.marshall(AbstractXMLObjectMarshaller.java:162)
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.marshallChildElements(AbstractXMLObjectMarshaller.java:271)
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.marshallInto(AbstractXMLObjectMarshaller.java:212)
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.marshall(AbstractXMLObjectMarshaller.java:162)
at org.opensaml.saml.common.AbstractSAMLObjectMarshaller.marshall(AbstractSAMLObjectMarshaller.java:65)
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.marshallChildElements(AbstractXMLObjectMarshaller.java:271)
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.marshallInto(AbstractXMLObjectMarshaller.java:212)
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.marshall(AbstractXMLObjectMarshaller.java:118)
at org.opensaml.saml.common.AbstractSAMLObjectMarshaller.marshall(AbstractSAMLObjectMarshaller.java:57)
at com.divinvest.sso.opensaml.SamlAssertionProducerV2.marshall(SamlAssertionProducerV2.java:171)
... 25 more
Caused by: org.w3c.dom.DOMException: Unable to resolve namespace prefix ds found on element {http://www.w3.org/2000/09/xmldsig#}DigestMethod
at net.shibboleth.utilities.java.support.xml.NamespaceSupport.rootNamespaces(NamespaceSupport.java:247)
at net.shibboleth.utilities.java.support.xml.NamespaceSupport.rootNamespaces(NamespaceSupport.java:295)
at net.shibboleth.utilities.java.support.xml.NamespaceSupport.rootNamespaces(NamespaceSupport.java:200)
at org.opensaml.core.xml.io.AbstractXMLObjectMarshaller.prepareForAdoption(AbstractXMLObjectMarshaller.java:422)
... 44 more
Am I missing anything in the unmarshall method? I am marshall my response objects with signed assertion, signed response but when the assertion is encrypted, I am not able to.
Thank you

The issue was that xmltooling was bringing the wrong xmlsec version (1.5.7), I had to include an exclusing in order to use xmlsec 2.0.5 that is using by opensaml-security-api.

Related

MSAL4J with Extensions error: com.microsoft.aad.msal4jextensions.CrossProcessCacheFileLock - null

I try to authenticate to Azure AD throught MSAL4J Java library (https://github.com/AzureAD/microsoft-authentication-library-for-java) with extention (https://github.com/AzureAD/microsoft-authentication-extensions-for-java) for cross platform cache serialization.
I am using the "Desktop app that calls web APIs: Acquire a token interactively" flow (https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-desktop-acquire-token-interactive?tabs=dotnet)
Here is my code:
private static IAuthenticationResult acquireTokenInteractive(final boolean onlyFromCache) throws Exception {
scopeSet.add(API_SCOPE);
final PersistenceTokenCacheAccessAspect perSettings = new PersistenceTokenCacheAccessAspect(createPersistenceSettings());
pca = PublicClientApplication.builder(CLIENT_ID).authority(AUTHORITY).setTokenCacheAccessAspect(perSettings).build();
final Set<IAccount> accountsInCache = pca.getAccounts().join();
IAuthenticationResult result;
try {
// Take first account in the cache. In a production application, you would filter
// accountsInCache to get the right account for the user authenticating.
final IAccount account = accountsInCache.iterator().next();
final SilentParameters silentParameters = SilentParameters.builder(scopeSet, account).build();
// try to acquire token silently. This call will fail since the token cache
// does not have any data for the user you are trying to acquire a token for
result = pca.acquireTokenSilently(silentParameters).join();
} catch (final Exception ex) {
if ((ex.getCause() instanceof MsalException) || (ex instanceof NoSuchElementException)) {
if (!onlyFromCache) {
final InteractiveRequestParameters parameters = InteractiveRequestParameters.builder(new URI("http://localhost"))
.scopes(scopeSet).build();
// Try to acquire a token interactively with system browser. If successful, you should see
// the token and account information printed out to console
result = pca.acquireToken(parameters).join();
} else {
return null;
}
} else {
// Handle other exceptions accordingly
throw ex;
}
}
return result;
}
I get an error while accessing cache:
[ForkJoinPool.commonPool-worker-19] ERROR com.microsoft.aad.msal4jextensions.CrossProcessCacheFileLock - null
and when I try to acquire token, an exception is thrown after a valid login in the browser (Firefox) :
java.util.concurrent.CompletionException:
java.lang.AbstractMethodError: Receiver class
com.sun.jna.platform.win32.WinCrypt$DATA_BLOB does not define or
inherit an implementation of the resolved method 'abstract
java.util.List getFieldOrder()' of abstract class
com.sun.jna.Structure. at
java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:314)
at
java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:319)
at
java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1702)
at
java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1692)
at
java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at
java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at
java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at
java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at
java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
Caused by: java.lang.AbstractMethodError: Receiver class
com.sun.jna.platform.win32.WinCrypt$DATA_BLOB does not define or
inherit an implementation of the resolved method 'abstract
java.util.List getFieldOrder()' of abstract class
com.sun.jna.Structure. at
com.sun.jna.Structure.fieldOrder(Structure.java:952) at
com.sun.jna.Structure.getFields(Structure.java:1006) at
com.sun.jna.Structure.deriveLayout(Structure.java:1172) at
com.sun.jna.Structure.calculateSize(Structure.java:1097) at
com.sun.jna.Structure.calculateSize(Structure.java:1049) at
com.sun.jna.Structure.allocateMemory(Structure.java:403) at
com.sun.jna.Structure.(Structure.java:194) at
com.sun.jna.Structure.(Structure.java:182) at
com.sun.jna.Structure.(Structure.java:169) at
com.sun.jna.Structure.(Structure.java:161) at
com.sun.jna.platform.win32.WinCrypt$DATA_BLOB.(WinCrypt.java:74)
at
com.sun.jna.platform.win32.Crypt32Util.cryptProtectData(Crypt32Util.java:80)
at
com.sun.jna.platform.win32.Crypt32Util.cryptProtectData(Crypt32Util.java:60)
at
com.sun.jna.platform.win32.Crypt32Util.cryptProtectData(Crypt32Util.java:47)
at
com.microsoft.aad.msal4jextensions.persistence.CacheFileAccessor.write(CacheFileAccessor.java:56)
at
com.microsoft.aad.msal4jextensions.PersistenceTokenCacheAccessAspect.afterCacheAccess(PersistenceTokenCacheAccessAspect.java:144)
at
com.microsoft.aad.msal4j.TokenCache$CacheAspect.close(TokenCache.java:171)
at com.microsoft.aad.msal4j.TokenCache.saveTokens(TokenCache.java:218)
at
com.microsoft.aad.msal4j.AbstractClientApplicationBase.acquireTokenCommon(AbstractClientApplicationBase.java:131)
at
com.microsoft.aad.msal4j.AcquireTokenByAuthorizationGrantSupplier.execute(AcquireTokenByAuthorizationGrantSupplier.java:63)
at
com.microsoft.aad.msal4j.AcquireTokenByInteractiveFlowSupplier.acquireTokenWithAuthorizationCode(AcquireTokenByInteractiveFlowSupplier.java:193)
at
com.microsoft.aad.msal4j.AcquireTokenByInteractiveFlowSupplier.execute(AcquireTokenByInteractiveFlowSupplier.java:39)
at
com.microsoft.aad.msal4j.AuthenticationResultSupplier.get(AuthenticationResultSupplier.java:71)
at
com.microsoft.aad.msal4j.AuthenticationResultSupplier.get(AuthenticationResultSupplier.java:20)
at
java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
... 6 more
What am I doing wrong ?
Thanks by advance !

Is there a way to batch upload a collection of InputStreams to Amazon S3 using the Java SDK?

I am aware of the TransferManager and the .uploadFileList() and .uploadFileDirectory() methods, however they accept java.io.File types as arguments. I have a collection of byte array input streams containing jpeg image data. I don't want to create in-memory files to store this data before I upload it either.
So what I need is essentially what the S3 client's PutObjectRequest does but for a collection of InputStream objects. Also, if one upload fails, I want to abort the whole thing and not upload anything, much like how a database transaction will reverse the changes if something goes wrong along the way.
Is this possible with the Java SDK?
Before I share an answer, please consider upgrading...
fyi - TransferManager is deprecated, now supported as TransferManagerBuilder in JAVA AWS SDK, please consider upgrading if TransferManagerBuilder Object suits your needs.
now since you asked about TransferManager, you could either 1) copy the code below and replace the functionality/arguments with your custom in memory handling of the input stream and handle it in your custom function... or; 2) further below is another sample, try to use this as-is...
Github source modify with with inputstream and issue listed here
private def uploadFile(is: InputStream, s3ObjectName: String, metadata: ObjectMetadata) = {
try {
val putObjectRequest = new PutObjectRequest(bucketName, s3ObjectName,
is, metadata)
// TransferManager supports asynchronous uploads and downloads
val upload = transferManager.upload(putObjectRequest)
upload.addProgressListener(ExceptionReporter.wrap(UploadProgressListener(putObjectRequest)))
} catch {
case e: Exception => throw new RuntimeException(e)
}
}
Bonus, Nice custom answer here using sequence input streams
public void combineFiles() {
List<String> files = getFiles();
long totalFileSize = files.stream()
.map(this::getContentLength)
.reduce(0L, (f, s) -> f + s);
try {
try (InputStream partialFile = new SequenceInputStream(getInputStreamEnumeration(files))) {
ObjectMetadata resultFileMetadata = new ObjectMetadata();
resultFileMetadata.setContentLength(totalFileSize);
s3Client.putObject("bucketName", "resultFilePath", partialFile, resultFileMetadata);
}
} catch (IOException e) {
LOG.error("An error occurred while combining files. {}", e);
}
}
private Enumeration<? extends InputStream> getInputStreamEnumeration(List<String> files) {
return new Enumeration<InputStream>() {
private Iterator<String> fileNamesIterator = files.iterator();
#Override
public boolean hasMoreElements() {
return fileNamesIterator.hasNext();
}
#Override
public InputStream nextElement() {
try {
return new FileInputStream(Paths.get(fileNamesIterator.next()).toFile());
} catch (FileNotFoundException e) {
System.err.println(e.getMessage());
throw new RuntimeException(e);
}
}
};
}

How to properly handle Exceptions in client thrown by a SOAP web service

I am setting up a Client using Spring-boot in Java to access a soap endpoint (for testing purpose).What's the best approach to handle Exceptions? I want to handle SOAPFaultClientExceptions...
I have already tried this:
How to Parse SoapFaultClientException in spring-ws
but it didn't work properly, as I couldn't call the getValue() method on detail
try {
JAXBElement res = (JAXBElement) getWebServiceTemplate().marshalSendAndReceive(url, request);
return (GetBankResponseType) res.getValue();
}catch (SoapFaultClientException ex) {
SoapFaultDetail soapFaultDetail = ex.getSoapFault().getFaultDetail(); // <soapFaultDetail> node
// if there is no fault soapFaultDetail ...
if (soapFaultDetail == null) {
throw ex;
}
SoapFaultDetailElement detailElementChild = soapFaultDetail.getDetailEntries().next();
Source detailSource = detailElementChild.getSource();
Object detail = getWebServiceTemplate().getUnmarshaller().unmarshal(detailSource);
JAXBElement source = (JAXBElement) detail;
System.out.println("Text::"+source.getValue());
}//catch other Exceptions...Which ones?
return null;
}
Expected result is a handled Exception, (SOAPFaultClientException) or others... which get Thrown by the webservice when wrong parameters are passed. I don't find any suitable solutions.
Configure ClientInterceptor or FaultMessageResolver to your WebServiceTemplate and do your error handling there.

Getting null or empty userid in Sinch

FATAL EXCEPTION: main
java.lang.IllegalArgumentException: toUserId must not be null or empty
at com.sinch.android.rtc.internal.client.calling.DefaultCallClient.call(DefaultCallClient.java:153)
at com.sinch.android.rtc.internal.client.calling.DefaultCallClient.callUser(DefaultCallClient.java:102)
at com.sinch.android.rtc.internal.client.calling.DefaultCallClient.callUser(DefaultCallClient.java:97)
at packagename.SinchService$SinchServiceInterface.callUser(SinchService.java:91)
at packagename.PlaceCallActivity.callButtonClicked(PlaceCallActivity.java:142)
at packagename.PlaceCallActivity.access$000(PlaceCallActivity.java:26)
at packagename.PlaceCallActivity$1.onClick(PlaceCallActivity.java:173)
at android.view.View.performClick(View.java:4802)
at android.view.View$PerformClick.run(View.java:20101)
at android.os.Handler.handleCallback(Handler.java:810)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:189)
at android.app.ActivityThread.main(ActivityThread.java:5532)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:950)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:745)
What could be the possible issues please elaborate tell. I am getting random mobile number from database using json parser request.
private void callButtonClicked() {
try {
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("status",status));
params.add(new BasicNameValuePair("mobile", callNo));
JSONObject json = jsonParser.makeHttpRequest(Config.URL_Random, "POST",
params);
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
response=json.getString(MOBILE);
}
}catch (JSONException e){
e.printStackTrace();
}
String userNo = response;
Log.d("Response is",userNo);
try {
Call call = getSinchServiceInterface().callUser(userNo);
if (call == null) {
// Service failed for some reason, show a Toast and abort
Toast.makeText(this, "Service is not started. Try stopping the service and starting it again before "
+ "placing a call.", Toast.LENGTH_LONG).show();
return;
}
String callId = call.getCallId();
Intent callScreen = new Intent(this, CallScreenActivity.class);
callScreen.putExtra(SinchService.CALL_ID, callId);
startActivity(callScreen);
} catch (MissingPermissionException e) {
ActivityCompat.requestPermissions(this, new String[]{e.getRequiredPermission()}, 0);
}
}
I am storing user mobile number at login screen and callno is the number in Textview of LoggedIn No Ok. This json response response=json.getString(MOBILE); will get any random number from a database. So tell me please where i am doing it wrong and also i guess my php api made by another guy who is storing the number giving random number assured me that everything is absolutely fine from there side.
Try this maybe because your callNo is out of scope of callbuttonclicked
Suppose if you have number in textview then you do this inside callbuttonclicked function:
callNo= userNo.getText().toString();
userNo = (TextView) findViewById(R.id.number); --> do this in onCreate method
hope that would resolve your problem.
seems to me you have empty ot null values in you database, we dont allow emty strong as userid to send to

Elmah doesn't log exceptions using WebAPI with HttpResponseException

In my WebApi code, I raise a HttpResponseException which short-circuits the request pipeline and generates a valid Http response. However, I'm trying to integrate webApi with elmah logging, yet the HttpResponseExeptions aren't showing up.
I have the web.config set-up for elmah and have the following code:
In Global.asx.cs:
static void ConfigureWebApi(HttpConfiguration config)
{
config.Filters.Add(new ServiceLayerExceptionFilter());
config.Filters.Add(new ElmahHandledErrorLoggerFilter());
config.DependencyResolver = new WebApiDependencyResolver(ObjectFactory.Container);
}
Filter:
public class ElmahHandledErrorLoggerFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
base.OnException(actionExecutedContext);
ErrorSignal.FromCurrentContext().Raise(actionExecutedContext.Exception);
}
}
Code where exception is raised:
public Task<FileUpModel> UploadFile()
{
if (Request.Content.IsMimeMultipartContent())
{
var provider = new TolMobileFormDataStreamProvider("C:\images\");
var task = Request.Content.ReadAsMultipartAsync(provider).ContinueWith(
t =>
{
if (t.IsFaulted || t.IsCanceled)
throw new HttpResponseException(HttpStatusCode.InternalServerError);
var fileInfo = provider.FileData.FirstOrDefault();
if (fileInfo == null)
// the exception here isn't logged by Elmah?!
throw new HttpResponseException(HttpStatusCode.InternalServerError);
var uploadModel = new FileUpModel { success = true };
return uploadModel;
});
return task;
}
else
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not properly formatted"));
}
}
Can anyone who has implemented this before let me know what I'm doing wrong?
As mentioned above, the Elmah filter does not catch and log anything when you raise a HttpResponseException. More specifically, if the following syntax is used:
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "It was a bad request");
or
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "HttpResponseException - This request is not properly formatted"));
I wanted to trap and log an error in both cases. The way to do it is to use an "ActionFilterAttribute", override "OnActionExecuted", and check actionExecutedContext.Response.IsSuccessStatusCode.
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
// when actionExecutedContext.Response is null, the error will be caught and logged by the Elmah filter
if ((actionExecutedContext.Response != null) && !actionExecutedContext.Response.IsSuccessStatusCode)
{
try
{
var messages = (System.Web.Http.HttpError)((System.Net.Http.ObjectContent<System.Web.Http.HttpError>)actionExecutedContext.Response.Content).Value;
StringBuilder stringBuilder = new StringBuilder();
foreach (var keyValuePair in messages) {
stringBuilder.AppendLine("Message: Key - " + keyValuePair.Key + ", Value - " + keyValuePair.Value);
}
Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("Web API Failed Status Code returned - " + stringBuilder.ToString()));
}
catch (Exception ex)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("Error in OnActionExecuted - " + ex.ToString()));
}
}
}
On a side note, I also overwrote "OnActionExecuting" to validate the model state. This allowed me to remove all of the checks within my actions.
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (actionContext.ModelState != null && !actionContext.ModelState.IsValid)
{
StringBuilder stringBuilder = new StringBuilder();
foreach (var obj in actionContext.ModelState.Values)
{
foreach (var error in obj.Errors)
{
if(!string.IsNullOrEmpty(error.ErrorMessage)) {
stringBuilder.AppendLine("Error: " + error.ErrorMessage);
}
}
}
Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("Invalid Model State -- " + stringBuilder.ToString()));
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
Of course, you will need to add the filter using "config.Filters.Add".
Web API special cases HttpResponseException thrown in action and converts into HttpResponseMessage and hence you are not seeing your exception filter getting invoked.
This is not true in the case of throwing HttpResponseException from filters. However, ideally one need not throw HttpResponseException from filters as you could short-circuit a request by setting the Response property on the supplied input context.
You need to turn on Elmah for HttpFilters in order to get this to work as you expect for WebApi.
Use Elmah.Contrib.WebApi available as a NuGet Package, it will wire include a class that you can then wire up following the instructions on the Elmah.Contrib.WebApi project site.
If you want to do this yourself, Capturing Unhandled Exceptions in ASP.NET Web API's with ELMAH walks you through what the Elmah.Contrib.WebApi is doing for you.
Additionally, I had to change the way that the error response is thrown for it to be picked by Elmah to:
throw new HttpException((int)HttpStatusCode.NotAcceptable, "This request is not properly formatted");
I would also recommend the use of the Elmah.MVC NuGet Package.

Resources