Connect to AWS MQTT - c#

Trying to connect to AWS IOT device from C# by using M2MQTT library :
public void connect()
{
const string IotEndpoint = "a3cnel9bxxxxx-ats.iot.eu-central-1.amazonaws.com";
const int BrokerPort = 8883;
const string Topic = "topic_1";
X509Certificate clientCert = X509Certificate2.CreateFromCertFile("C:\\cpp_test\\certs\\pi4\\certificate.pem.crt");
X509Certificate caCert = X509Certificate.CreateFromCertFile("C:\\cpp_test\\certs\\Amazon-root-CA-1.pem");
MqttClient client = new MqttClient(IotEndpoint, BrokerPort, true, caCert, clientCert, MqttSslProtocols.TLSv1_2);
String message = "Test message";
string clientId = Guid.NewGuid().ToString();
try
{
client.Connect(clientId);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
if (ex.InnerException != null)
MessageBox.Show("Inner exception: {0}", ex.InnerException.Message);
}
client.Publish(Topic, Encoding.UTF8.GetBytes(message));
}
Got exception:
Received an unexpected EOF or 0 bytes from the transport stream.
What might be wrong? Where is starting point of problem solving?

Getting no data is often a result of the SSL handshake failing or protocols not being supported, verify the certificates and protocols are correct.

Related

Getting a 400 Bad Request when trying to connect to an ONVIF camera

I have a Xamarin Android app that has a feature for taking snapshots of external cameras. Until now we were using some models that provided us access from HTTP with CGI for this.
However, these models were discontinuated and we are forced to change for models that provide ONVIF protocol.
I created an additional ClassLibrary project in my solution (once it is not possible to add Services References directly in Xamarin Android projects) to handle this function. And in this project I added a Service Reference to ONVIF wsdl (http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl).
So, I created the following function:
public string GetSnapshotUri(string cameraIPAddress, string username, string password)
{
try
{
var messageElement = new TextMessageEncodingBindingElement()
{
MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None)
};
HttpTransportBindingElement httpBinding = new HttpTransportBindingElement()
{
AuthenticationScheme = AuthenticationSchemes.Basic
};
CustomBinding bind = new CustomBinding(messageElement, httpBinding);
var mediaClient = new MediaClient(bind, new EndpointAddress($"http://{ cameraIPAddress }/onvif/Media"));
mediaClient.ClientCredentials.UserName.UserName = username;
mediaClient.ClientCredentials.UserName.Password = password;
Profile[] profiles = mediaClient.GetProfiles();
string profileToken = profiles[0].token;
MediaUri mediaUri = mediaClient.GetSnapshotUri(profileToken);
return mediaUri.Uri;
}
catch (WebException ex)
{
return ex.Message;
}
catch (Exception ex)
{
return ex.Message;
}
}
But when the function is called and the mediaClient.GetProfiles() method is reached, an error is thrown:
**
System.Net.WebException: 'There was an error on processing web
request: Status code 400(BadRequest): Bad Request'
**
see error message
I've tried to search for any related problem, but everything I've tried didn't work.
Any suggestions?
Link related: ONVIF api capture image in C#
Thanks!
After a long time, I finally had success.
Here is the final solution:
public Byte[] GetSnapshotBytes(string ip, string user, string password)
{
try
{
if (string.IsNullOrEmpty(ip)) return null;
var snapshotUri = string.Format("http://{0}:80/onvifsnapshot/media_service/snapshot?channel=1&subtype=0", ip);
NetworkCredential myCredentials = new NetworkCredential(user, password);
WebRequest myWebRequest = WebRequest.Create(snapshotUri);
myWebRequest.Credentials = myCredentials.GetCredential(new Uri(snapshotUri), "");
using (HttpWebResponse lxResponse = (HttpWebResponse)myWebRequest.GetResponse())
{
using (BinaryReader reader = new BinaryReader(lxResponse.GetResponseStream()))
{
Byte[] lnByte = reader.ReadBytes(1 * 1024 * 1024 * 10);
return lnByte;
}
}
}
catch (Exception)
{
throw;
}
}

Why "(502) Bad Gateway" error happen?

Windows 7 SP1.
Domain network.
.NET Framework 4.6.1.
All my Internet browsers have configured proxy settings for Internet connections (it works fine).
I need to download file from Internet. I configure WebClient for it will read proxy settings from default Internet browser and use credential of current process and I expected these conditions are enough for successfull downloading. But I get the exception (look the comment in my code):
static void Main(string[] args) {
String file_name = Path.GetRandomFileName();
String full_path = Environment.ExpandEnvironmentVariables(
Path.Combine(#"%LocalAppData%\Temp", file_name));
using (WebClient client = new WebClient()) {
client.Credentials = CredentialCache.DefaultCredentials;
//client.Proxy = WebRequest.GetSystemWebProxy();
var proxyUri = WebRequest.GetSystemWebProxy()
.GetProxy(new Uri("https://yadi.sk/i/jPScGsw9qiSXU"));
try {
client.DownloadFile(proxyUri, full_path);
}
catch (Exception ex) {
// The remote server returned an error: (502) Bad Gateway.
Console.WriteLine(ex.Message);
}
}
Console.WriteLine("Press any key for exit.");
Console.ReadKey();
}
What I did wrong?
You need to retrieve the proxy for the specific URL then set it as the proxy URL of the web request.
static void Main(string[] args) {
String file_name = Path.GetRandomFileName();
String full_path = Environment.ExpandEnvironmentVariables(
Path.Combine(#"%LocalAppData%\Temp", file_name));
using (WebClient client = new WebClient()) {
client.Credentials = CredentialCache.DefaultCredentials;
var proxyUri = WebRequest.GetSystemWebProxy()
.GetProxy(new Uri("https://yadi.sk/i/jPScGsw9qiSXU"));
client.Proxy = new WebProxy(proxyUri);
client.Proxy.Credentials = CredentialCache.DefaultCredentials;
try {
client.DownloadFile("https://yadi.sk/i/jPScGsw9qiSXU", full_path);
}
catch (Exception ex) {
// The remote server returned an error: (502) Bad Gateway.
Console.WriteLine(ex.Message);
}
}
Console.WriteLine("Press any key for exit.");
Console.ReadKey();
}
This is implemented just in case the proxy uri is different depending on the url you are trying to access.

iOS (APN) - Authentication failed because the remote party has closed the transport stream in C#

While sending push notifications from C# code to apple phone i am getting this error. I googled for this issue but those answers could not solve this problem.
Can any one give me suggestions to fix this issue.
protected void Page_Load(object sender, EventArgs e)
{
string deviceID = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
string certificateFilePath ="XXXXXXX.cer";
pushMessage(deviceID, certificateFilePath);
}
public void pushMessage(string deviceID, string certificateFilePath)
{
try
{
int port = 2195;
String hostname = "gateway.sandbox.push.apple.com";
String certificatePath = HttpContext.Current.Server.MapPath(certificateFilePath);
X509Certificate2 clientCertificate = new X509Certificate2(certificatePath);
X509Certificate2Collection certificatesCollection = new X509Certificate2Collection(clientCertificate);
TcpClient client = new TcpClient(hostname, port);
SslStream sslStream = new SslStream(
client.GetStream(),
false,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
null
);
try
{
sslStream.AuthenticateAsClient(hostname, certificatesCollection, System.Security.Authentication.SslProtocols.Default, true);
//sslStream.AuthenticateAsClient(hostname, certificatesCollection, System.Security.Authentication.SslProtocols.Default, false);
//sslStream.AuthenticateAsClient(hostname, certificatesCollection, System.Security.Authentication.SslProtocols.Tls, true);
}
catch (AuthenticationException ex)
{
client.Close();
return;
}
MemoryStream memoryStream = new MemoryStream();
BinaryWriter writer = new BinaryWriter(memoryStream);
writer.Write((byte)0);
writer.Write((byte)0);
writer.Write((byte)32);
String deviceId = deviceID;
writer.Write(HexStringToByteArray(deviceId.ToUpper()));
String payload = "{\"aps\":{\"alert\":\"Test...\",\"badge\":1}}";
writer.Write((byte)0);
writer.Write((byte)payload.Length);
byte[] b1 = System.Text.Encoding.UTF8.GetBytes(payload);
writer.Write(b1);
writer.Flush();
byte[] array = memoryStream.ToArray();
sslStream.Write(array);
sslStream.Flush();
client.Close();
}
catch { }
}
Have you provide Certificate password to authenticate your certificate?
I recommended to use PushSharp save time and rids of messy coding.
PushSharp is a server-side library for sending Push Notifications to iOS (iPhone/iPad APNS), Android (C2DM and GCM - Google Cloud Message), Windows Phone, Windows 8, Amazon, Blackberry, and (soon) FirefoxOS devices!

Incomplete messages (C# TCP/IP Client)

First of all, I'm absolutely not a network programmer. What I try to do, is a very simple TCP/IP communication between a Java server and a C# client.
Java server:
public void run(){
try {
// Open server socket
_server = new ServerSocket(SERVER_PORT);
_client = _server.accept();
System.out.println("ComInterface: client connected.");
// Wait for a client data output stream
while(true){
// Receive message from client
BufferedReader is =
new BufferedReader(new InputStreamReader(_client.getInputStream()));
msg = is.readLine();
// Process message
if(msg!=null){
System.out.println("ComInterface: Message Received : <" + msg + ">.");
processMessage(msg); // Independant method
}
else{
System.out.println("ComInterface: client closed connection.");
_client.close();
_client = _server.accept();
System.out.println("ComInterface: client connected.");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void sendMessage(String msg){
try {
// Out stream
DataOutputStream os = new DataOutputStream(_client.getOutputStream());
os.writeBytes((String)(msg+"\n"+(char)13));
os.flush();
System.out.println("ComInterface: Message <" + msg + "> sent");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
And here's the C# client:
public class ComInterface : MonoBehaviour
{
public const String SERVER_IP = "127.0.0.1"; // Localhost
public const int PORT = 1100; // Default port
public const int READ_BUFFER_SIZE = 5000; // 4.8828125 kilobytes
private TcpClient _client;
private ASCIIEncoding _asen;
private byte[] _readBuffer;
private String _msg;
public Boolean connected { get; internal set; } // setter is for internal use only
/**
* Initialize internal variables (buffer, socket...)
*/
public ComInterface()
{
connected = false;
_client = new TcpClient();
_asen = new ASCIIEncoding();
_readBuffer = new Byte[READ_BUFFER_SIZE];
_msg = String.Empty;
}
/**
* Connect to server at SERVER_IP:PORT
* Return true if connection was a success, or false if failure.
*/
public Boolean Connect()
{
try
{
_client.Connect(SERVER_IP, PORT);
connected = true;
Array.Clear(_readBuffer, 0, _readBuffer.Length);
Debug.Log("TCPClient: <Connect> Connected to the server");
// Start an asynchronous read invoking ReceiveComMessage
_client.GetStream().BeginRead(_readBuffer, 0, READ_BUFFER_SIZE, new AsyncCallback(ReceiveComMessage), _client.GetStream());
}
catch (Exception ex)
{
Debug.Log("TCPClient: <Connect> Cannot connect to the server - " + ex.Message);
connected = false;
}
// Return connection state
return connected;
}
/**
* Received a message from Communicator
*/
private void ReceiveComMessage(IAsyncResult ar)
{
int BytesRead;
String msg;
try
{
BytesRead = _client.GetStream().EndRead(ar);
if (BytesRead < 1)
{
// if no bytes were read server has close.
Debug.Log("TCPClient: <ReceiveComMessage> The server has closed (BytesRead<1)");
this.Disconnect();
return;
}
// Convert the byte array the message was saved into,
msg = Encoding.ASCII.GetString(_readBuffer);
Debug.Log("C# Message: \"" + msg + "\""); // Output example in log below
BytesRead = 0;
Array.Clear(_readBuffer, 0, _readBuffer.Length);
// Start a new asynchronous read into readBuffer.
_client.GetStream().BeginRead(_readBuffer, 0, READ_BUFFER_SIZE, new AsyncCallback(ReceiveComMessage), _readBuffer);
}
catch (Exception ex)
{
Debug.Log("TCPClient: <ReceiveComMessage> The server has closed (Exception):" + ex.Message + " see " + ex.StackTrace);
this.Disconnect();
}
The main problem is that all the message are arriving incomplete. Here's the log trace:
C#: Message "{
C#: Message ""sender":"Bob"",
C#: Message ""recipient":",
etc...
Instead of for instance
C#: Message "{"sender":"Bob","recipient":[1,2,3]}"
I'm a bit confused and I'd need some help to resolve this. Thank you very much!
TCP is a stream-oriented connection, not message-oriented. It has no concept of a message. When you write out your serialized string, it only sees a meaningless sequence of bytes. TCP is free to break up that stream up into multiple fragments and they will be received at the client in those fragment-sized chunks. It is up to you to reconstruct the entire message on the other end.
In your scenario, one would typically send a message length prefix. This way, the client first reads the length prefix so it can then know how large the incoming message is supposed to be.
I would seriously consider using something like Google's Protocol Buffers as a good way of declaring your messages and then streaming them with the size prefix option. The nice thing is that you define your set of messages once and then use the available tools to automatically generate C++, Java, C#, etc code from the message definitions. This will help in having a consistent messaging set that works between languages.
A message (any data, I mean), when sent through a socket, is divided into several packets.
When printing each received packet, you don't see your whole message.
You should define an end of message string (something like ".#."). Until you receive this sequence, you keep concatenating the messages you receive.
This is what session protocols (that is, protocols that run on the top of TCP) do.
Hope this helps.
Regards, Calil
Take a look at this example...
Java TCP Server...
import java.net.*;
import java.io.*;
public class TcpServer
{
public static void main(String h[])
{
try
{
ServerSocket serverSocket = new ServerSocket(1100);
Socket socket = serverSocket.accept();
System.out.println("Client Accepted");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("Received: " + bufferedReader.readLine());
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
printWriter.println("Hello Theo. Welcome to socket programming.");
} catch (Exception e)
{
System.out.println(e);
}
}
}
C# TCP Client...
using System;
using System.IO;
using System.Net.Sockets;
class Program
{
static void Main(string[] args)
{
try
{
var client = new TcpClient("localhost", 1100);
var stream = client.GetStream();
var streamWriter = new StreamWriter(stream);
streamWriter.WriteLine("My name is Theo");
streamWriter.Flush();
var streamReader = new StreamReader(stream);
Console.WriteLine("Received: " + streamReader.ReadLine());
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.WriteLine("Press a key to continue.");
Console.ReadKey();
}
}

SSL TCP SslStream Server throws unhandled exception "System.Security.Cryptography.CryptographicException: cannot find the original signer"

I'm trying to create a C# TCP server to receive TCP data with SslStream on a Windows 2008 server from a client (objective C mobile application) sending TCP data.
I'm using Microsoft's sample code (NOTE: my modified version of that code is at the end of this question) i.e. it is the server code, just under the line "The following code example demonstrates creating an TcpListener that uses the SslStream class to communicate with clients.")
Exception
However, when I run this server code I get the following exception:
System.Security.Cryptography.CryptographicException: Cannot find the original si
gner.
at System.Security.Cryptography.CryptographicException.ThrowCryptographicExce
ption(Int32 hr)
at System.Security.Cryptography.X509Certificates.X509Utils._LoadCertFromFile(
String fileName, IntPtr password, UInt32 dwFlags, Boolean persistKeySet, SafeCer
tContextHandle& pCertCtx)
at System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertific
ateFromFile(String fileName, Object password, X509KeyStorageFlags keyStorageFlag
s)
at System.Security.Cryptography.X509Certificates.X509Certificate.CreateFromCe
rtFile(String filename)
at SslTcpServer.LocationSslTcpServer.RunServer(String certificate) in c:\SslTcpServer\Program.cs:line 20
at SslTcpServer.Program.Main(String[] args) in c:\SslTcpServer\Program.cs:line 180
I've also tried the code at http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate.aspx and it throws a similar exception.
Additional Information
I got my SSL certificate from NameCheap. I purchased the EssentialSSL Wildcard certificate. I made a Created Certificate Request on the Windows 2008 server i.e. that gaint text starting with:
-----BEGIN NEW CERTIFICATE REQUEST-----
alots of random characters
-----END NEW CERTIFICATE REQUEST-----
and uploaded that gaint text file to NameCheap and got emailed a Certificate.cer file.
My Code
public sealed class LocationSslTcpServer
{
static X509Certificate serverCertificate = null;
// The certificate parameter specifies the name of the file
// containing the machine certificate.
public static void RunServer(string certificate)
{
serverCertificate = X509Certificate.CreateFromCertFile(certificate);
// Create a TCP/IP (IPv4) socket and listen for incoming connections.
TcpListener listener = new TcpListener(IPAddress.Any, 8080);
listener.Start();
while (true)
{
Console.WriteLine("Waiting for a client to connect...");
// Application blocks while waiting for an incoming connection.
// Type CNTL-C to terminate the server.
TcpClient client = listener.AcceptTcpClient();
ProcessClient(client);
}
}
static void ProcessClient(TcpClient client)
{
// A client has connected. Create the
// SslStream using the client's network stream.
SslStream sslStream = new SslStream(
client.GetStream(), false);
// Authenticate the server but don't require the client to authenticate.
try
{
sslStream.AuthenticateAsServer(serverCertificate,
false, SslProtocols.Tls, true);
// Display the properties and settings for the authenticated stream.
DisplaySecurityLevel(sslStream);
DisplaySecurityServices(sslStream);
DisplayCertificateInformation(sslStream);
DisplayStreamProperties(sslStream);
// Set timeouts for the read and write to 5 seconds.
sslStream.ReadTimeout = 5000;
sslStream.WriteTimeout = 5000;
// Read a message from the client.
Console.WriteLine("Waiting for client message...");
string messageData = ReadMessage(sslStream);
Console.WriteLine("Received: {0}", messageData);
// Write a message to the client.
byte[] message = Encoding.UTF8.GetBytes("Hello from the server.<EOF>");
Console.WriteLine("Sending hello message.");
sslStream.Write(message);
}
catch (AuthenticationException e)
{
Console.WriteLine("Exception: {0}", e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
}
Console.WriteLine("Authentication failed - closing the connection.");
sslStream.Close();
client.Close();
return;
}
finally
{
// The client stream will be closed with the sslStream
// because we specified this behavior when creating
// the sslStream.
sslStream.Close();
client.Close();
}
}
static string ReadMessage(SslStream sslStream)
{
// Read the message sent by the client.
// The client signals the end of the message using the
// "<EOF>" marker.
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
do
{
// Read the client's test message.
bytes = sslStream.Read(buffer, 0, buffer.Length);
// Use Decoder class to convert from bytes to UTF8
// in case a character spans two buffers.
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
// Check for EOF or an empty message.
if (messageData.ToString().IndexOf("<EOF>") != -1)
{
break;
}
} while (bytes != 0);
return messageData.ToString();
}
static void DisplaySecurityLevel(SslStream stream)
{
Console.WriteLine("Cipher: {0} strength {1}", stream.CipherAlgorithm, stream.CipherStrength);
Console.WriteLine("Hash: {0} strength {1}", stream.HashAlgorithm, stream.HashStrength);
Console.WriteLine("Key exchange: {0} strength {1}", stream.KeyExchangeAlgorithm, stream.KeyExchangeStrength);
Console.WriteLine("Protocol: {0}", stream.SslProtocol);
}
static void DisplaySecurityServices(SslStream stream)
{
Console.WriteLine("Is authenticated: {0} as server? {1}", stream.IsAuthenticated, stream.IsServer);
Console.WriteLine("IsSigned: {0}", stream.IsSigned);
Console.WriteLine("Is Encrypted: {0}", stream.IsEncrypted);
}
static void DisplayStreamProperties(SslStream stream)
{
Console.WriteLine("Can read: {0}, write {1}", stream.CanRead, stream.CanWrite);
Console.WriteLine("Can timeout: {0}", stream.CanTimeout);
}
static void DisplayCertificateInformation(SslStream stream)
{
Console.WriteLine("Certificate revocation list checked: {0}", stream.CheckCertRevocationStatus);
X509Certificate localCertificate = stream.LocalCertificate;
if (stream.LocalCertificate != null)
{
Console.WriteLine("Local cert was issued to {0} and is valid from {1} until {2}.",
localCertificate.Subject,
localCertificate.GetEffectiveDateString(),
localCertificate.GetExpirationDateString());
}
else
{
Console.WriteLine("Local certificate is null.");
}
// Display the properties of the client's certificate.
X509Certificate remoteCertificate = stream.RemoteCertificate;
if (stream.RemoteCertificate != null)
{
Console.WriteLine("Remote cert was issued to {0} and is valid from {1} until {2}.",
remoteCertificate.Subject,
remoteCertificate.GetEffectiveDateString(),
remoteCertificate.GetExpirationDateString());
}
else
{
Console.WriteLine("Remote certificate is null.");
}
}
public static void DisplayUsage()
{
Console.WriteLine("To start the server specify:");
Console.WriteLine("serverSync certificateFile.cer");
Environment.Exit(1);
}
}
class Program
{
static int Main(string[] args)
{
string certificate = null;
certificate = "Certificate.cer";
try
{
LocationSslTcpServer.RunServer(certificate);
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
Console.ReadLine();
}
return 0;
}
}
Thanks in advance for helping!
I successfully used X.509 Digital Certificate Generator to:
Create CA certificate (Ca.pfx).
Create Client certificate signed with a CA certificate (Client.pfx).
Create Server certificate signed with a CA certificate (Server.pfx).
Install CA certificate (Ca.pfx) in the Trusted Root Certificate folder.
Install Client and Server certificate (Client.pfx and Server.pfx) in Personal folder.
For steps 4 and 5: From the Search Box (near the Windows start button - left bottom your desktop) typo: cert, then select the Manage Computer Certificates application.

Categories

Resources