根据权威机构调查报告显示消费者在购买智能设备时的考量因素,安全和隐私的因素占比大约在50%。
著名网络安全公司SonicWall在其2020年发布的网络安全报告中揭露,去年(2019)其记录的针对IoT智能设备的安全攻击增长5%,并且可以预见未来针对IoT智能设备的攻击将会越来越多。
安全在智能设备开发中非常重要, 同时作为普通的开发者和中小企业从0构建一套全面且稳定的安全防护能力,也是比较困难的。 那如何轻松地构建智能设备的安全能力,免遭黑客的攻击呢?
答案就是使用AliOS Things来进行智能设备的开发, AliOS Things是阿里巴巴自主研发、面向 IoT 领域的、高可伸缩的物联网操作系统。 AliOS Things 致力于搭建云端一体化 IoT 基础设施,具备极致性能、极简开发、云端一体、丰富组件、安全防护等关键能力。
AliOS Things在加速智能设备产品开发的同时, 注重建设智能设备的安全防护能力。
在端上安全能力的建设是以ID2为中心,目标是为物联网设备提供设备身份认证, 在上方提供使用ID2的tls安全连接协议(itls:基于mbedtls修改,主要修改mbedtls中tls协议的认证部分,改用ID2认证), 在下方需要对接各种不同厂家的不同等级的安全芯片(tee, se, mcu中的安全能力)。应用的开发者可以使用大家比较熟悉的mbedtls api来开发应用。
AliOS Things的安全能力比较丰富,后续会有一系列的文章来阐述。
本文章先从应用开发者接触比较多的Mbed TLS开始介绍。
Mbed TLS 是一个提供安全基础能力的库,它提供了安全算法,X.509证书,TLS/DTLS协议的实现。
安全算法方面它主要提供了下列功能
AES, Blowfish, Triple-DES (3DES), DES, ARC4, Camellia, XTEA, ChaCha20
ECB, CBC, CFB, CTR, GCM, CCM
RSA, DH, ECC, ECDHE, ECDSA
MD2, MD4, MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, RIPEMD-160
HMAC, Poly1305
ASN.1, BASE64
协议实现方面它提供了TLS/DTLS协议的客户端和服务端实现,支持SSL 3.0,TLS 1.0,TLS 1.1,TLS 1.2, DTLS 1.0, DTLS 1.2。
一言以蔽之, Mbed TLS是OpenSSL的替代品,Mbed TLS比OpenSSL更轻量级,适用于资源比较受限的嵌入式设备上。
鉴于Mbed TLS是一个提供安全基础能力的库,提供的能力比较全面,大多功能都可以单独使用。所以它的使用场景特别丰富。
前提说明:Transport Layer Security (TLS) 是Secure Sockets Layer (SSL) 的更高级版本, TLS 1.0是在SSL 3.0的基础上开发的,TLS是IETF标准化后的重新命名。在多种代码实现中并不区分TLS与SSL的名称,在API, 数据结构中大多都使用SSL的名称。
Mbed TLS的源码特别多, 下面以TLS客户端的一次TLS握手的代码实现为例,来分析一下TLS握手的处理过程。
// 执行SSL握手,协商SSL连接的各个因素。
int mbedtls_ssl_handshake( mbedtls_ssl_context *ssl )
{
int ret = 0;
if( ssl == NULL || ssl->conf == NULL )
return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
MBEDTLS_SSL_DEBUG_MSG( 2, ( "=> handshake" ) );
while( ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER )
{
// 根据SSL握手的状态,循环处理SSL握手的各个阶段,直到握手结束。
ret = mbedtls_ssl_handshake_step( ssl );
if( ret != 0 )
break;
}
MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= handshake" ) );
return( ret );
}
// 处理SSL握手的单个步骤
int mbedtls_ssl_handshake_step( mbedtls_ssl_context *ssl )
{
int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE;
if( ssl == NULL || ssl->conf == NULL )
return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
#if defined(MBEDTLS_SSL_CLI_C)
if( ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT )
// SSL客户端主动发起SSL连接请求,在这里处理SSL握手的单个步骤。
ret = mbedtls_ssl_handshake_client_step( ssl );
#endif
#if defined(MBEDTLS_SSL_SRV_C)
if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER )
// SSL被动接受SSL连接请求,在这里处理SSL握手的单个步骤。
ret = mbedtls_ssl_handshake_server_step( ssl );
#endif
return( ret );
}
// SSL客户端的单个阶段的握手处理
int mbedtls_ssl_handshake_client_step( mbedtls_ssl_context *ssl )
{
int ret = 0;
// SSL状态和参数和合法性检查
if( ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER || ssl->handshake == NULL )
return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
MBEDTLS_SSL_DEBUG_MSG( 2, ( "client state: %d", ssl->state ) );
// flush此连接上的数据,防止上一个步骤的数据未完全发出去
if( ( ret = mbedtls_ssl_flush_output( ssl ) ) != 0 )
return( ret );
#if defined(MBEDTLS_SSL_PROTO_DTLS)
// DTLS处理
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM &&
ssl->handshake->retransmit_state == MBEDTLS_SSL_RETRANS_SENDING )
{
if( ( ret = mbedtls_ssl_flight_transmit( ssl ) ) != 0 )
return( ret );
}
#endif /* MBEDTLS_SSL_PROTO_DTLS */
/* Change state now, so that it is right in mbedtls_ssl_read_record(), used
* by DTLS for dropping out-of-sequence ChangeCipherSpec records */
#if defined(MBEDTLS_SSL_SESSION_TICKETS)
if( ssl->state == MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC &&
ssl->handshake->new_session_ticket != 0 )
{
ssl->state = MBEDTLS_SSL_SERVER_NEW_SESSION_TICKET;
}
#endif
switch( ssl->state )
{
case MBEDTLS_SSL_HELLO_REQUEST:
// 切换状态hello request到发送client hello状态
ssl->state = MBEDTLS_SSL_CLIENT_HELLO;
break;
/*
* ==> ClientHello
*/
case MBEDTLS_SSL_CLIENT_HELLO:
//发送client hello报文,主要包括ssl协议的版本,随机数,加密算法套件, 压缩算法,协议扩展。
//此阶段主要是客户端用来告知服务端,自己所具备的能力,如支持的算法,协议版本,协议扩展等,
//就这些能力与服务端进行协商,如服务端会选择一个算法套件。
ret = ssl_write_client_hello( ssl );
break;
/*
* <== ServerHello
* Certificate
* ( ServerKeyExchange )
* ( CertificateRequest )
* ServerHelloDone
*/
case MBEDTLS_SSL_SERVER_HELLO:
// 解析服务端发送过来的server hello报文,处理服务端发来的协议版本,随机数,session id,
// 服务端选择的加密算法套件,压缩算法(一般不使用压缩),协议扩展等。
// 另外如果客户端发来是重协商请求,执行重协商检查和处理。
ret = ssl_parse_server_hello( ssl );
break;
case MBEDTLS_SSL_SERVER_CERTIFICATE:
// 解析服务端发送过来的server 证书,协议证书,进行证书的合法性检查,
// 检查通过后利用信任的CA证书进行验证,验证通过后检查证书的用途,检查用途合法后切换状态到
// 下一个阶 MBEDTLS_SSL_SERVER_KEY_EXCHANGE。
ret = mbedtls_ssl_parse_certificate( ssl );
break;
case MBEDTLS_SSL_SERVER_KEY_EXCHANGE:
// 解析服务端的key交换,或者key计算的材料。
ret = ssl_parse_server_key_exchange( ssl );
break;
case MBEDTLS_SSL_CERTIFICATE_REQUEST:
// 解析服务度发送的证书请求报文(可选),只有在服务端要求认证客户端时才有这个报文。
// 客户端在收到服务端发来的证书请求报文后,发送自己的认证给对端认证。
ret = ssl_parse_certificate_request( ssl );
break;
case MBEDTLS_SSL_SERVER_HELLO_DONE:
//解析服务度发送的server hello done报文,表示server hello阶段的报文到此结束
ret = ssl_parse_server_hello_done( ssl );
break;
/*
* ==> ( Certificate/Alert )
* ClientKeyExchange
* ( CertificateVerify )
* ChangeCipherSpec
* Finished
*/
case MBEDTLS_SSL_CLIENT_CERTIFICATE:
//发送客户端的证书,只有在收到服务端发来的证书请求时才发送。服务端会对发过去的证书进行认证
//以确定客户端身份的合法性。
ret = mbedtls_ssl_write_certificate( ssl );
break;
case MBEDTLS_SSL_CLIENT_KEY_EXCHANGE:
//发送客户端的KEY协商报文,主要包括KEY协商的协议如DH, 和协商的材料。
ret = ssl_write_client_key_exchange( ssl );
break;
case MBEDTLS_SSL_CERTIFICATE_VERIFY:
ret = ssl_write_certificate_verify( ssl );
break;
case MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC:
// 客户端发送changecipherspec报文,表明接下来开始使用协商计算出来的key,进行加密通信
ret = mbedtls_ssl_write_change_cipher_spec( ssl );
break;
case MBEDTLS_SSL_CLIENT_FINISHED:
// 客户端发送finished报文,表明握手结束。消息包括客户端交换过的重要信息的MAC,此消息是被加密的
// 重要信息包括客户端发送的随机数,KEY交换的材料等, 这个报文的意义在于之前协商的报文虽然是明文,
// 但确是不能被篡改的,所以需要一个握手最后过程的完整性保护。
ret = mbedtls_ssl_write_finished( ssl );
break;
/*
* <== ( NewSessionTicket )
* ChangeCipherSpec
* Finished
*/
#if defined(MBEDTLS_SSL_SESSION_TICKETS)
case MBEDTLS_SSL_SERVER_NEW_SESSION_TICKET:
// 解析服务端的session ticket。
ret = ssl_parse_new_session_ticket( ssl );
break;
#endif
case MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC:
// 解析服务端发送的changecipherspec报文,表明服务端接下来会使用之前
// 协商出来的key,进行加密通信。
ret = mbedtls_ssl_parse_change_cipher_spec( ssl );
break;
case MBEDTLS_SSL_SERVER_FINISHED:
// 解析服务端的finished报文,计算之前协商过程重要信息的MAC值,保证之前的协商信息
// 虽然是明文的, 但没有被篡改过。
ret = mbedtls_ssl_parse_finished( ssl );
break;
case MBEDTLS_SSL_FLUSH_BUFFERS:
// flush之前发送的协商报文
MBEDTLS_SSL_DEBUG_MSG( 2, ( "handshake: done" ) );
ssl->state = MBEDTLS_SSL_HANDSHAKE_WRAPUP;
break;
case MBEDTLS_SSL_HANDSHAKE_WRAPUP:
// SSL连接已经建立,准备切换当前的ssl session和传输层,并释放之前旧的session和传输层数据。
mbedtls_ssl_handshake_wrapup( ssl );
break;
default:
//其他无效状态
MBEDTLS_SSL_DEBUG_MSG( 1, ( "invalid state %d", ssl->state ) );
return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
}
return( ret );
}
AliOS Things Mbed TLS是在开源的Mbed TLS 2.16.0 基础上,适配到AliOS Things。 秉承的宗旨是尽量少改动,API与开源的完全保持一致,方便开发者使用和移植上面的应用。相关代码路径在components/security/mbedtls。
AliOS Things Mbed TLS组件在开源的基础上主要有以下几点修改:
为适应嵌入式设备资源非常受限的场景,AliOS Things对Mbed TLS还提供了组件化的配置, 用户可以执行aos make menuconfig, 依次进入到 Utility Configuration -> Mbed TLS 2.16.0 -> Mbedtls Configuration 根据自己的场景需要,进行适当的配置, 我们对Mbed TLS组件提供了默认配置,能覆盖大部分场景。
以一个简单的TLS客户端为例, 该TLS客户端访问阿里云物联网平台(上海站点)iot-as-mqtt.cn-shanghai.aliyuncs.com。
......
#define SERVER_PORT "1883"
#define SERVER_NAME "a1MZxOdcBnO.iot-as-mqtt.cn-shanghai.aliyuncs.com"
#define GET_REQUEST "GET / HTTP/1.0\r\n\r\n"
// 验证对端证书所需要的CA认证
const char* tls_client_test_ca_pem = \
"-----BEGIN CERTIFICATE-----\r\n"
"MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG\r\n" \
"A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv\r\n" \
"b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw\r\n" \
"MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i\r\n" \
"YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT\r\n" \
"aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ\r\n" \
"jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp\r\n" \
"xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp\r\n" \
"1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG\r\n" \
"snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ\r\n" \
"U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8\r\n" \
"9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E\r\n" \
"BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B\r\n" \
"AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz\r\n" \
"yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE\r\n" \
"38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP\r\n" \
"AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad\r\n" \
"DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME\r\n" \
"HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\r\n" \
"-----END CERTIFICATE-----\r\n";
int tls_client_test( void )
{
mbedtls_net_context server_fd;
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
mbedtls_x509_crt cacert;
......
/* 设置DEBUG级别 */
#if defined(MBEDTLS_DEBUG_C)
mbedtls_debug_set_threshold( DEBUG_LEVEL );
#endif
// 0. 初始化需要的数据结构,
mbedtls_net_init( &server_fd );
mbedtls_ssl_init( &ssl );
mbedtls_ssl_config_init( &conf );
mbedtls_x509_crt_init( &cacert );
// 1. 加载信任的CA证书
mbedtls_x509_crt_parse( &cacert, (const unsigned char *) tls_client_test_ca_pem,
strlen( tls_client_test_ca_pem ) + 1 );
// 2. 建立TCP的连接
mbedtls_net_connect( &server_fd, SERVER_NAME, SERVER_PORT, MBEDTLS_NET_PROTO_TCP )
// 3. 使用SSL默认配置, 设定为SSL客户端,采用TCP。
mbedtls_ssl_config_defaults( &conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 )
// 3.1. 配置ssl的认证模式为需要认证对端。
mbedtls_ssl_conf_authmode( &conf, MBEDTLS_SSL_VERIFY_REQUIRED );
// 3.2. 配置信任的CA链
mbedtls_ssl_conf_ca_chain( &conf, &cacert, NULL );
// 3.3. 配置系统的随机数发生函数。这里tls_client_random为自己实现的随机数发生函数。
mbedtls_ssl_conf_rng( &conf, tls_client_random, NULL );
// 3.4. 配置debug函数,这里my_debug为自己实现的debug函数,比如是一个简单的打印到串口的函数。
mbedtls_ssl_conf_dbg( &conf, my_debug, NULL );
#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
// 3.5. 配置SSL的最大分片长度。
mbedtls_ssl_conf_max_frag_len( &conf, MBEDTLS_SSL_MAX_FRAG_LEN_4096 );
#endif
// 3.6. 使以上的配置conf对当前SSL连接 ssl 生效。
mbedtls_ssl_setup( &ssl, &conf )
// 3.7. 为当前SSL连接ssl设置对端的主机名。
mbedtls_ssl_set_hostname( &ssl, SERVER_NAME )
// 3.8. 为当前SSL连接ssl设置ssl的I/O函数分别为mbedtls_net_recv/mbedtls_net_send。
mbedtls_ssl_set_bio( &ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL );
// 4. 发起SSL握手
mbedtls_ssl_handshake( &ssl );
// 5. 获得对端证书的认证结果。注意这里只是得到认证结果,真正的认证过程是在上一步SSL握手过程中。
mbedtls_ssl_get_verify_result( &ssl );
// 6. 发送数据给对端。SSL握手成功后,SSL连接已经建立,此时可以利用SSL连接
// 发送出去的数据是被加密和完整性保护的。
mbedtls_ssl_write( &ssl, buf, len );
// 7. 从SSL连接中读取数据。
do {
ret = mbedtls_ssl_read( &ssl, buf, buf_len -1 );
if( ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE )
continue;
if( ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY )
break;
if(ret <= 0)
break;
......
} while (1);
// 8. 关闭SSL连接。
mbedtls_ssl_close_notify( &ssl );
// 9. 释放SSL相关资源。
mbedtls_net_free( &server_fd );
mbedtls_x509_crt_free( &cacert );
mbedtls_ssl_free( &ssl );
mbedtls_ssl_config_free( &conf );
mbedtls_free(buf);
......
}
1、TLS 握手失败,Debug消息显示"buffer too small ..." 或者 "bad message length"
这可能是TLS消息出来的I/O buffer小于消息的长度, 通过配置增大I/O buffer即可, 下列通过AliOS Things的menuconfig来配置,Mbedtls Configuration -> TLS/DTLS protocol support -> TLS/DTLS maximum incoming/outgoing content length , AliOS Things默认配置的I/O buffer为4KB, 一般扩大到16384 (16KB)可以解决所有的此类问题。
2、支持TLS1.3吗?
暂不支持TLS1.3, AliOS Things 的Mbeb TLS组件能力与社区的相同版本能力保持一致,目前支持的协议版本:SSL 3.0,TLS 1.0,TLS 1.1,TLS 1.2, DTLS 1.0, DTLS 1.2。 现在主流服务器都会向后兼容支持TLS1.2等,不会强制客户端使用TLS1.3版本。
如需更多技术支持,可加入钉钉开发者群
更多技术与解决方案介绍,请访问阿里云AIoT首页https://iot.aliyun.com/