本文主要介绍基于 ESP8266_RTOS_SDK 的 SSL 加密使用方法,将分别介绍 ESP8266 作为 SSL client 和 SSL server 的使用方法。
SSL 功能需要占用大量内存,请开发者在上层应用程序确保内存足够。在将 SSL fragment 设置为 8KB 以及证书用 private key RSA2048 的情况下, SSL 双向认证功能需要 30KB 的空间,由于服务器的证书大小不同,所需空间可能更大。
为使 SSL/TLS 更加安全,ESP8266_RTOS_SDK openssl 默认仅支持 RSA2048 以及以上的 private key 生成的证书。 见Asymmetric algorithm key lengths。
本文所用的 SDK:ESP8266_RTOS_SDK: Version 1.5.0
本示例可以用两块 ESP8266 分别作为 openssl client 以及 server,也可以用一块 ESP8266 作为 client/server, 用电脑作为 server/client。如果用电脑作为 client/server,需安装 openssl 库:
sudo apt-get install openssl
本文中所用的证书脚本为gencrt.sh genheader.sh,这两个脚本会生成 ca.crt,client.crt,client.key,server.crt,server.key,ssl_client_crt.h,ssl_server_crt.h。
ca.crt 为 CA 证书,用于校验 server.crt 以及 client.crt。
ssl_client_crt.h 为 ca.crt、client.crt、client.key 生成的数组文件。
ssl_server_crt.h 为 ca.crt、server.crt、server.key 生成的数组文件。
如果用户想使用自己的证书,请先阅读SSL-TLS 双向认证(一) – SSL-TLS工作原理。
在 Ubuntu 端运行 openssl s_server -CAfile ca.crt -cert server.crt -key server.key -verify 1 -tls1_1 -accept 443
其中 ca.crt、server.crt 以及 server.key 为附件 gencrt.sh 生成的证书。
下载源码。
修改 openssl_demo.c 的以下两行,将地址改为电脑 IP 地址:
#define OPENSSL_DEMO_TARGET_NAME "192.168.3.196"
#define OPENSSL_DEMO_TARGET_TCP_PORT 443
修改 user_config.h 的以下两行,改为本地可用 Wi-Fi:
#define SSID "HUAWEI001"
#define PASSWORD ""
让电脑与 ESP8266 处于同一局域网内,编译下载固件。
下载源码。
修改 user_config.h 的以下两行,改为本地可用 Wi-Fi:
#define SSID "HUAWEI001"
#define PASSWORD ""
编译下载固件,并让电脑与 ESP8266 处于同一局域网内。
根据 ESP8266 串口 log 获得 ESP8266 的 IP 地址 192.168.3.6。
在Ubuntu端运行 openssl s_client -CAfile ca.crt -cert client.crt -key client.key -verify 1 -tls1_1 -host 192.168.3.6 -port 443
。
192.168.3.6 为 ESP8266 的 IP 地址 ca.crt client.crt ca.key 为附件 gencrt.sh 生成的证书。
本节主要介绍 openssl 证书认证相关接口,更多软件接口介绍,请参考 ESP8266 编程手册 ESP8266__RTOS_SDK_API Reference.pdf。
#include "openssl/ssl.h"
SSL_CTX* SSL_CTX_new(const SSL_METHOD *method)
功能:根据用户所需要的 SSL/TLS 协议版本创建 SSL context。
返回:已经设定好的 SSL context。
参数: const SSL_METHOD *method
设置用于数据交换协议版本。
TLSv1_client_method
TLSv1_1_client_method
SSLv3_client_method
SSLv23_client_method
TLSv1_server_method
TLSv1_1_server_method
SSLv3_server_method
SSLv23_server_method
X509* d2i_X509(X509 **cert, const unsigned char *buffer, long len)
功能:将crt证书文件转换为 _x509_ctx 格式的结构体。
返回:crt文件转换后的 X509 结构指针。
参数:
X509 **cert
: crt 文件转换后的 X509 格式的 context ,需要在外部分配存储空间,建议传入 NULL。。
const unsigned char *buffer
: 字符串数组,crt 文件原始格式。
long len
: buffer 长度。
int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x)
功能:添加用于校验对端的 CA 证书,ESP8266 作为 server 时也是用此函数添加 CA 证书。
返回:0-失败, 1-成功
参数:
SSL_CTX *ctx
: SSL context。
X509 *x
: CA 证书转换后的 X509 结构指针。
int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const unsigned char *d)
功能:添加 client/server 本地证书。
返回:0-失败, 1-成功
参数:
SSL_CTX *ctx
: SSL context。
len
: cert长度。
const unsigned char *d
: cert 数据。
int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, const unsigned char *d, long len)
。功能:添加对应 4.4 节证书的本地私钥。
返回:0-失败, 1-成功
参数:
type
: 未使用,可传入 NULL。
SSL_CTX *ctx
: SSL context。
len
: private key 长度。
const unsigned char *d
: private key 数据。
void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *))
功能:设定是否校验方式,比如是否校验对端证书。
参数:
SSL_CTX *ctx
: SSL context。
int mode
: 校验方式,可以设置为 SSL_VERIFY_NONE
或 SSL_VERIFY_PEER
。
int (*default_verify_callback) (int ok, X509_STORE_CTX *ctx)
: 校验结果,用户自定义回调函数。
void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len)
功能:设定SSL fragment 长度,参见第 6 节 fragment 介绍。
参数:
SSL_CTX *ctx
: SSL context。
size_t len
: 根据证书长度以及传递秘文长度设定。
fragment 是指 openssl 在建立SSL/TLS连接握手过程中证书验证以及交换数据时加解密数据的缓存大小。
TLS 协议支持的 fragment 最大是 16K(RFC2246)。由于嵌入式系统RAM资源比较珍贵(ESP8266 本身 RAM 空间比较小),以及嵌入式系统在正常使用时很少用到大文件加解密,所以 ESP8266_RTOS_SDK 以及 ESP8266_NONOS_SDK 中开放 fragment 设定接口 SSL_CTX_set_default_read_buffer_len 和 espconn_secure_set_size,以节省资源。
fragment 大小主要由芯片 RAM 大小、证书、传输单笔数据(对这笔数据整体进行加密)的最大长度决定(传输单笔数据的最大长度 < fragment < RAM 大小)。
ESP8266_RTOS_SDK(openssl lib) 中设定范围为 2048 - 8192 Bytes
在传输的单笔数据较小的情况下,证书大小主要由私钥长度决定,使用不同的私钥长度生成的证书所需的 fragment 大小建议值:
私钥长度 | fragment 推荐值 |
---|---|
RSA2048 | 2048 |
RSA3072 | 3072 |
RSA4096 | 4096 |