Introduction
This comprehensive guide covers SSL/TLS security implementation on NetBurner embedded devices. Whether you're securing a web interface, IoT communication, or industrial control system, this guide provides practical solutions for establishing encrypted, authenticated connections.
What You'll Learn:**
- Core SSL/TLS concepts explained clearly
- Multiple implementation approaches from simple to advanced
- Certificate management strategies
- Common issues and their solutions
- Production-ready deployment practices
Understanding SSL/TLS Fundamentals
Note: This document uses "SSL" as shorthand for SSL/TLS protocols. Modern implementations use TLS (Transport Layer Security), the successor to SSL.
The Security Problem
In a normal network connection, data travels in plain text. This creates two critical vulnerabilities:
┌────────┐ ┌────────┐
│ Client │─────────────────────────────────────>│ Server │
└────────┘ Unencrypted Data └────────┘
Vulnerability 1: Eavesdropping**
┌────────┐ ┌────────┐
│ Client │──────────┐ ┌────│ Server │
└────────┘ │ │ └────────┘
v ^
┌──────────┐ │
│ Attacker │─────────────────┘
│(Sniffing)│
└──────────┘
"I can read everything!"
Vulnerability 2: Man-in-the-Middle (MITM)**
┌────────┐ ┌────────┐
│ Client │──────────┐ ┌────│ Server │
└────────┘ │ │ └────────┘
v ^
┌──────────┐ │
│ Attacker │─────────────────┘
│ (Proxy) │
└──────────┘
"I can read AND modify everything!"
How SSL/TLS Solves These Problems
SSL/TLS provides three critical security features:
- Encryption: Data is scrambled so eavesdroppers see gibberish
- Authentication: Verify you're talking to the real server (not an attacker)
- Integrity: Detect if data has been tampered with
The Trust Chain: How Authentication Works
SSL/TLS uses a "chain of trust" similar to how we trust professionals:
Real-World Analogy: Trusting a Doctor
┌─────────────────┐
│ Medical School │ <── We trust the institution
└────────┬────────┘
│ issues diploma
v
┌─────────┐
│ Hospital│ <── Hospital verifies the diploma
└────┬────┘
│ employs
v
┌────────┐
│ Doctor │ <── We trust the doctor by extension
└────────┘
SSL/TLS Certificate Chain
┌──────────────────────┐
│ Root CA │ <── Trusted by browsers/clients (pre-installed)
│ (e.g., DigiCert) │
└──────────┬───────────┘
│ signs
v
┌──────────────────────┐
│ Intermediate CA │ <── Issues certificates for organizations
└──────────┬───────────┘
│ signs
v
┌──────────────────────┐
│ Server Certificate │ <── Your device's identity
│ CN: yourdevice.com │
└──────────────────────┘
Key Components of a Certificate:**
- Identity (Common Name/CN) - Who this certificate represents
- Public Key - Like an open padlock anyone can use to encrypt
- Signature - Cryptographic proof from a trusted CA
Public Key Cryptography Simplified
Think of it like a special mailbox:
┌─────────────────────────────────────────┐
│ Public Key = Open Padlock │
│ (Anyone can lock, share freely) │
│ │
│ ┌───────┐ │
│ │ [BOX] │ <── Locked Box │
│ │ [LCK] │ (Encrypted Data) │
│ └───────┘ │
│ │
│ Private Key = Key to Padlock │
│ (Only owner can unlock, KEEP SECRET!) │
│ │
│ ┌───────┐ │
│ │ [BOX] │ <── Unlocked Box │
│ │ [GEM] │ (Decrypted Data) │
│ └───────┘ │
└─────────────────────────────────────────┘
Critical Security Principle:**
[LOCK] The system is only as secure as the private key. If an attacker gets your private key, they can decrypt all traffic and impersonate your device.
Certificate Types at a Glance
| Type | Trust Level | Browser Warnings | Best For | Cost |
| Self-Signed | Low | [!] Yes | Development, testing, internal networks | Free |
| Private CA | Medium | [!] Yes (until CA installed) | Enterprise deployments, controlled environments | Free |
| Public CA | High | [OK] No | Production, public-facing devices | Varies |
| ACME (Let's Encrypt) | High | [OK] No | Production with public DNS | Free |
Quick Start Guide
For SSL Servers (Your NetBurner Device)
Option 1: Automatic (Recommended for Production)
If you have a public DNS name:**
#include <acmeservlet.h>
void UserMain(void *pd) {
AcmeConfig config;
config.domain = "mydevice.example.com";
config.email = "admin@example.com";
StartAcmeHttps(config);
printf("Secure HTTPS server running\n");
}
#define TICKS_PER_SECOND
System clock ticks per second.
Definition constants.h:49
void OSTimeDly(uint32_t to_count)
Delay the task until the specified value of the system timer ticks. The number of system ticks per se...
Definition nbrtos.h:1855
void init()
System initialization. Ideally called at the beginning of all applications, since the easiest Recover...
bool WaitForActiveNetwork(uint32_t ticks_to_wait=120 *TICKS_PER_SECOND, int interface=-1)
Wait for an active network connection on at least one interface.
If you only have local/private network:**
#include <init.h>
#include <startnet.h>
void UserMain(void *pd) {
printf("HTTPS server running (self-signed)\n");
}
void StartHttps(uint16_t ssl_port, uint16_t http_port)
Start the HTTPS secure web server.
For SSL Clients (Connecting to Servers)
#include <ssl.h>
#include <nettcp.h>
extern const char letsEncryptCA[];
extern const int letsEncryptCA_len;
void ConnectSecurely() {
int fd =
connect(
"api.example.com", 443);
if (ssl) {
SSL_write(ssl, "GET / HTTP/1.1\r\n\r\n", 18);
SSL_close(ssl);
}
}
bool SSL_AddCertToClientCaList(const unsigned char *certBuff=nullptr, uint32_t certBuffSize=0, uint16_t certBuffFormat=SSL_FILETYPE_PEM)
Attempts to add a cert to the Certificate Authority List to the client SSL context.
void SslInit()
This function initializes the SSL library.
int SSL_connect(IPADDR ip, uint16_t remotePort, uint32_t timeout, const char *commonName, bool verifyPeer=false, bool waitForNegotiation=true, const unsigned char *certBuff=nullptr, uint32_t certBuffSize=0, uint16_t certBuffFormat=SSL_FILETYPE_PEM, int intf=-1)
SSL mirror of the TCP::connect() call.
int connect(const IPADDR &ipAddress, uint16_t remotePort, uint32_t timeout)
Establish a TCP connection to a remote host.
Definition tcp.h:1547
Certificate Types and Use Cases
Decision Matrix
Need Public Access?
│
┌─────────┴─────────┐
YES NO
│ │
Have DNS name? Multiple devices?
│ │
┌─────┴─────┐ ┌─────┴─────┐
YES NO YES NO
│ │ │ │
v v v v
Use ACME Use Public Private Self-Signed
(Let's Encrypt) CA CA (Simplest)
1. Self-Signed Certificates
What It Is:** Device acts as its own Certificate Authority
Visual Flow:**
NetBurner Device
┌──────────────────────────┐
│ 1. Generate Key Pair │
│ 2. Create Certificate │
│ 3. Sign with Own Key │
│ 4. Use for Connections │
└──────────┬───────────────┘
│
v
Browser/Client
┌──────────────────────────┐
│ [!] Certificate Warning │
│ "Not Trusted" │
│ │
│ [Proceed Anyway] │
└──────────────────────────┘
When to Use:**
2. Private Certificate Authority (CA)
What It Is:** You create your own CA and sign device certificates
Visual Flow:**
Step 1: Create Your CA
┌─────────────────────┐
│ Computer with │
│ OpenSSL/CreateCerts │
│ │
│ 1. Create CA Key │
│ 2. Create CA Cert │
└─────────┬───────────┘
│
v
Step 2: Sign Device Certs
┌─────────────────────┐
│ For each device: │
│ 1. Generate Key │
│ 2. Create CSR │
│ 3. Sign with CA │
└─────────┬───────────┘
│
v
Step 3: Deploy
┌──────────────────────┬──────────────────────┐
│ Install CA Cert on │ Install Device Cert │
│ All Clients │ on Each Device │
└──────────────────────┴──────────────────────┘
When to Use:**
- [YES] Multiple devices in controlled environment
- [YES] Corporate/enterprise deployment
- [YES] Need certificate management
[YES] Air-gapped or private networks
Advantages:**
- [YES] One-time CA installation for all clients
- [YES] Central control over certificate issuance
- [YES] Can revoke compromised certificates
[YES] No external dependencies
Limitations:**
- [NO] More complex initial setup
- [NO] Must manually manage certificate expiration
[NO] CA certificate must be distributed to all clients
NetBurner Implementation:**
Use the provided scripts in \nburn\CreateCerts\\endiskip:
# Step 1: Create your CA (one time)
cd C:\nburn\CreateCerts\ECDSA
createCA.bat
# Step 2: Create device certificate
createDeviceCert.bat mydevice.example.com
# Step 3: Convert to C++ for NetBurner
compfile device.key comp_key comp_key_len key.cpp
compfile device.crt comp_cert comp_cert_len cert.cpp
extern const unsigned char comp_cert[];
extern const int comp_cert_len;
extern const unsigned char comp_key[];
extern const int comp_key_len;
void UserMain(void *pd) {
SSL_InstallCertificate(comp_cert, comp_cert_len,
comp_key, comp_key_len);
}
3. Public CA Certificates
What It Is:** Purchase certificates from trusted authorities (DigiCert, GoDaddy, etc.)
Visual Flow:**
Step 1: Generate CSR
┌──────────────────────┐
│ Device/Computer │
│ 1. Create Key │
│ 2. Generate CSR │
└──────────┬───────────┘
│
v
Step 2: Submit to CA
┌──────────────────────┐
│ Certificate Authority│
│ (DigiCert, etc.) │
│ │
│ 1. Verify Domain │
│ 2. Issue Certificate │
└──────────┬───────────┘
│
v
Step 3: Install
┌──────────────────────┐
│ NetBurner Device │
│ 1. Install Cert │
│ 2. Install Chain │
└──────────────────────┘
When to Use:**
- [YES] Public-facing production devices
- [YES] Need universal trust (no warnings)
- [YES] Customer/client access
[YES] Professional appearance required
Advantages:**
- [YES] No browser warnings
- [YES] Pre-trusted by all clients
- [YES] Professional appearance
[YES] Insurance/warranty often included
Limitations:**
- [NO] Annual costs ($50-$500+)
- [NO] Manual renewal process
- [NO] Requires public DNS name
[NO] Validation requirements
Process:**
- Generate Certificate Signing Request (CSR):
openssl genrsa -out device.key 2048
openssl req -new -key device.key -out device.csr
# Enter device domain name as Common Name
- Submit CSR to CA and complete verification
- Receive certificate and chain files
- Combine certificate with chain:
# Create complete chain file
cat device.crt intermediate.crt root.crt > device_chain.crt
- Convert and install on NetBurner (same as Private CA method)
4. ACME (Automated Certificate Management)
What It Is:** Automated certificate issuance from Let's Encrypt or BuyPass
Visual Flow:**
┌──────────────────────────────────────────────────────┐
│ NetBurner Device with ACME │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 1. Request Certificate from Let's Encrypt │ │
│ └─────────────────┬────────────────────────────┘ │
│ v │
│ ┌──────────────────────────────────────────────┐ │
│ │ 2. Let's Encrypt Verifies Domain Ownership │ │
│ │ (HTTP-01 Challenge) │ │
│ └─────────────────┬────────────────────────────┘ │
│ v │
│ ┌──────────────────────────────────────────────┐ │
│ │ 3. Certificate Issued & Installed │ │
│ └─────────────────┬────────────────────────────┘ │
│ v │
│ ┌──────────────────────────────────────────────┐ │
│ │ 4. Auto-Renewal Before Expiration (90 days) │ │
│ └──────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
│
v
Browser/Client
┌──────────────────────┐
│ [OK] Trusted Connect │
│ No Warnings │
└──────────────────────┘
When to Use:**
- [YES] Production devices with public DNS
- [YES] Need automatic renewal
- [YES] Want trusted certs without cost
[YES] Device accessible from internet
Requirements:**
- [YES] Public DNS name pointing to device
- [YES] Device reachable on port 80 (for validation)
[YES] NNDK 3.5.0 or later
Advantages:**
- [YES] Completely automatic
- [YES] Free trusted certificates
- [YES] Auto-renewal every 60 days
[YES] No manual certificate management
Limitations:**
- [NO] Requires public internet access
- [NO] Must have DNS name (no IP-only)
[NO] Shorter validity (90 days vs 1 year)
NetBurner Implementation:**
#include <acmeservlet.h>
void UserMain(void *pd) {
AcmeConfig config;
config.domain = "iot-device.example.com";
config.email = "admin@example.com";
config.acmeServer = ACME_LETSENCRYPT_PRODUCTION;
int result = StartAcmeHttps(config);
if (result == 0) {
printf("ACME HTTPS server started successfully\n");
printf("Certificate will auto-renew before expiration\n");
} else {
printf("ACME setup failed: %d\n", result);
}
}
void EnableSystemDiagnostics()
Turn on the diagnostic reports from the config page.
Use Case Decision Table
| Scenario | Recommended Method | Why |
| Development/Testing | Self-Signed | Fastest, simplest |
| Internal corporate network, 10+ devices | Private CA | Central management, one-time client setup |
| Public website/API | ACME or Public CA | Trusted by all browsers |
| Air-gapped system | Private CA or Self-Signed | No external dependencies |
| IoT fleet with DNS names | ACME | Automatic, scalable, free |
| Single device, few users | Self-Signed | Adequate for small scale |
| Requires IP address (no DNS) | Self-Signed or Private CA | ACME requires DNS |
| Critical infrastructure | Public CA | Professional support, insurance |
Implementation Methods
Method 1: Automatic Onboard Certificate Generation
Simplest approach - certificates generated automatically on first use.**
How It Works
First Boot / Certificate Needed
│
v
┌──────────────────────────────────┐
│ Check: Certificate exists? │
└──────┬────────────────────┬──────┘
NO YES
│ │
v v
┌──────────────────┐ ┌──────────────┐
│ Generate New │ │ Use Existing │
│ Self-Signed │ │ Certificate │
│ Certificate │ └──────────────┘
└──────────┬───────┘
│
v
┌──────────────────────────────────┐
│ Store in Non-Volatile Memory │
└──────────────────────────────────┘
Basic Implementation
#include <init.h>
const char *AppName = "Secure Device";
void UserMain(void *pd) {
printf("HTTPS Server: %s\n", AppName);
printf("Certificate auto-generated if needed\n");
while (1) {
}
}
Advanced: Auto-Regeneration
Enable automatic regeneration when certificates expire:
#define ENABLE_AUTOCERT_REGEN (1)
#define AUTO_CERT_GEN_CHECK (60 * 20 * TICKS_PER_SECOND)
- Warning
- Define both macros. predef.h sets AUTO_CERT_GEN_CHECK inside its own #ifdef ENABLE_AUTOCERT_REGEN block, which is evaluated before predef-overload.h is included on the last line of predef.h. Define ENABLE_AUTOCERT_REGEN alone and the build fails on an undefined AUTO_CERT_GEN_CHECK. Do not edit the SDK's predef.h to work around this. See On-board Cert Generation - Simple and Modifying System Files with Overload.
Customizing Certificate Generation
#include <ssl.h>
void CustomCertCallback(SslCertAttributes_t *attrs) {
attrs->country = "US";
attrs->state = "California";
attrs->locality = "San Diego";
attrs->organization = "My Company";
attrs->commonName = "mydevice.local";
attrs->altNames = "mydevice.local,192.168.1.100,#LOCAL";
}
void UserMain(void *pd) {
SSL_SetCertificateCallback(CustomCertCallback);
}
Method 2: ACME Automated Certificates
Production-ready trusted certificates with automatic renewal.**
Complete ACME Example
#include <init.h>
#include <acmeservlet.h>
#include <nbrtos.h>
const char *AppName = "ACME Secure Device";
void UserMain(void *pd) {
printf("Device IP: %s\n", GetIP().c_str());
printf("Waiting for DNS resolution...\n");
AcmeConfig config;
config.domain = "iot-device.example.com";
config.email = "admin@example.com";
config.acmeServer = ACME_LETSENCRYPT_PRODUCTION;
config.acceptTerms = true;
printf("Starting ACME certificate process for: %s\n", config.domain);
int result = StartAcmeHttps(config);
if (result == 0) {
printf("[OK] ACME HTTPS server running\n");
printf("Certificate valid for 90 days\n");
printf("Auto-renewal occurs at 60 days\n");
} else {
printf("[ERR] ACME failed with error: %d\n", result);
printf("Check: DNS resolution, firewall, port 80 access\n");
}
while (1) {
}
}
ACME Troubleshooting Checklist
Prerequisites Check:
[ ] Device has public IP address
[ ] DNS A record points to device IP
[ ] Port 80 open and accessible from internet
[ ] Port 443 open for final HTTPS
[ ] NTP
time synchronized (critical!)
[ ] Sufficient free memory (>50KB)
Common Issues:
[X] DNS not resolving -> Check DNS propagation (nslookup)
[X] Port 80 blocked -> Check firewall rules
[X] Challenge fails -> Verify HTTP-01 accessibility
[X] Time sync error -> Enable and wait for NTP
[X] Memory allocation fail -> Check heap usage
time_t time(time_t *pt)
Gets the current system GMT time.
Method 3: Manual Certificate Installation
For private CAs or purchased certificates.**
Step-by-Step Process
Step 1: Generate Certificate Files**
Using NetBurner CreateCerts scripts:
# Navigate to CreateCerts directory
cd C:\nburn\CreateCerts\ECDSA
# Create CA (one time, if needed)
createCA.bat
# Create device certificate
createDeviceCert.bat mydevice.example.com
# Output files:
# - device.key (private key - KEEP SECRET!)
# - device.crt (certificate)
# - ca.crt (CA certificate)
Step 2: Convert to C++ Code**
# Convert key
compfile device.key comp_key comp_key_len key.cpp
# Convert certificate
compfile device.crt comp_cert comp_cert_len cert.cpp
# Optional: Convert CA for chain
compfile ca.crt comp_ca comp_ca_len ca.cpp
Step 3: Add to Project**
YourProject/
├── src/
│ ├── main.cpp
│ ├── key.cpp <- Add these
│ ├── cert.cpp <- Add these
│ └── ca.cpp <- Optional
└── makefile
Update makefile if using command line builds:
CPP_SRC += \
src/main.cpp \
src/key.cpp \
src/cert.cpp
Step 4: Install in Code**
#include <init.h>
#include <ssl.h>
extern const unsigned char comp_cert[];
extern const int comp_cert_len;
extern const unsigned char comp_key[];
extern const int comp_key_len;
void UserMain(void *pd)
{
int result = SSL_InstallCertificate(
comp_cert, comp_cert_len,
comp_key, comp_key_len
);
if (result == 0) {
printf("[OK] Certificate installed successfully\n");
} else {
printf("[ERR] Certificate installation failed: %d\n", result);
}
while (1) {
}
}
Installing Certificate Chain
For trusted certificates, install the full chain:
extern const unsigned char comp_device_cert[];
extern const int comp_device_cert_len;
extern const unsigned char comp_chain[];
extern const int comp_chain_len;
extern const unsigned char comp_key[];
extern const int comp_key_len;
void InstallWithChain() {
SSL_InstallCertificate(comp_chain, comp_chain_len,
comp_key, comp_key_len);
SSL_InstallCertificate(comp_device_cert, comp_device_cert_len,
comp_key, comp_key_len);
SSL_AddIntermediateCert(comp_chain, comp_chain_len);
}
Method 4: SSL Client Implementation
Connecting to secure servers with certificate validation.**
Basic SSL Client
#include <ssl.h>
#include <nettcp.h>
extern const char trustedCA[];
extern const int trustedCA_len;
void SecureClientConnection()
{
int fd =
connect(
"api.example.com", 443);
if (fd > 0) {
if (ssl) {
printf("[OK] Secure connection established\n");
const char* request = "GET / HTTP/1.1\r\n"
"Host: api.example.com\r\n"
"\r\n";
SSL_write(ssl, request, strlen(request));
char buffer[1024];
int n = SSL_read(ssl, buffer, sizeof(buffer)-1);
if (n > 0) {
buffer[n] = '\0';
printf("Response: %s\n", buffer);
}
SSL_close(ssl);
} else {
printf("[ERR] SSL handshake failed\n");
}
}
}
int close(int fd)
Close the specified file descriptor and free the associated resources.
Advanced: Certificate Verification
#include <ssl.h>
int VerifyCertCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store) {
if (!preverify_ok) {
int err = X509_STORE_CTX_get_error(store);
int depth = X509_STORE_CTX_get_error_depth(store);
printf("Certificate verification failed at depth %d\n", depth);
printf("Error: %s\n", X509_verify_cert_error_string(err));
return 0;
}
return 1;
}
void SetupSecureClient() {
SSL_SetVerifyCallback(VerifyCertCallback);
SSL_SetVerifyMode(SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
}
Loading CAs from Storage
#include <effs_fat.h>
#include <ssl.h>
void LoadCAsFromSD() {
F_FILE* f =
f_open(
"ca-bundle.crt",
"r");
if (f) {
long size = f_tell(f);
char* certs = new char[size + 1];
certs[size] = '\0';
delete[] certs;
printf("[OK] Loaded CA certificates from SD\n");
}
}
#define f_read(buf, size, size_st, filehandle)
Read data from the current position in a file.
Definition api_f.h:526
#define f_seek(filehandle, offset, whence)
Move the stream position of an open file.
Definition api_f.h:550
#define f_close(filehandle)
Closes an opened file.
Definition api_f.h:487
#define f_open(filename, mode)
Opens a file in the file system.
Definition api_f.h:512
Troubleshooting
Common Browser Warning Messages
1. "Your Connection is Not Private"
┌────────────────────────────────────────┐
│ [!] Your connection is not private │
│ │
│ Attackers might be trying to steal │
│ your information... │
│ │
│ NET::ERR_CERT_AUTHORITY_INVALID │
└────────────────────────────────────────┘
Cause:** Certificate not signed by trusted CA
Solutions:**
- [OK] Use ACME for automatic trusted certificates
- [OK] Install your CA certificate on client devices
- [OK] Purchase certificate from public CA
- [!] For testing only: Click "Advanced" -> "Proceed (unsafe)"
2. "Certificate Name Mismatch"
┌────────────────────────────────────────┐
│ [!] Certificate doesn't match │
│ │
│ Certificate: device.example.com │
│ Accessing: 192.168.1.100 │
│ │
│ NET::ERR_CERT_COMMON_NAME_INVALID │
└────────────────────────────────────────┘
Cause:** Certificate issued for different name/IP
Solutions:**
- [OK] Access device using correct hostname
- [OK] Add Subject Alternative Names (SANs) to certificate
- [OK] Use wildcard certificate (*.example.com)
- [OK] Generate new certificate with correct CN/SAN
Example with multiple names:
void CertCallback(SslCertAttributes_t *attrs) {
attrs->commonName = "device.example.com";
attrs->altNames = "device.example.com,"
"device.local,"
"192.168.1.100,"
"10.0.0.50";
}
3. "Certificate Expired"
┌────────────────────────────────────────┐
│ [!] This certificate has expired │
│ │
│ Valid Until: Jan 15, 2025 │
│ Today: Feb 20, 2025 │
│ │
│ NET::ERR_CERT_DATE_INVALID │
└────────────────────────────────────────┘
Cause:** Certificate past expiration date
Solutions:**
- [OK] Use ACME for automatic renewal
- [OK] Enable auto-regeneration for self-signed certs
- [OK] Set calendar reminders for manual renewal
- [OK] Check device clock is synchronized (NTP)
4. "Certificate Not Yet Valid"
Cause:** Device clock incorrect or certificate future-dated
Solutions:**
#include <ntpclient.h>
void UserMain(void *pd) {
SetNTPServer("pool.ntp.org");
EnableNTP();
while (!NTPTimeSet()) {
}
printf("[OK] Time synchronized\n");
}
SSL Handshake Failures
Debugging Connection Issues
#include <ssl.h>
void DebugSSLConnection(const char* host, int port) {
printf("\n=== SSL Debug Info ===\n");
printf("Testing TCP connection to %s:%d...\n", host, port);
if (fd < 0) {
printf("[ERR] TCP connection failed\n");
return;
}
printf("[OK] TCP connected\n");
wolfSSL_Debugging_ON();
printf("Attempting SSL handshake...\n");
if (ssl) {
printf("[OK] SSL handshake successful\n");
WOLFSSL_CIPHER* cipher = SSL_get_current_cipher(ssl);
printf("Cipher: %s\n", SSL_CIPHER_get_name(cipher));
printf("Protocol: %s\n", SSL_get_version(ssl));
SSL_close(ssl);
} else {
printf("[ERR] SSL handshake failed\n");
int err = SSL_get_error(ssl, -1);
printf("Error code: %d\n", err);
}
wolfSSL_Debugging_OFF();
}
Decrypting a Packet Capture (TLS Key Logging)
Every TLS 1.3 handshake after the ServerHello is encrypted, including the Certificate, CertificateRequest and CertificateVerify messages, and including alerts. A capture of a failing TLS connection therefore shows a ClientHello, an opaque blob, and a connection that dies. The session secrets are what make the capture readable.
Warning: for testing only.** Key logging works in release builds as well as debug builds. Anyone who can read the console can decrypt every TLS session the device makes. Never ship a product with key logging enabled.
To turn it on for one project, define NB_TLS_KEYLOG in the project copy of user_settings-overload.h:
Clean the build once after you add the file, so that the crypto library is rebuilt. See "Changing wolfSSL Settings for One Project" below. To turn it on for every project instead, uncomment // #define NB_TLS_KEYLOG in the user_settings.h file for your platform, under libraries/include/crypto/platform/.
No application code is needed. The library registers the callbacks when each TLS socket is created, so client sockets, the HTTPS server and mail are all covered, on TLS 1.2 and TLS 1.3 alike. Each session secret is printed to the console as one NSS key log line:
CLIENT_HANDSHAKE_TRAFFIC_SECRET <client random> <secret>
SERVER_HANDSHAKE_TRAFFIC_SECRET <client random> <secret>
CLIENT_TRAFFIC_SECRET_0 <client random> <secret>
SERVER_TRAFFIC_SECRET_0 <client random> <secret>
Collect the lines, save them to a file, and point Wireshark at it: Preferences, Protocols, TLS, "(Pre)-Master-Secret log filename". From the command line:
tshark -r tls.pcap -o "tls.keylog_file:/abs/path/keys.log" -Y "tls.handshake.type"
Two functions support this, both declared in crypto/ssl.h:
- SSL_KeyLogEnabled() reports whether the library was built with key logging. An application cannot test the define itself, because application objects are not compiled with WOLFSSL_USER_SETTINGS.
- SSL_KeyLogWrite() writes one line and is weak, so an application can send the lines to a TCP socket or flash instead of the console. Pass the line through unchanged.
Do not define SHOW_SECRETS or WOLFSSL_SSLKEYLOGFILE. Those are wolfSSL options that write to a file, and NetBurner platforms set NO_FILESYSTEM. A build with WOLFSSL_SSLKEYLOGFILE stops with an error that names NB_TLS_KEYLOG.
The board caches TLS sessions, so a second connection to the same server resumes and its handshake carries no Certificate message. Reboot between captures when you need a full handshake.
See TLS Key Logging for the whole capture and decrypt procedure.
Changing wolfSSL Settings for One Project
The wolfSSL settings for each platform are in libraries/include/crypto/platform/<PLATFORM>/user_settings.h. Editing that file changes every project. To change a setting for one project only, create this file in the project:
<project root>/overload/libraries/include/crypto/platform/user_settings-overload.h
Put in only the lines you want to change. Every platform user_settings.h includes user_settings-overload.h after all of its own settings, so your lines win. The SDK ships the file empty.
| To | Write |
| Turn on an option that is off | #define WOLFSSL_SP_4096 |
| Turn off an option that is on | #undef HAVE_AESCCM |
| Change a value | #undef WOLFSSL_TLS_SRAM_KB, then #define WOLFSSL_TLS_SRAM_KB 64 |
| Select a switch | #define CRYPTO_PROFILE_AESGCM |
A switch is a NetBurner macro that changes several wolfSSL settings at once. Every switch takes effect in user_settings_switches.h, which user_settings.h includes after your file, so a switch works from your file too:
| Switch | Effect |
| CRYPTO_PROFILE_AESGCM | TLS 1.3 only. Turns off TLS 1.2, DH, DES3, ChaCha20-Poly1305, Curve25519 and Ed25519. |
| CRYPTO_PROFILE_CHACHA | TLS 1.3 only. Turns off TLS 1.2, DH and DES3. |
| CRYPTO_PROFILE_MINIMAL | Turns off the same options as CRYPTO_PROFILE_AESGCM. |
| NB_TLS_KEYLOG | Prints TLS session secrets to the console. For testing only. |
| SSL_CUSTOM_MALLOC | Selects the memory region for the TLS allocator. Defined in predef.h. |
| ENABLE_ECC384, ENABLE_ECC521 | Turn the P-384 and P-521 curves on or off. Defined in predef.h. |
Define at most one crypto profile. The build stops with an error if you define more. A profile turns off its options even if your file defines one of them again.
SSL_TLS_1_3_SUPPORT also changes wolfSSL settings, but system code outside the crypto library reads it too. Change it in predef-overload.h, not in user_settings-overload.h. See Modifying System Files with Overload.
The first time you add the file, clean the build so that the crypto library is rebuilt. On the command line, run make clean, then make. In NBEclipse, select "Clean NetBurner System
Library" under the project's "Build Targets", then rebuild. After that, edits to the file rebuild the right objects. NBEclipse also needs the project folder overload/libraries/include in both the C and the C++ compiler include paths, above the $(NNDK_ROOT)/ entries. The TLS Key Logging example ships the file, and an nbeclipse-rules.json that adds the include paths on import.
Common Error Codes
| Error | Meaning | Solution |
| -188 | ASN parse error | Certificate format invalid |
| -173 | Bad certificate | Corrupted or wrong cert format |
| -155 | No CA match | Add appropriate CA to trust list |
| -313 | Cipher suite mismatch | Update wolfSSL or adjust cipher list |
| -101 | Connection reset | Network issue or server rejected |
Certificate Verification Issues
Verification Flowchart
SSL Handshake Started
│
v
┌────────────────────┐
│ Verify Certificate │
└────────┬───────────┘
v
┌─────────┐
│ Trusted │ YES ┌─────────────────┐
│ CA? ├──────>│ Check Validity │
└────┬────┘ └────────┬────────┘
│ NO │
v v
┌────────┐ ┌──────────┐
│ Reject │ │ Check CN │
└────────┘ └─────┬────┘
v
┌─────────┐
│ Matches │ YES ┌─────────┐
│ Host? ├──────>│ Success │
└────┬────┘ └─────────┘
│ NO
v
┌────────┐
│ Reject │
└────────┘
Bypass Verification (Development Only!)
void InsecureClientSetup() {
SSL_SetVerifyMode(SSL_VERIFY_NONE);
printf("[!] Certificate verification DISABLED\n");
printf("[!] Use only for development/testing\n");
}
void SecureClientSetup() {
SSL_SetVerifyMode(SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
}
Memory and Resource Issues
Memory Requirements
Typical SSL Memory Usage:
| Component | Size |
| SSL Context | ~8-12 KB |
| Certificate Chain | 2-8 KB |
| Per Connection | 40-60 KB |
| Crypto Operations | 10-20 KB |
Minimum Recommended: 128 KB free heap Comfortable: 256 KB+ free heap
Checking Memory
#include <system.h>
void CheckMemoryForSSL() {
uint32_t freeHeap = FreeRTOSMemRemaining();
uint32_t minFree = FreeRTOSMemMinEver();
printf("\n=== Memory Status ===\n");
printf("Current Free: %lu KB\n", freeHeap / 1024);
printf("Minimum Ever: %lu KB\n", minFree / 1024);
if (freeHeap < 128 * 1024) {
printf("[!] WARNING: Low memory for SSL\n");
printf(" Consider: Reducing buffer sizes\n");
printf(" Limiting connections\n");
printf(" Using lighter ciphers\n");
} else {
printf("[OK] Sufficient memory for SSL\n");
}
}
Performance Optimization
Cipher Suite Selection
#include <ssl.h>
const char* fastCiphers =
"ECDHE-ECDSA-AES128-GCM-SHA256:"
"ECDHE-RSA-AES128-GCM-SHA256";
const char* balancedCiphers =
"ECDHE-ECDSA-AES256-GCM-SHA384:"
"ECDHE-ECDSA-AES128-GCM-SHA256:"
"ECDHE-RSA-AES256-GCM-SHA384";
const char* secureCiphers =
"ECDHE-ECDSA-CHACHA20-POLY1305:"
"ECDHE-ECDSA-AES256-GCM-SHA384";
void ConfigureCiphers() {
SSL_SetCipherList(balancedCiphers);
printf("Cipher list configured\n");
}
Connection Reuse
WOLFSSL_CTX* ctx = nullptr;
void InitSSLContext() {
if (!ctx) {
ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method());
}
}
WOLFSSL* CreateConnection(const char* host, int port) {
InitSSLContext();
if (fd < 0) return nullptr;
WOLFSSL* ssl = wolfSSL_new(ctx);
wolfSSL_set_fd(ssl, fd);
if (wolfSSL_connect(ssl) != SSL_SUCCESS) {
wolfSSL_free(ssl);
return nullptr;
}
return ssl;
}
Best Practices
Security Principles
1. Private Key Protection
┌─────────────────────────────────────────┐
│ Private Key Security Hierarchy │
├─────────────────────────────────────────┤
│ │
│ Most Secure: │
│ ├─ Hardware Security Module (HSM) │
│ ├─ Secure Enclave / TPM │
│ └─ Encrypted Storage │
│ │
│ Moderate: │
│ ├─ Compiled into firmware │
│ ├─ Flash storage with permissions │
│ └─ Protected file system │
│ │
│ Least Secure: │
│ ├─ Plain text on SD card │
│ ├─ Unprotected flash │
│ └─ Debug accessible memory │
│ │
└─────────────────────────────────────────┘
Best Practices:**
- [YES] Never store private keys in version control
- [YES] Use different keys for dev/test/production
- [YES] Rotate keys periodically
- [YES] Monitor for unauthorized access
- [NO] Don't log private keys
- [NO] Don't transmit private keys unencrypted
- [NO] Don't reuse keys across devices (except CA)
2. Certificate Validation
SSL_SetVerifyMode(SSL_VERIFY_NONE);
SSL_SetVerifyMode(SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
int VerifyCallback(int preverify, WOLFSSL_X509_STORE_CTX* store) {
return preverify && CustomSecurityChecks(store);
}
SSL_SetVerifyCallback(VerifyCallback);
3. Protocol and Cipher Configuration
SSL_SetMinVersion(SSL_3_0);
SSL_SetMinVersion(TLS_1_2);
SSL_SetMinVersion(TLS_1_3);
const char* strongCiphers =
"TLS_AES_256_GCM_SHA384:"
"TLS_CHACHA20_POLY1305_SHA256:"
"ECDHE-ECDSA-AES256-GCM-SHA384:"
"ECDHE-RSA-AES256-GCM-SHA384";
SSL_SetCipherList(strongCiphers);
Production Checklist
Pre-Deployment
[ ] Security Review:
[ ] All private keys properly protected
[ ] Certificate chain complete and valid
[ ] Strong cipher suites configured
[ ] Old protocols disabled (SSL, TLS 1.0/1.1)
[ ] Certificate validation enabled for clients
[ ] Certificate Management:
[ ] Expiration dates documented
[ ] Renewal process established
[ ] Contact information current
[ ] Backup certificates available
[ ] Monitoring Setup:
[ ] Certificate expiration alerts
[ ] Connection failure logging
[ ] Performance metrics
[ ] Security event logging
[ ] Testing Complete:
[ ] Browser compatibility verified
[ ] Client application tested
[ ] Load testing performed
[ ] Failover scenarios tested
[ ] Documentation:
[ ] Certificate inventory maintained
[ ] Configuration documented
[ ] Recovery procedures written
[ ] Contact list updated
Post-Deployment Monitoring
#include <ssl.h>
void MonitorSSLStatus() {
uint32_t activeConnections = SSL_GetActiveConnections();
uint32_t failedHandshakes = SSL_GetFailedHandshakes();
uint32_t certificateErrors = SSL_GetCertErrors();
printf("\n=== SSL Status ===\n");
printf("Active Connections: %lu\n", activeConnections);
printf("Failed Handshakes: %lu\n", failedHandshakes);
printf("Certificate Errors: %lu\n", certificateErrors);
uint32_t daysUntilExpiry = SSL_GetCertDaysRemaining();
if (daysUntilExpiry < 30) {
printf("[!] Certificate expires in %lu days!\n", daysUntilExpiry);
}
if (failedHandshakes > 100) {
printf("[!] High handshake failure rate!\n");
}
}
Development Workflow
Recommended Development Cycle
┌─────────────────────────────────────────┐
│ Development Stages │
├─────────────────────────────────────────┤
│ │
│ 1. Development (Local Network) │
│ └─> Self-Signed Certificates │
│ - Fast iteration │
│ - No external dependencies │
│ │
│ 2. Integration Testing │
│ └─> Private CA Certificates │
│ - Test certificate chains │
│ - Verify validation logic │
│ │
│ 3. Staging (Pre-Production) │
│ └─> ACME Staging Certs │
│ - Test automation │
│ - Verify renewal process │
│ │
│ 4. Production │
│ └─> ACME Production or Public CA │
│ - Trusted by all clients │
│ - Automatic renewal │
│ │
└─────────────────────────────────────────┘
Configuration Management
#ifdef DEVELOPMENT
#define SSL_MODE SSL_SELF_SIGNED
#define VERIFY_MODE SSL_VERIFY_NONE
#define DEBUG_SSL 1
#elif defined(STAGING)
#define SSL_MODE SSL_ACME_STAGING
#define VERIFY_MODE SSL_VERIFY_PEER
#define DEBUG_SSL 1
#elif defined(PRODUCTION)
#define SSL_MODE SSL_ACME_PRODUCTION
#define VERIFY_MODE SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT
#define DEBUG_SSL 0
#define ENABLE_SSL_MONITORING 1
#endif
void ConfigureSSL() {
#if SSL_MODE == SSL_SELF_SIGNED
#elif SSL_MODE == SSL_ACME_STAGING
AcmeConfig config;
config.acmeServer = ACME_LETSENCRYPT_STAGING;
StartAcmeHttps(config);
#elif SSL_MODE == SSL_ACME_PRODUCTION
AcmeConfig config;
config.acmeServer = ACME_LETSENCRYPT_PRODUCTION;
StartAcmeHttps(config);
#endif
SSL_SetVerifyMode(VERIFY_MODE);
#if DEBUG_SSL
wolfSSL_Debugging_ON();
#endif
}
Common Pitfalls to Avoid
1. Time Synchronization
void UserMain(void *pd) {
EnableNTP();
}
void UserMain(void *pd) {
EnableNTP();
SetNTPServer("pool.ntp.org");
uint32_t timeout = 0;
while (!NTPTimeSet() && timeout++ < 30) {
}
if (NTPTimeSet()) {
printf("[OK] Time synchronized\n");
} else {
printf("[!] Time sync failed, SSL may have issues\n");
}
}
2. Certificate Chain Issues
SSL_InstallCertificate(device_cert, device_cert_len,
device_key, device_key_len);
SSL_InstallCertificate(full_chain, full_chain_len,
device_key, device_key_len);
3. Resource Leaks
void MakeRequest() {
SSL_write(ssl, data, len);
}
void MakeRequest() {
if (fd < 0) return;
if (ssl) {
SSL_write(ssl, data, len);
SSL_close(ssl);
}
}
4. Hardcoded Credentials
#define CA_KEY "-----BEGIN PRIVATE KEY-----\nMIIE..."
#define DEVICE_PASSWORD "admin123"
extern const char comp_key[];
extern const int comp_key_len;
void LoadCredentials() {
ReadSecureKey(keyBuffer, sizeof(keyBuffer));
}
Additional Resources
NetBurner Examples
Located in your NNDK installation:
C:\nburn\examples\SSL\
+-- acmeservlet\ <- ACME automation
+-- SslOnboardCertGeneration\ <- Auto-generated certs
| +-- Simple\
| +-- Advanced\
| +-- CompiledCa\
+-- SslClientVerifyPeerBasic\ <- Client validation
+-- SslClientVerifyPeerEffs\ <- Loading CAs from storage
+-- CreateCerts\ <- Certificate generation scripts
+-- ECDSA\
+-- RSA\
Documentation and References
Official Documentation
Educational Resources
Books:**
Wikipedia Articles
Tools and Utilities
Certificate Management
- OpenSSL: Included in NNDK at \Nburn\gcc\msys\1.0\bin\openssl.exe
- NetBurner CreateCerts: Scripts in \nburn\CreateCerts\\endiskip
- compfile: NetBurner utility to convert files to C++ arrays
Testing and Debugging
# Test SSL/TLS connection
openssl s_client -connect device.example.com:443 -showcerts
# Check certificate details
openssl x509 -in device.crt -text -noout
# Verify private key
openssl rsa -in device.key -check
# Test certificate chain
openssl verify -CAfile ca.crt device.crt
Online Testing
Quick Reference
Essential SSL Functions
StartAcmeHttps(config);
SSL_InstallCertificate(cert, key);
SSL_SetVerifyMode(mode);
SSL_GetCertDaysRemaining();
SSL_SetCertificateCallback(fn);
SSL_write(ssl, data, len);
SSL_read(ssl, buffer, size);
SSL_close(ssl);
wolfSSL_Debugging_ON();
SSL_get_error(ssl, ret);
CertGenReturnCode SSL_CreateNewSelfSignedCert(CertGenData &pGenData)
Generates a new self-signed certificate that will be stored on the device.
Configuration Constants
SSL_VERIFY_NONE
SSL_VERIFY_PEER
SSL_VERIFY_FAIL_IF_NO_PEER_CERT
SSL_3_0
TLS_1_0
TLS_1_1
TLS_1_2
TLS_1_3
ACME_LETSENCRYPT_STAGING
ACME_LETSENCRYPT_PRODUCTION
ACME_BUYPASS_PRODUCTION
SSL_FILETYPE_PEM
SSL_FILETYPE_DER
Recommended Approach for Most Users:**
- Development: Use auto-generated self-signed certificates
- Testing: Set up private CA for realistic testing
Production: Deploy ACME if possible, otherwise use public CA
Key Takeaways:**
- [LOCK] Private keys are your most sensitive security asset
- [SYNC] ACME automation eliminates manual certificate management
- [TIME] Time synchronization is critical for SSL/TLS
- [LINK] Complete certificate chains prevent trust issues
- [STAT] Monitor certificate expiration and connection health