This guide covers how to configure HTTPS/SSL encryption with certificates in nanoFramework WebServer.
The WebServer supports HTTPS connections using X.509 certificates for encrypted communication. This is essential for production deployments, especially when using authentication or transmitting sensitive data.
This section will give you an overview of basic certificate generation and how to use those with nanoFramework.
You can use openssl to generate a self-signed certificate for testing. Download on different platforms is available here.
# Generate private key and certificate
openssl req -newkey rsa:2048 -nodes -keyout server.key -x509 -days 365 -out server.crt
# Create password-protected private key
openssl rsa -des3 -in server.key -out server-encrypted.key
# Enter password when prompted (e.g., "1234")You'll get two files:
server.crt- The certificate (public key)server-encrypted.key- The encrypted private key
With the generated certificates in the previous step:
public static void Main()
{
// Certificate as string constants
const string serverCrt = @"-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAKL0UG+mRnNjMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
... (rest of certificate)
-----END CERTIFICATE-----";
const string serverKey = @"-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,2B9FDBE5B2B6AD34
WgJz8pSS8RQUFNjPsrG/s2pGYCJ5FghVnS5s6H+mDfY0+XqJdBcm0I2WZLy/
... (rest of encrypted private key)
-----END RSA PRIVATE KEY-----";
// Create certificate object
X509Certificate2 certificate = new X509Certificate2(
Encoding.UTF8.GetBytes(serverCrt),
Encoding.UTF8.GetBytes(serverKey),
"1234" // Password used to encrypt the private key
);
// Create HTTPS server
using (WebServer server = new WebServer(443, HttpProtocol.Https, new Type[] { typeof(MyController) }))
{
server.HttpsCert = certificate;
server.SslProtocols = SslProtocols.Tls12;
server.Start();
Thread.Sleep(Timeout.Infinite);
}
}public class SecureController
{
[Route("secure/data")]
[Authentication("Basic")]
public void GetSecureData(WebServerEventArgs e)
{
var data = new
{
message = "Secure data over HTTPS",
timestamp = DateTime.UtcNow,
encrypted = true
};
string json = JsonConvert.SerializeObject(data);
e.Context.Response.ContentType = "application/json";
WebServer.OutputAsStream(e.Context.Response, json);
}
}
public static void Main()
{
// Connect to WiFi
var connected = WifiNetworkHelper.ConnectDhcp(ssid, password, requiresDateTime: true);
if (!connected) return;
// Load certificate
X509Certificate2 cert = LoadCertificate();
using (WebServer server = new WebServer(443, HttpProtocol.Https, new Type[] { typeof(SecureController) }))
{
// Configure HTTPS
server.HttpsCert = cert;
server.SslProtocols = SslProtocols.Tls12;
// Configure authentication
server.Credential = new NetworkCredential("admin", "securepass");
server.Start();
Debug.WriteLine("HTTPS server started on port 443");
Thread.Sleep(Timeout.Infinite);
}
}
private static X509Certificate2 LoadCertificate()
{
// Certificate content (replace with your actual certificate)
const string certContent = @"-----BEGIN CERTIFICATE-----
... your certificate content here ...
-----END CERTIFICATE-----";
const string keyContent = @"-----BEGIN RSA PRIVATE KEY-----
... your encrypted private key here ...
-----END RSA PRIVATE KEY-----";
return new X509Certificate2(
Encoding.UTF8.GetBytes(certContent),
Encoding.UTF8.GetBytes(keyContent),
"your-private-key-password"
);
}This section goes through different options for the SSL configuration.
server.SslProtocols = SslProtocols.Tls12;server.SslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;SslProtocols.Tls- TLS 1.0SslProtocols.Tls11- TLS 1.1SslProtocols.Tls12- TLS 1.2 (minimum recommended)SslProtocols.Tls13- TLS 1.3
You have 4 diufferent ways to store certificates and use them:
- Embed in code: this is not really recommended for production as you cannot easilly replace the certificate except by flashing the device with a new code.
- From resources: similar as for the in code, this solution won't allow you to easilly replace the certificate.
- From Visual Studio: in the .NET nanoFramework extension, select your device, then
Edit Network Configuration, thenGeneral, you can upload root CA and device CA. - From storage: typically added in the (typically internal) storage at setup time or flash time, this is suitable for production as can also be replaced more easilly.
- At flash time: during the initial flash process, this can be deployed as well as the wifi credential. Also suitable for production and also easier to flash later on.
public static class Certificates
{
public const string ServerCertificate = @"-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAKL0UG+mRnNjMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
... certificate content ...
-----END CERTIFICATE-----";
public const string ServerPrivateKey = @"-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
... private key content ...
-----END RSA PRIVATE KEY-----";
}[!Note] You can upload mutliple certificates at once, add them one after the other one, in the same file/string each properly separated by the
BEGIN CERTIFICATEandEND CERTIFICATEmarkers.
// Add certificate files as embedded resources in your project
public static X509Certificate2 LoadFromResources()
{
var certBytes = Resources.GetBytes(Resources.BinaryResources.server_crt);
var keyBytes = Resources.GetBytes(Resources.BinaryResources.server_key);
return new X509Certificate2(certBytes, keyBytes, "password");
}// If your device supports file system
public static X509Certificate2 LoadFromFiles()
{
var certBytes = File.ReadAllBytes("I:\\certificates\\server.crt");
var keyBytes = File.ReadAllBytes("I:\\certificates\\server.key");
return new X509Certificate2(certBytes, keyBytes, "password");
}You can upload the certificates directly into the device by following the steps described in the naoff documentation.
Note that the same method can be used as well to deploy the certificated into the internal storage as well.
There are couple of elements to consider when creating a certificate and embedding it into your nanoFramework device.
For production, use certificates from a trusted Certificate Authority (CA):
- Let's Encrypt - Free certificates, typically used for hobbyist usage which still want some security
- Commercial CAs - Paid certificates, typically used for a device you'll sell
- Internal CA - For enterprise environments (commercial CA can also be used in enterprise environments)
Because self-signed certificates aren't trusted by browsers, you may need to:
- Install certificate in browser/OS certificate store
- Add security exception in browser
- Use proper CA-signed certificate for production
- Save the certificate as
server.crt - Double-click the file
- Click "Install Certificate..."
- Choose "Local Machine" or "Current User"
- Select "Trusted Root Certification Authorities"
- Complete the installation
The Model Context Procol server also works with HTTPS.
public static void Main()
{
X509Certificate2 cert = LoadCertificate();
// Discover MCP tools
McpToolRegistry.DiscoverTools(new Type[] { typeof(IoTTools) });
using (var server = new WebServer(443, HttpProtocol.Https, new Type[] { typeof(McpServerController) }))
{
// Configure HTTPS
server.HttpsCert = cert;
server.SslProtocols = SslProtocols.Tls12;
// Configure MCP
McpServerController.ServerName = "SecureIoTDevice";
McpServerController.Instructions = "Secure IoT device accessible via HTTPS only";
server.Start();
Debug.WriteLine("Secure MCP server started");
Thread.Sleep(Timeout.Infinite);
}
}Here are several ways to test your HTTPS endpoints securely and effectively:
The REST Client extension for VS Code provides an excellent way to test your HTTPS endpoints directly from your development environment.
Create a .http file in your project:
### Test basic HTTPS endpoint
GET https://192.168.1.100/api/status
Accept: application/json
### Test with authentication
GET https://192.168.1.100/secure/data
Authorization: Basic YWRtaW46cGFzc3dvcmQ=
Accept: application/json
### Test MCP over HTTPS
POST https://192.168.1.100/mcp
Content-Type: application/json
{
"jsonrpc": "2.0",
"method": "tools/list",
"id": 1
}
### Test file upload
POST https://192.168.1.100/api/upload
Content-Type: multipart/form-data; boundary=boundary
--boundary
Content-Disposition: form-data; name="file"; filename="test.txt"
Content-Type: text/plain
Hello World!
--boundary--For Self-Signed Certificates: Add these settings to your VS Code settings.json:
{
"rest-client.enableTelemetry": false,
"rest-client.environmentVariables": {
"$shared": {
"host": "192.168.1.100"
}
},
"http.proxyStrictSSL": false
}For production environments, always verify certificates properly:
# Test with proper certificate verification
curl --cacert ca-certificate.pem https://192.168.1.100/api/status
# If using a certificate authority bundle
curl --capath /etc/ssl/certs https://192.168.1.100/api/status
# Test with client certificate authentication
curl --cert client.pem --key client-key.pem https://192.168.1.100/secure/data
# Test with basic authentication and proper certificate verification
curl --cacert ca-certificate.pem -u admin:password https://192.168.1.100/secure/data
# MCP over HTTPS with certificate verification
curl --cacert ca-certificate.pem -X POST https://192.168.1.100/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'Warning: Only use these commands during development with self-signed certificates. Never use -k in production:
# DEVELOPMENT ONLY: Ignore certificate verification (NOT for production)
curl -k https://192.168.1.100/api/status
# DEVELOPMENT ONLY: With authentication, ignoring certificate errors
curl -k -u admin:password https://192.168.1.100/secure/data
# DEVELOPMENT ONLY: MCP testing with certificate verification disabled
curl -k -X POST https://192.168.1.100/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'To properly verify your self-signed or custom certificates:
On Windows (PowerShell):
# Add to Windows certificate store
Import-Certificate -FilePath "ca-certificate.crt" -CertStoreLocation "Cert:\LocalMachine\Root"
# Test after adding to store
curl https://192.168.1.100/api/statusOn Linux:
# Copy CA certificate to system store
sudo cp ca-certificate.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
# Test with system certificates
curl https://192.168.1.100/api/statusOn macOS:
# Add to system keychain
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ca-certificate.crt
# Test with system certificates
curl https://192.168.1.100/api/statusModern browsers provide excellent tools for testing HTTPS endpoints:
- Network Tab: Monitor request/response details, timing, and certificate information
- Security Tab: View certificate details and validation status
- Console: Use
fetch()API for programmatic testing
// Test API endpoint from browser console
fetch('https://192.168.1.100/api/status')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
// Test with authentication
fetch('https://192.168.1.100/secure/data', {
headers: {
'Authorization': 'Basic ' + btoa('admin:password')
}
})
.then(response => response.json())
.then(data => console.log(data));- Certificate not trusted: Install certificate in browser/OS
- Wrong private key password: Verify the password used during key encryption
- Certificate format: Ensure PEM format with proper headers
- Port conflicts: Ensure port 443 is available
- Memory issues: HTTPS uses more memory than HTTP