SETTING UP CERTIFICATES FOR A TIBBO DEVICE ACTING AS A TLS SERVER
Applies to: Tibbo G3 devices (e.g. TPP2W(G3))
Sample project: SSL_MULTI_SERVER
OVERVIEW
When the Tibbo device is the TLS server, a client (a PC, a phone, another device) connects to it and checks the certificate the device presents. For that to work, two things are needed:
- The device must hold its own certificate AND the matching private key. These are stored together in one file, the server bundle, which is added to the project and loaded with sock.tlsinit().
- The client must trust the device's certificate. With a self-signed certificate, this means installing the certificate on the client (for example, in the Windows certificate store).
This article shows how to create the certificate and bundle with OpenSSL, add them to the SSL_MULTI_SERVER sample, install the certificate on Windows, and test the connection.
The steps are given for both ECDSA P-256 and RSA 2048 keys; we suggest using ECDSA P-256 for higher security and connection speed versus RSA 2048.
- ECDSA P-256 : Smaller and faster. The socket buffers in the sample (6 pages RX and TX) are sized for this key type.
- RSA 2048 : Widest compatibility with older clients. Needs larger buffers: set TLS_RX_BUFF and TLS_TX_BUFF to 8 pages.
WHAT YOU NEED
- OpenSSL 3.x on your PC.
- TIDE with the SSL_MULTI_SERVER sample project.
- The IP address the device will use. The examples use 192.168.1.218; replace it with your own address.
- Optional, for testing: Python 3.
All commands below are written on a single line so they work the same in Command Prompt, PowerShell, and Git Bash.
IMPORTANT: THE CERTIFICATE IS TIED TO THE DEVICE'S IP ADDRESS
Clients check that the address they connected to is listed in the certificate's Subject Alternative Name (SAN). If the device's IP address changes, clients will reject the certificate, and you must create a new one. If the device will be reached by more than one address, list them all (see "Multiple addresses" at the end of Part 1).
PART 1 - CREATE THE CERTIFICATE AND KEY
Option A: ECDSA P-256
Step 1. Create a configuration file named device_ec.cnf with this content:
[req]
prompt = no
distinguished_name = dn
x509_extensions = v3_req
[dn]
CN = 192.168.1.218
[v3_req]
basicConstraints = critical,CA:TRUE
subjectAltName = @alt_names
[alt_names]
IP.1 = 192.168.1.218
Step 2. Generate the private key:
openssl ecparam -name prime256v1 -genkey -noout -out device_ec.key
Step 3. Create the self-signed certificate, valid for 10 years:
openssl req -new -x509 -key device_ec.key -out device_ec.crt -days 3650 -config device_ec.cnf
Step 4. Convert the certificate and the key to DER (binary) format:
openssl x509 -in device_ec.crt -outform DER -out device_ec_cert.der
openssl ec -in device_ec.key -outform DER -out device_ec_key.der
Option B: RSA 2048
Step 1. Create a configuration file named device_rsa.cnf with this content:
[req]
default_bits = 2048
prompt = no
distinguished_name = dn
x509_extensions = v3_req
[dn]
CN = 192.168.1.218
[v3_req]
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
IP.1 = 192.168.1.218
Step 2. Generate the private key:
openssl genrsa -out device_rsa.key 2048
Step 3. Create the self-signed certificate, valid for 10 years:
openssl req -new -x509 -key device_rsa.key -out device_rsa.crt -days 3650 -config device_rsa.cnf
Step 4. Convert the certificate and the key to DER (binary) format. The RSA key is converted to PKCS#8:
openssl x509 -in device_rsa.crt -outform DER -out device_rsa_cert.der
openssl pkcs8 -topk8 -nocrypt -in device_rsa.key -outform DER -out device_rsa_key.der
Check the certificate and confirm the SAN contains the device's address (use device_rsa.crt for RSA):
openssl x509 -in device_ec.crt -noout -ext subjectAltName
Expected output:
X509v3 Subject Alternative Name: IP Address:192.168.1.218
Multiple addresses
To use one certificate for several addresses, add more lines under [alt_names] before running Step 3:
[alt_names]
IP.1 = 192.168.1.218
IP.2 = 10.0.0.218
PART 2 - BUILD THE SERVER BUNDLE
The device needs the certificate and the private key in a single file:
The certificate (DER) followed directly by the private key (DER). The certificate must come first.
In Command Prompt (the /b switch is required; it copies the files as binary):
copy /b device_ec_cert.der + device_ec_key.der server_bundle.der
In PowerShell, run the same command through Command Prompt:
cmd /c copy /b device_ec_cert.der + device_ec_key.der server_bundle.der
In Git Bash, Linux, or macOS:
cat device_ec_cert.der device_ec_key.der > server_bundle.der
For RSA, use device_rsa_cert.der and device_rsa_key.der instead.
Check the result: the size of server_bundle.der must equal the sum of the two input files. As a guide, an ECDSA bundle is roughly 490 bytes and an RSA bundle roughly 2 KB.
You can also confirm that the bundle starts with the right certificate:
openssl x509 -inform DER -in server_bundle.der -noout -subject -ext subjectAltNameserver_bundle.der contains the private key and is compiled into the firmware. Do not publish the bundle, the .key files, or the *_key.der files. The .crt file holds no secrets and is the one you give to clients.
PART 3 - ADD THE BUNDLE TO THE SSL_MULTI_SERVER PROJECT
Step 1. Copy server_bundle.der into the SSL_MULTI_SERVER project folder, replacing the existing file.
Step 2. In TIDE, make sure server_bundle.der is part of the project (add it as an existing file if it is not listed). The file name must match the CERT_BUNDLE defined in global.tbh:
#define CERT_BUNDLE "server_bundle.der"
Step 3. Check the other settings in global.tbh:
const DEVICE_IP = "192.168.1.218" must match the IP in the certificate
const DEVICE_MASK = "255.255.255.0"
const DEVICE_GW = "192.168.1.1"
const NUM_TLS_SOCKS = 6 number of simultaneous clients
#define TLS_LISTEN_PORT "8443"
#define TLS_RX_BUFF 6 use 8 for RSA 2048
#define TLS_TX_BUFF 6 use 8 for RSA 2048Keep NUM_TLS_SOCKS at 6 or below for ECDSA P256 and 10 or below for RSA2048.
Step 4. Build the project and upload it to the device.
At boot, the device prints the number of free buffer pages and "SSL_MULTI_SERVER ready. Listening on port 8443 (6 sockets)".
How the sample uses the bundle: when a client connects (PL_SST_EST_POPENED), the socket opens the bundle and starts the handshake:
romfile.open(CERT_BUNDLE)
tls_res = sock.tlsinit(romfile.offset)
tls_res = sock.tlshandshake("")An empty string in tlshandshake() makes the socket act as the TLS server. When the handshake completes (PL_SST_EST_TLS), the device sends a welcome line and then echoes back anything it receives.
PART 4 - TRUST THE CERTIFICATE ON WINDOWS
Because the certificate is self-signed, Windows will not trust it until you install it as a trusted root. Install the .crt file (device_ec.crt or device_rsa.crt), never the key or the bundle. Use any one of these methods.
Method A - Double-click
- Double-click device_ec.crt (or device_rsa.crt).
- Click "Install Certificate...".
- Select "Local Machine" and click Next.
- Select "Place all certificates in the following store" and click Browse.
- Select "Trusted Root Certification Authorities" and click OK.
- Click Next, then Finish, and confirm the security prompt.
- Restart your browser.
Method B - PowerShell (run as Administrator)
Import-Certificate -FilePath .\device_ec.crt -CertStoreLocation Cert:\LocalMachine\Root
Method C - Command Prompt (run as Administrator)
certutil -addstore Root device_ec.crtTo remove the certificate later, open "Manage computer certificates" (certlm.msc), go to Trusted Root Certification Authorities > Certificates, and delete the entry named 192.168.1.218.
Note: Firefox may keep its own certificate list. If Firefox still warns, import the .crt file under Settings > Privacy & Security > Certificates > View Certificates > Authorities.
PART 5 - TEST THE CONNECTION
Test 1 - One connection with OpenSSL
openssl s_client -connect 192.168.1.218:8443 -CAfile device_ec.crt -verify_ip 192.168.1.218Look for "Verify return code: 0 (ok)". The device's welcome line appears after the handshake. Type some text and press Enter; the device answers with "ECHO: " followed by your text. Press Ctrl+C to close.
In the device's debug output, you should see the connection, the handshake, and the active count, for example:
sock[0] TCP from 192.168.1.70:52011
sock[0] TLS handshake started...
sock[0] TLS established. active: 1/6
Test 2 - Several connections at once with Python
Save the following as tls_multi_client.py:
# Opens several verified TLS connections to the device and echoes a line on each.
# Edit the settings below, or pass them on the command line:
# python tls_multi_client.py [device_ip] [port] [device_certificate.crt] [count]
import os, socket, ssl, sys, threading, time
IP = "192.168.1.218" # device address (must match the certificate's SAN)
PORT = 8443 # TLS_LISTEN_PORT in the sample
CA = "device_ec.crt" # the device's certificate (device_rsa.crt for RSA)
COUNT = 6 # number of simultaneous connections
args = sys.argv[1:]
if len(args) > 0: IP = args[0]
if len(args) > 1: PORT = int(args[1])
if len(args) > 2: CA = args[2]
if len(args) > 3: COUNT = int(args[3])
# Certificate paths are relative to this script's folder, wherever it is run from
HERE = os.path.dirname(os.path.abspath(__file__))
CA = os.path.join(HERE, CA)
ctx = ssl.create_default_context(cafile=CA) # verifies the device certificate and its IP
def run(i):
try:
with socket.create_connection((IP, PORT), timeout=30) as raw:
with ctx.wrap_socket(raw, server_hostname=IP) as s:
print(f"[{i}] connected: {s.version()} {s.cipher()[0]}")
print(f"[{i}] {s.recv(256).decode(errors='replace').strip()}")
s.sendall(f"hello from client {i}\r\n".encode())
print(f"[{i}] {s.recv(256).decode(errors='replace').strip()}")
time.sleep(5)
except Exception as e:
print(f"[{i}] FAILED: {e}")
threads = [threading.Thread(target=run, args=(i,)) for i in range(COUNT)]
for t in threads:
t.start()
time.sleep(1)
for t in threads:
t.join()
Run it:
python tls_multi_client.py 192.168.1.218 8443 device_ec.crt 6Each client should report "connected: TLSv1.3 ...", the welcome line, and its echo. The device's active count should climb to 6/6 and fall back as the clients close.
The script starts one client per second. The TLS handshake takes a noticeable amount of processing time on the device, so starting many clients at exactly the same moment can cause some of them to time out.
TROUBLESHOOTING
"Connection refused"
Nothing is listening on that port. Check that the port number in the client matches TLS_LISTEN_PORT and that the new firmware is running.
"tlsinit FAILED" in the device's debug output
The bundle could not be loaded. Check that server_bundle.der is in the project, that its name matches CERT_BUNDLE, and that it was built with the certificate first and the key second (Part 2).
"TLS handshake FAILED - resetting", or the handshake never completes
Most often, the buffers are too small for the key type. Use 8 pages for RSA 2048.
Client reports a certificate verification error or an IP address mismatch
The certificate is not installed on the client (Part 4), or the address the client connects to is not in the certificate's SAN (Part 1).
Some clients time out when many connect at once
Stagger the connections, and keep NUM_TLS_SOCKS at 6 or below. Also, allowing 0.5 seconds between connections helps.
Comments
0 comments
Article is closed for comments.