时间: 2020-09-03 00:08:26 人气: 2288 评论: 0
在最新的《在微控制器和物联网上使用JavaScript》的文章中,我们发现Espruino在Esp8266平台上非常受欢迎,Espruino的确很不错,但在EPS8266平台上还是非常有限的。Espruino提供了TLS支持,这一功能在其他硬件平台非常有用。在今天的这篇文章中,我们回到Particle Photon上来解决他的一个最大的缺点:缺少TLS支持,接下来我们将详细介绍如何添加这一功能。
在整个《在微控制器和物联网上使用JavaScript》系列中,我们探索了在微控制器平台上添JavaScript的各种方法。我们还学习了如何使用C和JavaScript库。然而,到目前为止,我们还是遗漏了一个问题,那就是安全通信。
当我们研究Particle Photon作为替代方案时,其固件为我们提供了一种安全通信的方法:即与Particle Cloud相连。Particle Cloud是由Photon的开发者Particle提供的一系列在线服务。它非常的方便:易于使用,加密,并且能够充当通往其他在线服务的网关。Particle Cloud允许我们将我们的传感器数据安全地发送到WebTask。这很好,但是,对于某些应用程序是无法依靠外部云平台的。另外,粒子云有其自身的局限性,可能不足以达到我们的目的。我们需要一个替代品。
另一方面,我们也在ESP8266上看过Espruino。Espruino提供了对TLS的支持,但是,它仅适用于某些特定的硬件。至于ESP8266,它默认是禁用的。因此,Espruino和ESP8266不是安全通信的有效替代方案。
在这篇文章中,我们将思考一个更加显而易见的方案:为Particle Photon添加一个安全的通信库。对此有很多选择,但我们会选择互联网上使用最多的:TLS。这将允许我们在没有网关或代理服务器(如中间的Particle Cloud)的情况下与常用服务进行通信。
TLS及其前身SSL都非常大。它支持许多密码和算法。因此,为了将代码大小和内存使用量降到最低,非常重要的一点就是我们需要选择一个专为此设计的库。该领域中有三个主要竞争者:Mbed TLS(以前称为PolarSSL),wolfSSL(以前称为CyaSSL)和GUARD TLS(以前称为MatrixSSL)。他们三个都是开源的,且拥有免费软件许可证,但其中只有Mbed TLS允许其在闭源项目中免费使用。这三者在嵌入式社区中都很受欢迎,所以选择哪一个取决于您的用例或许可要求,为了保持我们所有的选择公开,我们选择了Mbed TLS。
传输层安全性(TLS)是计算机网络上安全通信的标准。它的前身安全套接字层(SSL)由Netscape于1994年为其Web浏览器开发.TLS用于Internet上的安全通信,并且是由HTTPS实现的安全层。HTTPS基本上就是HTTP,但启用了TLS的TCP / IP Socket(这意味着几乎所有的互联网事务都依赖于TLS来实现安全性)。TLS支持许多不同的算法,提供各种级别的安全性和计算复杂性。它还支持高级功能,如前向保密,即使未来密钥泄漏,也可防止解密过去的消息。
TLS通过非对称和对称密码的组合工作。非对称密码学使用两个密钥,一个公共密钥和一个私密密钥,以允许加密数据在单一方向上流动。公钥用来加密只有私钥可以解密的数据。另一方面,对称密码使用单个共享密钥来加密和解密数据。这意味着任何知道共享密钥的人都可以读取和更改使用它加密的数据。
TLS的工作方式是在公共证书的形式下在通信双方中的一个保留一系列可信的公钥。证书是关于由该实体(或其他人)使用其私钥签署的实体信息。证书内也携带实体的公钥,实体的公钥允许任何人检查由该实体执行签名的有效性。换句话说,如果你有一个实体的证书,你就知道实体的数据(比如它的名字),你可以用它的公钥执行两个动作:为它加密数据,或者检查它对数据签名的有效性。但要真正确定这意味着什么,您需要确保公钥来自正确的实体,并且相信它。
在启动连接之前,一组可信任的证书必须事先存在于支持TLS的客户端中。至于网络环境中,Web浏览器和操作系统在安装时会附带自己的一组可信证书。
当客户端启动连接时,客户端连接到服务器并请求服务器发送其公共证书以及为了验证证书的合法性所需的其他证书。这是在没有任何加密的情况下在公开场合完成的。然后,客户端可以通过从其可信证书之一(预先安装在客户端中的证书)中查找签名来验证服务器的证书。服务器通常依靠中间证书进行验证,这意味着这些中间证书可能由不是来自可信组的证书进行签名,而我们必须用一个可信证书对中间证书进行签名。因此,为了检查服务器证书的有效性,客户端要沿着证书签名链直到找到一个可信证书的签名。如果所有签名都通过检验,那么收到的证书可以被认为是有效的。对于互联网连接,一旦服务器的证书得到验证,客户端必须将其中的一个字段(公用名称(CN))与启动连接时请求的主机名进行比较。因此,如果客户想连接www.google.com,证书必须为www.google.com(证书中的通用名称必须与浏览器请求的地址相匹配),这取决于控制一组可信任证书的实体,以确保为特定域请求证书的人是该域的实际所有者。这样,只有Google(公司)才能为www.google.com申请证书。
一旦证书被验证并且域名与通用名称匹配,就可以建立一个安全的通信通道。为此,TLS使用密钥交换算法。这些算法依赖于服务器的证书和非对称加密来协商服务器和客户端之间的新共享密钥。TLS支持多种不同的算法。一旦这个密钥建立,通信就切换到对称加密。对称加密比非对称加密更有效,因此更适合于在初始握手后与服务器交换数据。TLS也支持不同的对称算法,但大多数情况下选择AES的一种变体。
Mbed TLS是一个C库。它需要一个C99编译器并且高度可配置。我们将删除任何不必要的内容,如文件访问,旧版本支持(SSLv3),Berkeley / BSD / Linux套接字等,以将代码大小降至最低。
在继续之前需要谨慎小心。TLS所需的一些算法依赖于一个好的熵源,熵源是随机性的来源,通常用于提供Mbed TLS内部使用的随机数生成器(RNG)。这带来了一个问题:开箱即用的Particle Photon没有好的熵源。请注意,一个伪随机数发生器(PRNG)(比如Particle Firmware提供的那个)不够好。
一种选择是构建自己的,另一种选择是购买一个。无论您选择什么,请注意,没有良好的熵源可能会导致通信受损。对于我们的示例,我们已配置Mbed TLS以使用由粒子固件提供的PRNG。这不是一个好办法,不要在生产中使用它。粒子的PRNG依赖于在启动过程中设置一个真正随机的种子,除非连接到粒子云,否则不会完成。即使那样,也不能保证PRNG是一个用于加密的好熵源。如果您想在生产中使用类似的东西,请购买专业的随机数字生成器并将其集成到您的项目中。
对于我们的例子,我们将再次转向我们的传感器集线器示例。但是,我们将使用第三篇文章中的版本并在此基础上进行构建。我们第三篇文章中的传感器集线器示例执行以下操作:
对于这篇文章,我们将更改示例以执行以下操作:
Web任务需要TLS,所以所有的HTTP请求都会被加密。
您可能已经注意到,在我们的新示例中没有提及粒子云。没错,拥有TLS让我们可以不用依靠粒子云就能进行安全通信。
要使用Mbed TLS,我们需要在它的网站上下载最新的压缩包,并在我们所选择的目录解压它。然后,我们调整Docker命令行以将此目录公开给Docker镜像,该镜像用于编译Particle Photon固件。
Mbed TLS支持makefile和CMake。由于我们已经在JerryScript上使用CMake,所以我们可以直接用CMake安装Mbed TLS。编译MBed TLS只是一个正确调用CMake方法的问题。
mbed:mkdir -p $(BUILD_DIR)/mbedtls
CFLAGS="$(EXT_CFLAGS) -Os" cmake -B$(BUILD_DIR_MBED) -H../mbedtls \
-DENABLE_TESTING=OFF \
-DUSE_SHARED_MBEDTLS_LIBRARY=OFF \
-DUSE_STATIC_MBEDTLS_LIBRARY=ON \
-DENABLE_PROGRAMS=OFF \
-DCMAKE_TOOLCHAIN_FILE=$(JERRYDIR)/cmake/toolchain_external.cmake \
-DEXTERNAL_CMAKE_SYSTEM_PROCESSOR=armv7l \
-DEXTERNAL_CMAKE_C_COMPILER=arm-none-eabi-gcc \
-DEXTERNAL_CMAKE_C_COMPILER_ID=GNU
make VERBOSE=1 -C$(BUILD_DIR_MBED)
我们希望构建精简而干净的Mbed TLS版本而不需要任何多余的功能,例如文件访问。所有Mbed TLS选项均通过其配置文件config.h进行设置。来看我们为这个例子选择的设置。支持TLS 1.1,RSA,椭圆曲线和AES,将RAM使用量降到最低的三个重要设置是:
#define MBEDTLS_MPI_WINDOW_SIZE 1#define MBEDTLS_AES_ROM_TABLES
#define MBEDTLS_SSL_MAX_CONTENT_LEN 6144第一行更改Mbed TLS使用的大数库的内部设置,这会减慢操作速度,但消耗更少的RAM。第二行告诉系统预编译AES算法使用的表并将它们存储在静态常量C数组中。这允许表格保存在ROM而不是RAM中。第三行减少了Mbed TLS使用的接收缓冲区的大小。TLS要求至少16KiB的缓冲区,但是当服务器和客户端都支持扩展或者数据永远不会超过缓冲区大小时,可以使用较小的缓冲区。
Mbed TLS的原始config.h文件已完整记录。如果您有兴趣将库调整为您的需要,请查看它。
关于Mbed TLS的一个很酷的事情是,使用任何通信通道都非常简单。该库只需要定义两个函数:一个用于将数据写入通道,另一个用于接收数据。当然,如果您使用的是众所周知的套接字库如Berkeley Socket(大多数Unix,如macOS或Linux)或WinSock(Windows套接字,aspx),Mbed TLS为您提供了必要的功能。由于我们使用的是Particle Photon技术,我们的TCP客户端库没有开箱即用的支持。幸运的是,编写发送/接收函数非常简单:
TCPClient client;static inttcp_client_send(void *ctx, const unsigned char *buf, size_t len) {
TCPClient *client = reinterpret_cast<TCPClient *>(ctx);
return client->write(buf, len);
}static inttcp_client_recv(void *ctx, unsigned char *buf, size_t len) {
TCPClient *client = reinterpret_cast<TCPClient *>(ctx);
const int read = client->read(buf, len);
if(read <= 0) {
return MBEDTLS_ERR_SSL_WANT_READ;
} else {
return read;
}}bool connect(const char *host, uint16_t port) {
// (...)mbedtls_ssl_set_bio(&ssl, &client,
tcp_client_send, tcp_client_recv, NULL);
// (...) }我们编写了一个使用TCP客户端(来源于Particle)和Mbed TLS的小型C ++类来连接服务器:
struct tls_tcp_client {tls_tcp_client() {
mbedtls_ssl_init(&this->ssl);
mbedtls_ssl_config_init(&this->conf);
mbedtls_entropy_init(&this->entropy);
mbedtls_ctr_drbg_init(&this->ctr_drbg);
/* This is the best entropy source we have, NOT SECURE */mbedtls_entropy_add_source(&entropy, get_random, NULL, 1,
MBEDTLS_ENTROPY_SOURCE_STRONG);
mbedtls_ctr_drbg_seed(&ctr_drbg,
mbedtls_entropy_func, &entropy, NULL, 0);
mbedtls_ssl_config_defaults(&conf,
MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM,MBEDTLS_SSL_PRESET_DEFAULT);
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
mbedtls_ssl_conf_ca_chain(&conf, &global_tls_ca, NULL);
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED);
//mbedtls_ssl_conf_verify(&conf, verify, NULL);mbedtls_ssl_conf_max_version(&conf,
MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_2);
mbedtls_ssl_conf_min_version(&conf,
MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_2);
}bool connect(const char *host, uint16_t port) {
mbedtls_ssl_session_reset(&ssl);
mbedtls_ssl_setup(&ssl, &conf);
mbedtls_ssl_set_bio(&ssl, &client,
tcp_client_send, tcp_client_recv, NULL);
Log.trace("TLS connect to host: %s", host);
mbedtls_ssl_set_hostname(&ssl, host);
client.connect(host, port);
Log.print(client.remoteIP().toString());
int code = 0;
unsigned long now = millis();
while((code = mbedtls_ssl_handshake(&ssl)) ==
MBEDTLS_ERR_SSL_WANT_READ) {
if((millis() - now) > 10000) {
// timeoutbreak;
} //Log.trace("10 Free mem: %u", System.freeMemory());Particle.process();
}if(code != 0) {
char buf[128];
mbedtls_strerror(code, buf, sizeof(buf));
Log.trace("TLS connected failed, code %i -> %s", code, buf);
client.stop();
return false;
}return true;
} // (...)}由于支持TLS,公开我们最新版本的photon.TCP客户端和Javascript对象也非常简单。我们只需要使用JerryScript的API 来包装我们tls_tcp_client类。这类似于我们为普通TCP客户端所做的工作(TCP客户端由Particle固件提供)
static jerry_value_t create_tls_tcp_client(const jerry_value_t func,
技术沙龙 教程文章 热点综合