How can I establish a secure channel for SSL/TLS from a handheld device? - ssl

I am trying to call a REST method from a handheld device (Windows CE / Compact framework) with this code:
public static HttpWebRequest SendHTTPRequestNoCredentials(string uri, HttpMethods method, string data, string contentType)
{
ExceptionLoggingService.Instance.WriteLog("Reached
fileXferREST.SendHTTPRequestNoCredentials");
WebRequest request = null;
try
{
request = WebRequest.Create(uri);
request.Method = Enum.ToObject(typeof(HttpMethods), method).ToString();
request.ContentType = contentType;
((HttpWebRequest)request).Accept = contentType;
((HttpWebRequest)request).KeepAlive = false;
((HttpWebRequest)request).ProtocolVersion = HttpVersion.Version10;
if (method != HttpMethods.GET && method != HttpMethods.DELETE)
{
byte[] arrData = Encoding.UTF8.GetBytes(data);
request.ContentLength = arrData.Length;
using (Stream oS = request.GetRequestStream())
{
oS.Write(arrData, 0, arrData.Length);
}
}
else
{
request.ContentLength = 0;
}
}
catch (Exception ex)
{
String msgInnerExAndStackTrace = String.Format(
"{0}; Inner Ex: {1}; Stack Trace: {2}", ex.Message, ex.InnerException,
ex.StackTrace);
ExceptionLoggingService.Instance.WriteLog(String.Format("From
FileXferREST.SendHTTPRequestNoCredentials(): {0}", msgInnerExAndStackTrace));
}
return request as HttpWebRequest;
}
The vals being passed to the method are:
uri: "https://seastore.nrbq.ad/ggr.web/api/inventory/sendXML/duckbill/platypus/INV_3_20090313214959000.xml"
HttpMethods: HttpMethods.POST
data: [ some xml ]
contentType: "application/xml"
...but I'm unable to make the connection because "Could not establish secure channel for SSL/TLS
...System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host"
So what must I do to establish a secure channel for SSL/TLS, so that the existing connection is not so rudely closed by the emotionally remote host?
Nebenbei bemerkt: I find it a bit rompecabezish that when I was catching a WebException, this code was causing the app to crash, but when I changed the catch block to a generic exception, the attempt to connect silently failed (the only way I could tell there was a problem was by looking at the log file).
To be more specific, with the WebException code in HttpWebRequest SendHTTPRequestNoCredentials()'s catch block, like so:
catch (WebException webex)
{
HttpWebResponse hwr = (HttpWebResponse)webex.Response;
HttpStatusCode hsc = hwr.StatusCode;
String webExMsgAndStatusCode = String.Format("{0} Status code == {1}", webex.Message,
hsc.ToString());
ExceptionLoggingService.Instance.WriteLog(String.Format("From
FileXferREST.SendHTTPRequestNoCredentials: {0}", webExMsgAndStatusCode));
}
...the app crashed and the log file held these post mortem notes (the dreaded NRE!):
Date: 3/13/2009 11:40:15 PM
Message: Reached FileXferREST.SendHTTPRequestNoCredentials
Date: 3/13/2009 11:40:31 PM
Message: From frmMain.SendInventories: NullReferenceException; Inner Ex: ; Stack Trace: at
HHS.FileXferREST.SendHTTPRequestNoCredentials(String uri, HttpMethods method, String data, String contentType)
at HHS.FileXferREST.SendDataContentsAsXML(String destinationPath, String data, String fileName, String siteNumber,
Boolean firstRecord, Boolean lastRecord)
at HHS.frmMain.SendInventories()
at HHS.frmMain.menuItemSEND_Inventories_Click(Object sender, EventArgs e)
. . .
However, with the generic Exception code in the catch block (as shown at the top of this post), the app seemed to be strolling through the park on a sunny summer Sunday morn -- no exception message or crash or sign of any wintery discontent whatsoever -- but the log file reveals this:
Date: 3/13/2009 11:54:52 PM
Message: Reached FileXferREST.SendHTTPRequestNoCredentials
Date: 3/13/2009 11:54:54 PM
Message: From FileXferREST.SendHTTPRequestNoCredentials(): Could not establish secure channel for SSL/TLS; Inner Ex:
System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
at System.Net.Sockets.Socket.ReceiveNoCheck(Byte[] buffer, Int32 index, Int32 request, SocketFlags socketFlags)
at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
at System.Net.Connection.System.Net.ISslDataTransport.Receive(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.SslConnectionState.ClientSideHandshake()
at System.Net.SslConnectionState.PerformClientHandShake()
at System.Net.Connection.connect(Object ignored)
at System.Threading.ThreadPool.WorkItem.doWork(Object o)
at System.Threading.Timer.ring()
; Stack Trace: at System.Net.HttpWebRequest.finishGetRequestStream()
at System.Net.HttpWebRequest.GetRequestStream()
at HHS.FileXferREST.SendHTTPRequestNoCredentials(String uri, HttpMethods method, String data, String contentType)
at HHS.FileXferREST.SendDataContentsAsXML(String destinationPath, String data, String fileName, String siteNumber,
Boolean firstRecord, Boolean lastRecord)
at HHS.frmMain.SendInventories()
at HHS.frmMain.menuItemSEND_Inventories_Click(Object sender, EventArgs e)
. . .
Notwithstanding that last interesting tidbit, what really matters is: How can I establish a secure channel for SSL/TLS from a handheld device?
UPDATE
I called the code from a "sandbox" app running on my PC and get a similar, albeit not identical, exception. This is what it caught:
Message: From SendHTTPRequestNoCredentials(): The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.; Inner Ex: System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, Exception exception)
at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
. . .
UPDATE 2
Based on some of the comments here, and the links that accompany them, I was thinking I needed to add this within my code:
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
...in some context:
public static HttpWebRequest SendHTTPRequestNoCredentials(string uri, HttpMethods method, string data, string
contentType)
{
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;
WebRequest request = null;
try
{
request = WebRequest.Create(uri);
...but, although this is a .NET 3.5 client app, and, according to this [http://msdn.microsoft.com/en-us/library/system.net.servicepointmanager.servercertificatevalidationcallback
(v=vs.90).aspx], ServerCertificateValidationCallback is supposedly available in 3.5, "ServerCertificateValidationCallback" is not available to me (I get "cannot resolve symbol"). It seems this is in the System.Net assembly, but attempts to add a reference to System.Net to my project are futile, as there is no such assembly available via Add References on the .NET tab. The alphabetically-ordered list goes from "System.Messaging" to "System.Net.Irda"
I reckon this lack is because this is a feature-poor Compact Framework project.
Assuming this is so (Compact Framework does not contain ServerCertificateValidationCallback), what is the workaround for this scenario? How can I have my client handheld app accept the self-signed ssl certificate on the server (REST app running on a local network)?
UPDATE 3
Should I check/tick either or both of the following in Control Panel > Programs > Turn Windows features on or off > Internet Information Services > World Wide Web Service > Security:
Client Certificate Mapping Authentication
IIS Client Certificate Mapping Authentication
?
UPDATE 4
I can access ServicePoint, like so:
ServicePoint svcPoint = ServicePointManager.FindServicePoint(uri);
...but does this do me any good. Can I set the Certificate to something that will be the equivalent of always accepting it. IOW, what do I need here:
ServicePoint svcPoint = ServicePointManager.FindServicePoint(uri);
svcPoint.Certificate = ???
UPDATE 5
Even with this code:
namespace HHS
{
using System.Net;
using System.Security.Cryptography.X509Certificates;
class TrustAllCertificatesPolicy : ICertificatePolicy
{
public TrustAllCertificatesPolicy()
{
}
public bool CheckValidationResult(ServicePoint sp, X509Certificate cert, WebRequest req, int problem)
{
return true;
}
}
}
private void frmMain_Load(object sender, EventArgs e)
{
System.Net.ServicePointManager.CertificatePolicy = new TrustAllCertificatesPolicy();
}
...I still get this:
Message: Reached FileXferREST.SendHTTPRequestNoCredentials
Date: 3/18/2009 11:41:09 PM
Message: From FileXferREST.SendHTTPRequestNoCredentials(): Could not establish secure channel for SSL/TLS; Inner Ex: System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
at System.Net.Sockets.Socket.ReceiveNoCheck(Byte[] buffer, Int32 index, Int32 request, SocketFlags socketFlags)
at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
at System.Net.Connection.System.Net.ISslDataTransport.Receive(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.SslConnectionState.ClientSideHandshake()
at System.Net.SslConnectionState.PerformClientHandShake()
at System.Net.Connection.connect(Object ignored)
at System.Threading.ThreadPool.WorkItem.doWork(Object o)
at System.Threading.Timer.ring()
; Stack Trace: at System.Net.HttpWebRequest.finishGetRequestStream()
at System.Net.HttpWebRequest.GetRequestStream()
at HHS.FileXferREST.SendHTTPRequestNoCredentials(String uri, HttpMethods method, String data, String contentType)
. . .
BTW, TrustAllCertificatesPolicy's (empty) constructor is probably moot, as it is grayed out.

The .NET Compact Framework does not have ServerCertificateValidationCallback.
What you could do is to set a CertificatePolicy class to validate the certificate.
public class TrustAllCertificatePolicy : ICertificatePolicy
{
public TrustAllCertificatePolicy()
{
}
public bool CheckValidationResult(ServicePoint sp, X509Certificate cert, WebRequest req, int problem)
{
return true;
}
}
...
System.Net.ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy();
See this link for more information.

Look at my answer Here
In a nutshell certificate management and security is not implemented well in CE and you will need to create your own web request object from Microsofts object. More details can be found at this link http://labs.rebex.net/HTTPS

Related

Sending a POST request using httpClient in a custom workflow activity in Dynamics CRM throws an exception "ThreadAbortException"

I have a task to retrieve data created in an entity called "Denunciation" then do some structring and send this data (in JSON string format) to an API, so I need to make a POST request, everything works fine but when I want to send the post request, it throws this Excpetion
The Exception Details:
System.Threading.ThreadAbortException
HResult=0x80131530
Message=The thread has been abandoned.
Source=System
StackTrace:
at System.Net.CookieModule.OnSendingHeaders(HttpWebRequest httpWebRequest)
at System.Net.HttpWebRequest.UpdateHeaders()
at System.Net.HttpWebRequest.SubmitRequest(ServicePoint servicePoint)
at System.Net.HttpWebRequest.BeginGetRequestStream(AsyncCallback callback, Object state)
at System.Net.Http.HttpClientHandler.StartGettingRequestStream(RequestState state)
at System.Net.Http.HttpClientHandler.PrepareAndStartContentUpload(RequestState state)
--- End of stack trace from previous location ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at HclsDeclareProspect.DeclareProspect.<Execute>d__28.MoveNext() in C:\Users\aimad.quouninich\Desktop\Projects\VDN\HclsDeclareProspect\DeclareProspect.cs:line 177
This exception was originally thrown at this call stack:
[External Code]
HclsDeclareProspect.DeclareProspect.Execute(System.Activities.CodeActivityContext) in DeclareProspect.cs
My Code in the Execute Method:
// Send POST request APIM
CanalItem endpoint = CanalProvider.Retrieve(service, endpointReference);
_trace += string.Format("Endpoint : {0} \r\n", endpoint.Name);
_trace += string.Format("Endpoint Url : {0} \r\n", endpoint.Url);
_trace += string.Format("Endpoint Login : {0} \r\n", endpoint.Login);
_trace += string.Format("Endpoint Login : {0} \r\n", endpoint.Password);
string url = $"{endpoint.Url}/denunciation";
var content = new StringContent(requestJSON.ToString(), Encoding.UTF8, "application/json");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Apim-Subscription-Key", endpoint.Password);
try
{
var result = await client.PostAsync(url, content);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
If I removed the try-catch block the custom workflow and pluginRegistrationTool will exit without any error message and the subsequent line codes will not execute.
I have tried many things like using httpWebRequest instead of httpClient but when I tried to write to stream it exist with this error
System.Security.SecurityException
HResult=0x8013150A
Message=Type Authorization Request Failed 'System.Net.WebPermission, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.
Source=mscorlib
StackTrace:
at System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet)
at System.Security.CodeAccessPermission.Demand()
at System.Net.HttpWebRequest.get_Proxy()
at System.Net.WebRequest.GetRequestStreamAsync()
at HclsDeclareProspect.DeclareProspect.<Execute>d__29.MoveNext() in C:\Users\aimad.quouninich\Desktop\Projects\VDN\HclsDeclareProspect\DeclareProspect.cs:line 186
This exception was originally thrown at this call stack:
[External Code]
HclsDeclareProspect.DeclareProspect.Execute(System.Activities.CodeActivityContext) in DeclareProspect.cs
Using this code for the HttpWebrequest in place of httpClient:
string url = $"{endpoint.Url}/denunciation";
var content = new StringContent(requestJSON.ToString(), Encoding.UTF8, "application/json");
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var request = (HttpWebRequest)WebRequest.Create(new Uri(url));
request.ContentType = "application/json";
request.Method = "POST";
request.Timeout = 4000; //ms
var itemToSend = JsonConvert.SerializeObject(requestJSON);
using (var streamWriter = new StreamWriter(await request.GetRequestStreamAsync()))
{
streamWriter.Write(itemToSend);
streamWriter.Flush();
streamWriter.Dispose();
}
// Send the request to the server and wait for the response:
using (var response = await request.GetResponseAsync())
{
// Get a stream representation of the HTTP web response:
using (var stream = response.GetResponseStream())
{
var reader = new StreamReader(stream);
var message = reader.ReadToEnd();
}
}
I don't know what to do, please any help will be appreciated.
Solved,
The problem was in Newtonsoft.json package. it looks like the CRM doesn't accept extern packages/assemblies and the tool Ilmerge (to merge multiple assemblies) is deprecated and no longer supported by the CRM.
When running the debugger and the process was asynchronous I dind'nt notice the problem and was working fine, so I removed Newtonsoft.json and used the built-in solution wich is DataContractSerializer then using Trace log I was able to see that post works fine, but in debugging it is not working the program just exit, the solution to that is to put the breakpoint after the post request line of code to be able to see the results.
I hope this will help others with the same problem.

Access self signed X509certificates in XamarinForms for mqtt TLS connection to a Mosquitto broker

I desire to TLS secure with a self signed x509certificate a number of existing XamarinForms apps that connect to a mosquitto mqtt broker using the M2MqttDotnetCore client.
To that end I have created a simple sample XamarinForms pub/sub chat app to learn how to secure an XamarinForms mqtt client application that can be sound in this GitHub repository. jhalbrecht/XamarinFormsMqttSample
I have samples in Mosquitto_pub, python and a .net console app that accomplish this goal of successfully connecting to a mosquitto broker over port 8883 with TLS and a self signed certificate. The XamarinForms UWP app also works unsecured and secured. I'm having trouble getting the Android app to work with TLS on port 8883, The Android app does work unsecured on port 1883. This is the runtime log from Visual Studio 2017
[0:] M2Mqtt.Exceptions.MqttConnectionException: Exception connecting to the broker ---> System.AggregateException: One or more errors occurred. ---> System.Security.Authentication.AuthenticationException: A call to SSPI failed, see inner exception. ---> Mono.Btls.MonoBtlsException: Ssl error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED
at /Users/builder/jenkins/workspace/xamarin-android-d15-9/xamarin-android/external/mono/external/boringssl/ssl/handshake_client.c:1132
at Mono.Btls.MonoBtlsContext.ProcessHandshake () [0x00038] in <fb6d78e506844b3b96d5b35aa047fbbd>:0
at Mono.Net.Security.MobileAuthenticatedStream.ProcessHandshake (Mono.Net.Security.AsyncOperationStatus status) [0x0003e] in <fb6d78e506844b3b96d5b35aa047fbbd>:0
at (wrapper remoting-invoke-with-check) Mono.Net.Security.MobileAuthenticatedStream.ProcessHandshake(Mono.Net.Security.AsyncOperationStatus)
at Mono.Net.Security.AsyncHandshakeRequest.Run (Mono.Net.Security.AsyncOperationStatus status) [0x00006] in <fb6d78e506844b3b96d5b35aa047fbbd>:0
at Mono.Net.Security.AsyncProtocolRequest+<ProcessOperation>d__24.MoveNext () [0x000ff] in <fb6d78e506844b3b96d5b35aa047fbbd>:0
--- End of stack trace from previous location where exception was thrown ---
at Mono.Net.Security.AsyncProtocolRequest+<StartOperation>d__23.MoveNext () [0x0008b] in <fb6d78e506844b3b96d5b35aa047fbbd>:0
--- End of inner exception stack trace ---
at Mono.Net.Security.MobileAuthenticatedStream+<ProcessAuthentication>d__47.MoveNext () [0x00254] in <fb6d78e506844b3b96d5b35aa047fbbd>:0
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.ThrowIfExceptional (System.Boolean includeTaskCanceledExceptions) [0x00011] in <d4a23bbd2f544c30a48c44dd622ce09f>:0
at System.Threading.Tasks.Task.Wait (System.Int32 millisecondsTimeout, System.Threading.CancellationToken cancellationToken) [0x00043] in <d4a23bbd2f544c30a48c44dd622ce09f>:0
at System.Threading.Tasks.Task.Wait () [0x00000] in <d4a23bbd2f544c30a48c44dd622ce09f>:0
at M2Mqtt.Net.MqttNetworkChannel.Connect () [0x000a8] in <72fbe921f857483bafbb8b397ec98dd1>:0
at M2Mqtt.MqttClient.Connect (System.String clientId, System.String username, System.String password, System.Boolean willRetain, System.Byte willQosLevel, System.Boolean willFlag, System.String willTopic, System.String willMessage, System.Boolean cleanSession, System.UInt16 keepAlivePeriod) [0x0001e] in <72fbe921f857483bafbb8b397ec98dd1>:0
--- End of inner exception stack trace ---
at M2Mqtt.MqttClient.Connect (System.String clientId, System.String username, System.String password, System.Boolean willRetain, System.Byte willQosLevel, System.Boolean willFlag, System.String willTopic, System.String willMessage, System.Boolean cleanSession, System.UInt16 keepAlivePeriod) [0x00037] in <72fbe921f857483bafbb8b397ec98dd1>:0
at M2Mqtt.MqttClient.Connect (System.String clientId) [0x00000] in <72fbe921f857483bafbb8b397ec98dd1>:0
at MqttDataServices.Services.MqttDataService+<Initialize>d__5.MoveNext () [0x00266] in C:\jstuff\MqttSample\MqttDataServices\Services\MqttDataService.cs:183
The way I am currently loading and accessing the X509certificates is not secure or a best practice. It works. I hope to eventually learn how to access the device ca keystores for each mobile platform. I use the cross-platform plug-in FilePicker to load a cert, base64 encode it, and save it.
FileData fileData = await Plugin.FilePicker.CrossFilePicker.Current.PickFile();
if (fileData == null)
return; // user canceled file picking
string fileName = fileData.FileName;
string content = Convert.ToBase64String(fileData.DataArray, 0, fileData.DataArray.Length,
Base64FormattingOptions.None);
string deviceFileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), fileName);
File.WriteAllText(deviceFileName, content);
I have reached out to a few Xamarin folks via twitter. I have an open issue in my above mentioned repository discussing the problem where #baulig from Microsoft has I believe given me the answer however I don't currently know how to implement it.
I just looked at the certificate validation code and what it does is
essentially
var certStore = KeyStore.GetInstance ("AndroidCAStore");
certStore.Load(null);
This is the entry point:
https://github.com/mono/mono/blob/master/mcs/class/System/Mono.Btls/MonoBtlsX509LookupAndroid.cs,
it calls this code
https://github.com/mono/mono/blob/master/mcs/class/System/System/AndroidPlatform.cs#L101
which then calls into xamarin-android code here:
https://github.com/xamarin/xamarin-android/blob/master/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs
The KeyStore should be this class:
https://developer.xamarin.com/api/type/Java.Security.KeyStore/.
So you should be able to do this via Java.Security.KeyStore.
What permissions are necessary to grant in AndroidManifest.xml?
What terms might I research to properly access the platform ca keystores?
Additions after initial posting
February 27, 2019 (MST) 2:51 PM
Added certs and mqtt client creation from MqttDataService.cs
X509Certificate caCert = X509Certificate.CreateFromCertFile(Path.Combine(filesDirectoryBasePath, "ca.crt"));
string thePfxPathOnDevice = Path.Combine(filesDirectoryBasePath, "xamarinclient.pfx");
string theBase64EncodedPfx = File.ReadAllText(thePfxPathOnDevice);
byte[] certificate = Convert.FromBase64String(theBase64EncodedPfx);
X509Certificate2 clientCert = new X509Certificate2(certificate, "xamarin");
_client = new MqttClient(
GetHostName(_xpdSetting.MqttBrokerAddress),
Int32.Parse(_xpdSetting.MqttBrokerTlsPort),
_xpdSetting.UseTls,
caCert,
clientCert,
MqttSslProtocols.TLSv1_2
//MyRemoteCertificateValidationCallback
);
Since you are using .Net's/Mono Socket (via M2MqttDotnetCore), just use cert pinning and you only have to handle the RemoteCertificateValidationCallback. Thus no messing with Android's trusted stores, etc...
SslStream Usage on Android:
Note: There are issues with SslStream on Android, object allocations can go crazy... I believe(?) there is an open issue about this. (I had to use Java's SSLSocket a couple times to work around this issue)
Enable Native TLS 1.2+
Using BoringSSL via the Android project build options
Add your cert to the Android's Asset directory:
├── Assets
│   └── sushihangover.cert
This is your cert/.pem file (NOT your KEY!!)
Make sure that this is an ascii file with no unicode BOM header
Via openssl example (just change it to your host and secure port)
echo -n | openssl s_client -connect 10.1.10.250:5001 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'
RemoteCertificateValidationCallback Implementation
Note: The following code can be in used in NetStd2.0 or Xamarin.Android
X509Certificate sushihangoverCert; // Class level var
bool CertificateValidation(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors certificateErrors)
{
if (sushihangoverCert == null)
{
// There is no non-async version of OpenAppPackageFileAsync (via Xamarin.Essential) 😡 Why!!!
using (var waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset))
{
Task.Run(async () =>
{
using (var assetStream = await Xamarin.Essentials.FileSystem.OpenAppPackageFileAsync("sushihangover.cert"))
using (var memStream = new MemoryStream())
{
assetStream.CopyTo(memStream);
sushihangoverCert = new X509Certificate(memStream.ToArray());
waitHandle.Set();
}
});
waitHandle.WaitOne();
}
}
return sushihangoverCert.Equals(certificate) ? true : false;
}
SSLStream Usage Example:
Note: This is connecting to a NetCore Web API port using a self-signed cert
using (var tcpClient = new TcpClient("10.1.10.250", 5001))
using (var ssl = new SslStream(tcpClient.GetStream(), false, new RemoteCertificateValidationCallback(CertificateValidation)))
{
ssl.AuthenticateAsClient("10.1.10.250", null, System.Security.Authentication.SslProtocols.Tls12, false);
if (ssl.CanWrite)
{
var send = Encoding.ASCII.GetBytes("GET /api/item HTTP/1.1\r\nhost: 10.1.10.250\r\n\r\n");
await ssl.WriteAsync(send, 0, send.Length);
var buffer = new byte[4096];
var count = await ssl.ReadAsync(buffer, 0, buffer.Length);
Console.WriteLine(Encoding.UTF8.GetString(buffer, 0, count));
}
else
throw new SocketException();
}
Server cert mismatch error:
If your server cert (self-signed or not) does not match the one that you are pinning to, you will receive:
{Mono.Btls.MonoBtlsException: Ssl error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED

ServiceFabric: Service does not exist during deployment

I have an existing system using service fabric. Everything is fine except during a service publish the service is unavailable and any resolutions return an error.
This is expected however it would be nice if during this time instead the calls just waited or timedout. During this time my error logs will sometimes fill up with 200K lines of the same error.
I want some code like the following however where would it go?
public async Task Execute(Func<Task> action)
{
try
{
action()
.ConfigureAwait(false);
}
catch (FabricServiceNotFoundException ex)
{
await Task.Delay(TimeSpan.FromSeconds(??))
.ConfigureAwait(false);
action()
.ConfigureAwait(false);
}
}
Error:
System.Fabric.FabricServiceNotFoundException: Service does not exist. ---> System.Runtime.InteropServices.COMException: Exception from HRESULT: 0x80071BCD
at System.Fabric.Interop.NativeClient.IFabricServiceManagementClient6.EndResolveServicePartition(IFabricAsyncOperationContext context)
at System.Fabric.FabricClient.ServiceManagementClient.ResolveServicePartitionEndWrapper(IFabricAsyncOperationContext context)
at System.Fabric.Interop.AsyncCallOutAdapter2`1.Finish(IFabricAsyncOperationContext context, Boolean expectedCompletedSynchronously)
--- End of inner exception stack trace ---
at Microsoft.ServiceFabric.Services.Client.ServicePartitionResolver.ResolveHelperAsync(Func`5 resolveFunc, ResolvedServicePartition previousRsp, TimeSpan resolveTimeout, TimeSpan maxRetryInterval, CancellationToken cancellationToken, Uri serviceUri)
at Microsoft.ServiceFabric.Services.Communication.Client.CommunicationClientFactoryBase`1.CreateClientWithRetriesAsync(ResolvedServicePartition previousRsp, TargetReplicaSelector targetReplicaSelector, String listenerName, OperationRetrySettings retrySettings, Boolean doInitialResolve, CancellationToken cancellationToken)
at Microsoft.ServiceFabric.Services.Communication.Client.CommunicationClientFactoryBase`1.GetClientAsync(ResolvedServicePartition previousRsp, TargetReplicaSelector targetReplica, String listenerName, OperationRetrySettings retrySettings, CancellationToken cancellationToken)
at Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Client.FabricTransportServiceRemotingClientFactory.GetClientAsync(ResolvedServicePartition previousRsp, TargetReplicaSelector targetReplicaSelector, String listenerName, OperationRetrySettings retrySettings, CancellationToken cancellationToken)
at Microsoft.ServiceFabric.Services.Communication.Client.ServicePartitionClient`1.GetCommunicationClientAsync(CancellationToken cancellationToken)
at Microsoft.ServiceFabric.Services.Communication.Client.ServicePartitionClient`1.InvokeWithRetryAsync[TResult](Func`2 func, CancellationToken cancellationToken, Type[] doNotRetryExceptionTypes)
at Microsoft.ServiceFabric.Services.Remoting.V2.Client.ServiceRemotingPartitionClient.InvokeAsync(IServiceRemotingRequestMessage remotingRequestMessage, String methodName, CancellationToken cancellationToken)
at Microsoft.ServiceFabric.Services.Remoting.Builder.ProxyBase.InvokeAsyncV2(Int32 interfaceId, Int32 methodId, String methodName, IServiceRemotingRequestMessageBody requestMsgBodyValue, CancellationToken cancellationToken)
at Microsoft.ServiceFabric.Services.Remoting.Builder.ProxyBase.ContinueWithResultV2[TRetval](Int32 interfaceId, Int32 methodId, Task`1 task)
As expected, Service Fabric have to shutdown the service to start the new version, this will cause a transient error like the one you've got.
By default, the Remoting APIs already have a retry logic built-in, from the docs:
The service proxy handles all failover exceptions for the service
partition it is created for. It re-resolves the endpoints if there are
failover exceptions (non-transient exceptions) and retries the call
with the correct endpoint. The number of retries for failover
exceptions is indefinite. If transient exceptions occur, the proxy
retries the call.
With that said, you should not require to add extra retry logic, maybe you should try adjust the OperationRetrySettings for a better handling of these retries.
If does not solve the problem, and you still want to add the logic in your code, the simplest way to handle it is using a transient-fault-handling library like Polly, something like below:
var policy = Policy
.Handle<FabricServiceNotFoundException>()
.WaitAndRetry(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(3)
});
policy.Execute(() => DoSomething());
In this sample, you do an exponential backoff between retries, if the number of calls is too big, I would recomend implement the circuit breaker approach instead.

WCF - Returning HTTP 204 throws exception causing a body to be sent

I have implemented a RESTful service using WCF. One of the requirements is that the service resides on a different server (or at least port) from the website that will be consuming it.
To this end I have implemented custom message inspector - not really my work but I managed to put something together from a few different sources. One is here the other I can't find right now.
Basically this is so that I can say that the origin of the sender is allowed. Also, when the browser sends an OPTIONS request that it doesn't try to execute the method.
Everything works well except that when a 204 is returned fiddler was complaining that there is a body in the response. Upon examination I found there is an exception being thrown -
The server encountered an error processing the request. The exception message is 'The communication object, System.ServiceModel.InstanceContext, cannot be used for communication because it has been Aborted.'.
Stack trace:
at System.ServiceModel.Channels.CommunicationObject.ThrowIfClosedOrNotOpen()
at System.ServiceModel.InstanceContext.GetServiceInstance(Message message)
at System.ServiceModel.Dispatcher.InstanceBehavior.EnsureServiceInstance(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
The code is -
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
HttpRequestMessageProperty httpRequestHeader = request.Properties["httpRequest"] as HttpRequestMessageProperty;
if (httpRequestHeader.Method.ToUpper() == "OPTIONS" || httpRequestHeader.Headers[HttpRequestHeader.Authorization] == null)
{
instanceContext.Abort();
}
return httpRequestHeader;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
HttpRequestMessageProperty httpRequestHeader = correlationState as HttpRequestMessageProperty;
HttpResponseMessageProperty httpResponseHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
httpResponseHeader.Headers.Add("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,pulse-symphonycrm-userloginid,pulse-symphonycrm-domainandusername");
httpResponseHeader.Headers.Add("Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS");
httpResponseHeader.Headers.Add("Access-Control-Allow-Credentials", "true");
string origin = httpRequestHeader.Headers["origin"];
if (origin != null)
{
httpResponseHeader.Headers.Add("Access-Control-Allow-Origin", origin);
}
if (httpRequestHeader.Method.ToUpper() == "OPTIONS")
{
httpResponseHeader.StatusCode = HttpStatusCode.NoContent;
}
else if (httpRequestHeader.Headers[HttpRequestHeader.Authorization] == null)
{
httpResponseHeader.StatusDescription = "Unauthorized";
httpResponseHeader.StatusCode = HttpStatusCode.Unauthorized;
}
}
I think there must be something operating after the instanceContext.Abort() that is then causing the exception to be thrown and generating a body, but was unable to trace where this was coming from.
Can anyone shed some light on this and how to stop it while maintaining the headers being sent.
As I suspected and as Petar mentioned the instanceContext.Abort() was certainly at the root of the error as once I removed this the problem went away.
The method I went with in the end is to simply provide an OPTIONS equivalent method to be invoked that will return nothing when the browser sends this type of request -
// Note the void return type
[WebInvoke(Method = "OPTIONS", UriTemplate = "/login")]
void OptionsLogin();
[WebInvoke(Method = "POST", UriTemplate = "/login", ResponseFormat = WebMessageFormat.Json)]
LogInResult LogIn(LogInInformation loginInformation);
This method is in addition to the one that actually does and returns something.
However I believe the best method if you have many methods that will end up getting called in this way is to implement an OperationInvoker as mentioned by Petar and as shown here that does not call the underlying method.

Catch WCF FaultException in Client running on Mono

I have a server/client that uses NetTcp duplex channel and I'm trying to figure out how much work is required to run it under mono (v2.10.8).
Can someone tell me how to correctly throw a FaultException to a client that runs under mono?? Or if this isn't fully supported?
The contract is defined as:
[OperationContract(IsInitiating = true)]
[FaultContract(typeof(IOException))]
string ConnectWithFault(string clientVersion, string workstation)
and the server side method will always generate the fault for testing purposes:
public string ConnectWithFault(string clientVersion, string workstation)
{
IOException ioe = new IOException("Testing Fault Contract");
throw new FaultException<IOException>(ioe, new FaultReason(ioe.Message), new FaultCode("Sender"));
return "Should Never Reach Here!";
}
Everything works fine when I run the client normally in windowns, but when I run the client in mono command prompt in Windows, the FaultException does seem to come through to the client, but I’m not able to catch it properly. Or is there an internal mono error when it’s trying to catch the FaultException on the client side?? I’ve tried to catch is in a few different guises:
public void TestConnectWithFault()
{
Console.WriteLine("Client->TestConnectWithFault() +");
try
{
string response = RemoteSessionChannel.ConnectWithFault("client1.0", "MY-PC");
Console.WriteLine("**** DuplexClient->Connect Response: {0}", response);
}
catch (FaultException<IOException> ioe)
{
Console.WriteLine("Connect failed. (IOException): {0}", ioe.ToString());
}
catch (FaultException fe)
{
Console.WriteLine("Connect failed. (FaultException): {0}", fe.ToString());
}
catch (Exception ex)
{
Console.WriteLine("Connect failed. (Exception): {0}", ex.ToString());
}
Console.WriteLine("Client->TestConnectWithFault() -");
}
But I always just get the following output in the client console and the client hangs:
Client->TestConnectWithFault() +
**System.ServiceModel.FaultException: Testing Fault Contract
at System.ServiceModel.MonoInternal.DuplexClientRuntimeChannel.ProcessInputCor
e (IInputChannel input, System.ServiceModel.Channels.Message message) [0x00000]
in <filename unknown>:0
at System.ServiceModel.MonoInternal.DuplexClientRuntimeChannel.ProcessInput (I
InputChannel input, System.ServiceModel.Channels.Message message) [0x00000] in <
filename unknown>:0**
So it looks like the exception is coming across to the client, but isn't getting picked up by any of the catch blocks, or have I not got this right?
There has been bug #7177 that I fixed in Mono last week.
I quickly tested this and it's working fine now.