Help! I am trying to use the Arduino WiFi101 library to connect to an existing SSL server. For troubleshooting - I'm actually connecting to www.howsmyssl.com. Whether I use the IP address or the host name, the connection fails every time. The board I am using is the Adafruit Feather M0 Wifi. I used the Firmware Updater to upload the certificate for www.howsmyssl.com to the board. Here is the code:
#include <stdint.h>
#include <WiFi101.h>
#include <stdlib.h>
WiFiSSLClient client;
char ssid[] = "SciAv";
char pass[] = "FlyAcclaim";
void setup() {
WiFi.setPins(8,7,4,2);
while ( WiFi.begin(ssid, pass) != WL_CONNECTED) {
Serial.println("Connecting to SciAv");
delay(10000);
}
Serial.println("WiFi Connected");
if (!client.connectSSL("www.howsmyssl.com",443)) {
Serial.println("Connection Failed");
}
else {
Serial.println("Connected");
}
}
void loop() {
if (client.connected()) {
Serial.println("Connect to host");
}
}
You verificate if the certificate upload succefully on the board? Because to connect a ssl connection the shield needs be loaded with the ssl certificate. You can try add this verification before the Wifi network connection:
// check for the presence of the shield:
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
// don't continue:
while (true);
}
Alternatively, you can try using method 'connect("www.howsmyssl.com",443)' instead of 'connectSSL..'. This is useful If you have a library that accepts only plain Client, but you want to force it to use SSL, keeping the same method names of the non SSL client.
I have the same problem with the NANO33 IoT board. I found that an SSL connect failed on a board with the latest microcode (revision 1.4.8), but worked on a board with back-level microcode (revision 1.2.3). Just swapping out the NANO33 board makes the problem show up or go away. Reflash the microcode to a working back-level version if you need an SSL internet connection, or wait for Arduino to fix their bug in the next microcode release. Arduino WiFiNINA library used was version 1.8.13.
Related
I'm working with an ESP32 and an MQTT Server to create a meteo station. I managed to connect the esp32 to the server without any problem, even outside the local network, and I could post and received messages. So I decide to add some security with SSL and it's certificate, and suddenly, nothing work anymore.
After searching for a bit, I narrowed the problem down : if my esp32 is in the local network, SSL and it's certificate work like a charm. So that obviously means that my certificates are ok, and that my server configuration is probably ok too. But when I try from outside the network, it's the certificates that raises the error. More specifically, it raises :
sslv3 alert bad certificate mqtt
Which at least mean that I can reach my server, I just can't connect to it. To add even more puzzling element to the problem, I try to connect to the server with a computer outside of the network using MQTT explorer, with SSL and the exact same certificates, and it work without a itch.
I'm using mosquitto for the server. My configuration file give :
listener 8883
require_certificate true
allow_anonymous true ## this one is here for debug purpose. Taking it out doesn't change anything.
certfile C:\Users\username\Documents\Arduino\MQTT\MQTT SSL\broker\broker.crt
keyfile C:\Users\username\Documents\Arduino\MQTT\MQTT SSL\broker\broker.key
cafile C:\Users\username\Documents\Arduino\MQTT\MQTT SSL\ca\ca.crt
And for my ESP32, I can reproduce my problem with this simplified code. It come in part from the code given in that link : https://iotdesignpro.com/projects/how-to-connect-esp32-mqtt-broker .
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
const char* ssid = "mySsid";
const char* password = "myPassword";
const char* mqtt_server = "publicIp";
#define mqtt_port 8883
// I don t really need those two next lines for now, but I intend to use them later
#define MQTT_USER "eapcfltj"
#define MQTT_PASSWORD "3EjMIy89qzVn"
#define MQTT_SERIAL_PUBLISH_CH "/icircuit/ESP32/serialdata/tx"
#define MQTT_SERIAL_RECEIVER_CH "/icircuit/ESP32/serialdata/rx"
const char AWS_PUBLIC_CERT[] = ("-----BEGIN CERTIFICATE-----\n\
...
-----END CERTIFICATE-----\n");
//client.crt
const char AWS_DEVICE_CERT[] = ("-----BEGIN CERTIFICATE-----\n\
...
-----END CERTIFICATE-----\n");
//client.key
const char AWS_PRIVATE_KEY[] = ("-----BEGIN RSA PRIVATE KEY-----\n\
...
-----END RSA PRIVATE KEY-----\n");
WiFiClientSecure wifiClient;
PubSubClient client(wifiClient);
void setup_wifi() {
delay(10);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte *payload, unsigned int length) {
Serial.println("-------new message from broker-----");
Serial.print("channel:");
Serial.println(topic);
Serial.print("data:");
Serial.write(payload, length);
Serial.println();
}
void reconnect() {
// Loop until we're reconnected
wifiClient.setCACert(AWS_PUBLIC_CERT);
wifiClient.setCertificate(AWS_DEVICE_CERT);
wifiClient.setPrivateKey(AWS_PRIVATE_KEY);
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "ESP32Client-";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str(),MQTT_USER,MQTT_PASSWORD)) {
Serial.println("connected");
client.subscribe(MQTT_SERIAL_RECEIVER_CH);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
Serial.setTimeout(500);// Set time out for
setup_wifi();
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
reconnect();
}
void loop() {
client.loop();
if (Serial.available() > 0) {
char mun[501];
memset(mun,0, 501);
Serial.readBytesUntil( '\n',mun,500);
if (!client.connected()) {
reconnect();
}
client.publish(MQTT_SERIAL_PUBLISH_CH, mun);
}
}
So. Since to reach my server from outside the local network I needed to add some port forwarding on my box, said box is my main suspect. I don't exactly know what happen, but I suspect either a loss of data, or something that come from the fact that the certificates are hardcoded. Like, maybe the "\n" are badly interpreted by my Internet box or by another element ? I don't know. I couldn't found anything to help me more, so really I could use any help on that problem. How could I manage to connect my esp32 to my server ?
Apologies for any bad english, and thank you in advance for your help !
SSL certificates are issued to a specific name or IP. When connecting, the certificate's fields (CN or SAN) have to match the name or IP of the target host. Maybe you issued the certificate to an address in your internal LAN like "192.168.0.2" or "mqtt.local". Those work fine for SSL connections inside the LAN since the cert and host name match. If you try to connect from Internet to your public IP or, e.g. "myhome.dyndns.org" it doesn't match the cert's fields. SSL connection is denied. Your cert would have to include those public addresses. MQTT explorer may ignore those errors and ESP not.
I tried to connect to a websocket server with SSL. But always failed on connection(...).
I am new to cpprestsdk, I can't find doc on how to set SSL information to websocket_client.
websocket_client_config config;
config.set_server_name("wss://host:port/v3/api");
websocket_client client(config);
auto fileStream = std::make_sharedconcurrency::streams::ostream();
pplx::task requestTask = fstream::open_ostream(U("results2.html"))
.then([&](ostream outFile)
{
*fileStream = outFile;
// Create http_client to send the request.
uri wsuri(U("wss://host:port/v3/api"));
client.connect(wsuri).wait();
websocket_outgoing_message msg;
msg.set_utf8_message(obj.serialize());
client.send(msg).wait();
printf("send success: %s\n", obj.serialize().c_str());
return client.receive().get();
})
it throws "Error exception:set_fail_handler: 8: TLS handshake failed".
Documentation for cpprestsdk can be found here
C++ REST SDK WebSocket client. Although this doesn't show all the necessary information related to cpprestsdk it will help you.
And also you can get an SSL test example here. I show a simple websocket client implemented using SSL or wss:// scheme
websocket_client client;
std::string body_str("hello");
try
{
client.connect(U("wss://echo.websocket.org/")).wait();
auto receive_task = client.receive().then([body_str](websocket_incoming_message ret_msg) {
VERIFY_ARE_EQUAL(ret_msg.length(), body_str.length());
auto ret_str = ret_msg.extract_string().get();
VERIFY_ARE_EQUAL(body_str.compare(ret_str), 0);
VERIFY_ARE_EQUAL(ret_msg.message_type(), websocket_message_type::text_message);
});
websocket_outgoing_message msg;
msg.set_utf8_message(body_str);
client.send(msg).wait();
receive_task.wait();
client.close().wait();
}
catch (const websocket_exception& e)
{
if (is_timeout(e.what()))
{
// Since this test depends on an outside server sometimes it sporadically can fail due to timeouts
// especially on our build machines.
return;
}
throw;
}
And further examples here to guide you get it successfully is found here
https://github.com/microsoft/cpprestsdk/wiki/Web-Socket
I tried to use Boost Asio and Openssl to post some json to a SSL server,my case was it always had problem which was has boost::asio::ssl::error::stream_truncated issue when I tried to shutdown the stream,now I tried to ignore the issue,I have no idea if I should ignore it or what wrong I have done?
boost version is 1.68.0,Openssl version 1.1.1, VS 2017 CE,Windows 7 x64,
here is my code
#include "root_certificates.hpp"
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/error.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <cstdlib>
#include <iostream>
#include <string>
#include <time.h>
#include<fstream>
#include <ctime>
#include <istream>
int postsslserver()
{
try
{
auto const host ="mydomain.com";
auto const port = "https";
auto const target ="/apps/postpage.html" ;
retcode = 0;
setlocale(LC_ALL, "");
pwmd5hashed = "mysecret";
std::string jsondata ="\"Double\":12.0000001,";
int version =11;
// The io_context is required for all I/O
boost::asio::io_context ioc;
// The SSL context is required, and holds certificates
ssl::context ctx{ ssl::context::sslv23_client };
//20181021
ctx.set_default_verify_paths();
// This holds the root certificate used for verification
//load_root_certificates(ctx);
// Verify the remote server's certificate
//ctx.set_verify_mode(ssl::verify_peer);
ctx.set_verify_mode(ssl::verify_none);
// These objects perform our I/O
tcp::resolver resolver{ ioc };
ssl::stream<tcp::socket> stream{ ioc, ctx };
// Set SNI Hostname (many hosts need this to handshake successfully)
if (!SSL_set_tlsext_host_name(stream.native_handle(), host))
{
boost::system::error_code ec{ static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category() };
throw boost::system::system_error{ ec };
}
// Look up the domain name
auto const results = resolver.resolve(host, port);
// Make the connection on the IP address we get from a lookup
boost::asio::connect(stream.next_layer(), results.begin(), results.end());
// Perform the SSL handshake
stream.handshake(ssl::stream_base::client);// error always occured this line of code,the error hints was "handshake: certificate verify failed"
// Set up an HTTP POST request message
http::request<http::string_body> req{ http::verb::post, target, version };
req.set(http::field::host, host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
req.set(http::field::content_type, "application/json");
req.set(http::field::body, jsondata);
// Send the HTTP request to the remote host
http::write(stream, req);
// This buffer is used for reading and must be persisted
boost::beast::flat_buffer buffer;
// Declare a container to hold the response
http::response<http::dynamic_body> res;
// Receive the HTTP response
http::read(stream, buffer, res);
// Write the message to standard out
std::cout << res << std::endl;
// Gracefully close the stream
boost::system::error_code ec;
stream.shutdown(ec);// the problem was here! it always get boost::asio::ssl::error::stream_truncated issue
if (ec == boost::asio::error::eof)
{
// Rationale:
ec.assign(0, ec.category());
}
if (ec!= boost::asio::ssl::error::stream_truncated)//then I tried to ignore it
{
std::cout << ec.message()<< endl;
throw boost::system::system_error{ ec };
}
// If we get here then the connection is closed gracefully
}
catch (std::exception const& e)
{
//std::cerr << "Error: " << e.what() << std::endl;
write_text_to_log_file(e.what());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
thank you very much
The correct way to securely shutdown a SSL socket is not by directly trying to shutdown it.
First you have to cancel any possible outstanding operations then initiate a shutdown and close the socket afterwards.
Here's a snippet of a working solution:
virtual void OnClose(boost::system::error_code &ec)
{
//...
_socket.lowest_layer().cancel(ec);
_socket.async_shutdown(std::bind(&Socket<T>::ShutdownHandler, this->shared_from_this(), std::placeholders::_1));
//...
}
void ShutdownHandler(const boost::system::error_code &ec)
{
//On async_shutdown both parties send and receive a 'close_notify' message.
//When the shutdown has been negotiated by both parties, the underlying
//transport may either be reused or closed.
//The initiator of the shutdown will enter the shutdown-handler with an
//error value of eof. (Socket was securely shutdown)
if (ec && ec != boost::asio::error::eof)
{
LogError(ec, Error::ErrorLocation(__FUNCTION__, __LINE__));
}
boost::system::error_code ec2;
_socket.lowest_layer().close(ec2);
if (ec2)
LogError(ec2, Error::ErrorLocation(__FUNCTION__, __LINE__));
_shutdownComplete.exchange(true);
}
Also: boost asio ssl async_shutdown always finishes with an error?
I'm having trouble when I try to connect my local Photon Server with Unity3D WebGL Build, using secure websocket connection. I can establish the connection with websockets (not the secure one) and any other environment other than WebGL builds (Even in Unity's play mode with WebGL configuration, it just won't work when I get a build). I'm guessing that the problem is related with my certificate but I'm not entirely sure. I tried self-signed one, and a real one.
Here is the error:
WebSocket connection to 'wss://localhost:19091' failed: Error in
connection establishment: net::ERR_INSECURE_RESPONSE
I already tried 127.0.0.1 instead of localhost, tried to change the port.
I got my socket code from Photon's website, link is here:
https://www.photonengine.com/sdks#onpremiseunity3d
My Client code(uses Photon's SocketWebTcp class) is like this:
using ExitGames.Client.Photon;
using UnityEngine;
public class HiveClient : IPhotonPeerListener
{
public PhotonPeer PhotonClient { get; set; }
public HiveClient() { }
public void Connect()
{
this.PhotonClient = new PhotonPeer(this, ConnectionProtocol.WebSocketSecure);
this.PhotonClient.SocketImplementationConfig.Add(ConnectionProtocol.WebSocketSecure, typeof(SocketWebTcp));
this.PhotonClient.Connect("wss://localhost:19091", "Hive");
}
public void DebugReturn(DebugLevel level, string message)
{
Debug.Log("DebugReturn: " + level.ToString() + " Message: " + message);
}
public void OnEvent(EventData eventData)
{
Debug.Log("OnEvent: " + eventData.Code);
}
public void OnOperationResponse(OperationResponse operationResponse)
{
Debug.Log("OnOperationResponse: " + operationResponse.DebugMessage);
}
public void OnStatusChanged(StatusCode statusCode)
{
Debug.Log("OnStatusChanged: " + statusCode.ToString());
}}
Here is app's websocket listener in photon server config:
<WebSocketListeners>
<WebSocketListener IPAddress="0.0.0.0" Port="19091" DisableNagle="true" InactivityTimeout="10000" Secure = "true" StoreName = "MY" CertificateName = "DESKTOP-PQ845BC" UseMachineStore = "true">
</WebSocketListener>
</WebSocketListeners>
Lastly, here is my self-signed certificate:
Thank you.
Solved this, It seems certificates doesn't work with ip adresses like 127.0.0.1. Certificate expects some address (like xxx.photon.com)
As a temporary solution, one can send a https request to defined ip and insert the ip chrome's allowed ips. This temp solution relates to this answer:
https://stackoverflow.com/a/43493521/3013806
I need to write a simple program for work that does the following:
read a config file
connect to a bunch of servers
establish a ssl socket
pull info form the server's x509 cert, expire date and hostname for now
email a report when its done
items 3 and 4 are things that I have had bad luck researching/googleing and I do not know java well, at all since 1.2 around 2001
A verbose but throughout guide about the inners of Java Cryptographic Extension is found at Oracles website as well: http://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html
I found a code snipit that tells me what I need to know about java at http://www.exampledepot.com/egs/javax.net.ssl/GetCert.html
here it is:
try {
// Create the client socket
int port = 443;
String hostname = "hostname";
SSLSocketFactory factory = HttpsURLConnection.getDefaultSSLSocketFactory();
SSLSocket socket = (SSLSocket)factory.createSocket(hostname, port);
// Connect to the server
socket.startHandshake();
// Retrieve the server's certificate chain
java.security.cert.Certificate[] serverCerts =
socket.getSession().getPeerCertificates();
// Close the socket
socket.close();
} catch (SSLPeerUnverifiedException e) {
} catch (IOException e) {
} catch (java.security.cert.CertificateEncodingException e) {
}