嵌入式系统中的加解密签名

笔者来了解一下嵌入式系统中的加解密

1、背景与名词解释

笔者最近在做安全升级相关的模块,碰到了一些相关的概念和一些应用场景,特来学习记录一下。

1.1 名词解释

  1. 对称加密:对称加密是一种加密方法,使用相同的密钥(称为密钥)进行加密和解密 。发送方和接收方必须共享同一个密钥。对称加密算法的优点是速度快 ,适合大量数据的加密,但密钥分发和管理可能会面临安全挑战。
  2. 非对称加密:非对称加密使用成对的密钥:公钥和私钥 。公钥用于加密数据,只有持有配对的私钥才能解密。私钥用于对数据进行签名,公钥用于验证签名。非对称加密算法通常较慢适合于相对较小的数据量 ,但在密钥分发身份验证方面具有很大的优势。
  3. 签名:数字签名是非对称加密的一种应用,用于验证数据的完整性和来源。发送方使用其私钥对数据进行签名,接收方使用发送方的公钥来验证签名。如果验证成功,则可以确信数据未被篡改过。有点像这个数据被签名了一样,无法抵赖,就是从某个人发出的。
  4. 证书:证书是用于验证公钥拥有者身份的数字文件。证书包含公钥及其拥有者的信息,并由权威认证机构(CA)签名,用于证明该公钥确实属于指定的实体。证书常用于HTTPS连接中,以及公钥基础设施(PKI)中确保通信安全。

1.1.1 对称加密算法

  1. AES(Advanced Encryption Standard)
    AES是一种对称加密算法,广泛应用于保护敏感数据。它支持不同的密钥长度(如AES-128、AES-192、AES-256),速度快且安全性高,被广泛认可为目前最安全的对称加密算法之一。
  2. DES(Data Encryption Standard)
    DES是一种早期的对称加密算法,已不再被推荐使用,因为其56位密钥长度对现代计算能力来说安全性较低。
  3. 3DES(Triple DES)
    3DES是对DES的改进,通过多次对数据块应用DES算法来增强安全性。虽然安全性较高,但由于效率低下和对现代计算能力来说密钥长度较短的缺点,现在也逐渐被AES所取代。

1.1.2 非对称加密算法

  1. RSA(Rivest-Shamir-Adleman)
    RSA是最广为人知的非对称加密算法之一,用于加密、数字签名和密钥交换。它基于大素数的难解性,用于生成公钥和私钥对。RSA在加密和签名方面都有应用。
  2. DSA(Digital Signature Algorithm)
    DSA是一种用于生成和验证数字签名的非对称加密算法。它通常与SHA-1或SHA-2等哈希算法结合使用,用于确保数据的完整性和来源。
  3. ECC(Elliptic Curve Cryptography)
    ECC是一种基于椭圆曲线数学的非对称加密算法。它提供了与RSA相当的安全性,但使用更短的密钥长度,因此在资源受限的环境下更为适用。
  4. DH(Diffie-Hellman)
    Diffie-Hellman是一种密钥交换协议,虽然本身不是加密算法,但用于安全地交换对称加密算法中使用的密钥。它的安全性基于离散对数问题的难解性。
    这些算法都在不同的应用场景中发挥着重要作用,选择合适的加密算法取决于安全需求、性能要求以及特定的应用环境。

1.2 为什么对称加密有风险

如上图所示,甲和乙通过对称加密算法进行通信,

  1. 首先甲向乙发送对称秘钥,约定使用该秘钥进行加密和通讯
  2. 然后发送加密的消息通信,
  3. 由于网上有窃听者会监听数据,所以一旦秘钥在网上传输,就有泄露的风险,泄露之后,数据就和明文在网上传输没什么区别,有风险。
  4. 有些人会问,我不在网络上传输秘钥不就行了吗? 那不在网上传输,乙如何知道甲使用什么方式加密,又合入通信呢?所以该方式一定有风险。

1.3 为什么非对称加密安全性更高

基于以上的想法,一个秘钥肯定不行,那就有两个秘钥,一个永远保存在本地,另外一个可以再网上传输,一个加密,另外一个解密,不就可以保证秘钥不泄露吗?

如上图所示,私钥永远在本地,公钥在网上传输,公钥加密的数据,私钥才可以解密,所以即使网上知道了公钥,也无用,因为没有私钥,没法解密查看到数据。

1.4 为什么需要签名

因为上面公钥是公开的,所以任何人都可以用公钥加密给甲或者乙发数据,就导致甲或者乙无法验证对方的身份是否正确,假如窃听者模仿甲或者乙向对方发错误的指令,那可能也有严重的后果。

所以首先是验证身份,然后才是通信数据,这时候就有了签名的说法,类似你在通信数据上面做了签名,无法抵赖。

签名的验证是通过私钥加密,公钥验证签名的过程来实现,只要公钥可以验证过了,那说明对方一定是甲或者乙发出的,不会是第三方窃听者发出来。

1.5 为什么还需要证书

假如窃听者 在身份验证时,就截获了数据,然后甲和乙拿到的不是对端的公钥,而且窃听者的公钥,然后在窃听者那边去转换数据。

如上图所示,这样即使有了身份验证也无效,关键原因就是拿到的公钥不对,如果公钥有一定的公信力,那么就可以认可对方的身份,因此就有了证书的这个概念。

公钥被放到有公信力的证书里面,保证拿到的一定是对的公钥。证书一般有证书颁发机构来发放,即CA(certificate Authority)。证书包括了公钥,同时证书也被签名了,保证证书没有被篡改。

通常被内置在操作系统或浏览器中,这些受信任的根证书(Root Certificate)构成了信任链的基础,用于验证由该CA签发的所有证书的有效性。

2、嵌入式系统中的安全升级

2.1 嵌入式系统升级

嵌入式系统中的升级讲解相对较多,可以参考笔者之前的文章,BootLoader的理解与实现Bootloader学习理解----跳转优化异常Bootloader学习理解学习--加强版

2.2 嵌入式系统安全升级

像上面嵌入式设备升级,如果设备相同,完全可以模拟同样的嵌入式设备下发升级文件,必须进行签名认证信息,才可以。

2.3 Python代码中的RSA加密与签名验证

在python里面,我们用到了Cryptography以及OpenSSL这个库。

  1. 生成一对秘钥
    生成私钥时,public_exponent=65537指的是公钥指数,通常是一个小的奇数,用于加密或者验证签名,
    Key的size为2048位,也就是256Byte
    最后转化为明文显示
python 复制代码
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization

# 生成私钥
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend()
)

# 生成公钥
public_key = private_key.public_key()

# 将私钥和公钥序列化为PEM格式
private_pem = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.TraditionalOpenSSL,
    encryption_algorithm=serialization.NoEncryption()
)

public_pem = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)

# 打印私钥和公钥
print(private_pem.decode('utf-8'))
print(public_pem.decode('utf-8'))
  1. 私钥对文件进行签名
    签名的时候,指明的padding是v1.5版本的PKCS,一个应用相对广泛标准化的填充方案,适用于RSA签名和加密操作。还有其他版本的填充,比如1.2版本的,以及PSS算法填充等等。
    填充是保证数据块大小符合算法要求的一种方案,加密算法要求数据大小符合256Byte,比如长度是2048位的计算。
python 复制代码
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

# 已经有了private_key和要签名的文件1.txt

# 读取文件内容
with open('1.txt', 'rb') as f:
    data = f.read()

# 使用私钥对文件进行签名
signature = private_key.sign(
    data,
    padding.PKCS1v15()
    hashes.SHA256()
)

# 将签名写入文件
with open('signature', 'wb') as f:
    f.write(signature)
  1. 公钥对文件进行验签
    验证签名时,一定要和加密时候的填充方案一致,选择PKCSv15,不然会验签失败。
python 复制代码
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization


# 使用公钥验证签名
try:
    public_key.verify(
        signature,
        message,
        padding.PKCS1v15(),
        hashes.SHA256()
    )
    print("签名验证成功,消息未被篡改。")
except Exception as e:
    print(f"签名验证失败: {e}")

当然也可以用openssl库来 进行签名和验签工作。

sh 复制代码
# 生成2048位的RSA私钥
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048

# 从私钥中提取公钥
openssl rsa -pubout -in private_key.pem -out public_key.pem

# 使用私钥签名文件,生成签名文件
openssl dgst -sha256 -sign private_key.pem -out signature.bin example.txt

# 验证签名
openssl dgst -sha256 -verify public_key.pem -signature signature.bin example.txt

2.4 嵌入式系统中的加解密

笔者最近了解到一个mbedtls,适合于嵌入式的一个安全加密库,C语言编写,OpenSSL是互联网上面应用的,相对较大,不适合嵌入式。

仓库地址:https://github.com/Mbed-TLS/mbedtls

mbedtls编译完成后,会生成三个库文件,之后就可以根据头文件,链接这三个库进行加解密。

例如下面的代码,可以编译生成来进行测试,生成签名和验签。

c 复制代码
#include "mbedtls/pk.h"
#include "mbedtls/md.h"
#include "mbedtls/base64.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "string.h"
#include <stdlib.h>

#define SIGNATURE_MAX_SIZE  256

int rsa_pkcs1v15_sha256_sign(const unsigned char *msg, size_t msg_len,
                               const char *priavte_key_pem, char *sign_base64, int sign_len)
{
    mbedtls_pk_context pk;
    mbedtls_entropy_context entropy;
    mbedtls_ctr_drbg_context ctr_drbg;

    uint8_t sig_buff[SIGNATURE_MAX_SIZE];
    unsigned char hash[32] = {0};
    size_t sig_len = 0;
    int ret = 0;
    char *b64_out = NULL;
    int b64_len = 0;
    const char *pers = "mbedtls_pk_sign";       // Personalization data,
    // that is device-specific identifiers. Can be NULL.
    
    // 初始化随机数生成器
    mbedtls_entropy_init( &entropy );
    mbedtls_ctr_drbg_init( &ctr_drbg );
 
    //初始化上下文
    mbedtls_pk_init( &pk );

    mbedtls_ctr_drbg_seed( &ctr_drbg,
                           mbedtls_entropy_func,
                           &entropy,
                           (const unsigned char *) pers,
                           strlen( pers ) );

 //导入私钥
    ret = mbedtls_pk_parse_key(&pk, (const unsigned char *)priavte_key_pem,\
                               strlen(priavte_key_pem)+1,\
                               NULL, 0, NULL, 0);
    if(ret != 0)
    {
        ret = -1;
        goto exit;
    }

    // 计算 sha256 消息摘要
    ret = mbedtls_md(mbedtls_md_info_from_type( MBEDTLS_MD_SHA256 ),
                     (const unsigned char *)msg, msg_len, hash);
    if(ret != 0)
    {
        ret = -1;
        goto exit;
    }
 
    // 签名
    ret = mbedtls_pk_sign(&pk, MBEDTLS_MD_SHA256, hash, sizeof (hash), sig_buff, sizeof(sig_buff), &sig_len, mbedtls_ctr_drbg_random, &ctr_drbg);

    if(ret != 0)
    {
        ret = -1;
        goto exit;
    }

    b64_out = malloc(sig_len*2);
    if(b64_out == NULL)
    {
        ret = -1;
        goto exit;
    }
 
    // 对签名数据进行 base64 编码
    ret = mbedtls_base64_encode((unsigned char *)b64_out, sig_len*2,
                                (size_t *)&b64_len, (unsigned char *)sig_buff, (size_t)sig_len);

    if(ret != 0)
    {
        ret = -1;
        goto exit;
    }

    if(sign_len<b64_len)
    {
        ret = -1;
        goto exit;
    }

    strncpy(sign_base64, b64_out, sign_len);

exit:

    if(b64_out)
    {
        free(b64_out);
    }

    mbedtls_pk_free( &pk );
    mbedtls_ctr_drbg_free( &ctr_drbg );
    mbedtls_entropy_free( &entropy );

    return ret;

}
/**
 * @brief rsa_pkcs1v15_sha256_verify
 * 
 * @param [in] msg
 * @param [in] msg_len
 * @param [in] public_key_pem
 * @param [in] sign_base64
 * @return int 
 *  -- 0  verify pass
 *  -- -1 verify faild
 */
int rsa_pkcs1v15_sha256_verify(const unsigned char *msg, size_t msg_len,
                               const char *public_key_pem, const char *sign_base64)
{
    mbedtls_pk_context pk = {0};
    unsigned char hash[32] = {0};
    int ret = 0;
    size_t sign_len = 0;
    size_t b64out_len = 0;
    unsigned char *b64out_data = NULL;

    // 初始化上下文
    mbedtls_pk_init( &pk);

    // 导入公钥
    ret = mbedtls_pk_parse_public_key(&pk, (const unsigned char *)public_key_pem, strlen(public_key_pem)+1);
    if(ret != 0)
    {
        ret = -1;
        goto exit;
    }

    // 对需要验签的数据进行 sha256 计算,生成消息摘要数据
    ret = mbedtls_md(mbedtls_md_info_from_type( MBEDTLS_MD_SHA256 ),
                     (const unsigned char *)msg, msg_len, hash);
    if(ret != 0)
    {
        ret = -1;
        goto exit;
    }

    // 对原始签名数据进行 base64 解码
    sign_len = strlen(sign_base64);
    b64out_data = malloc(sign_len*2);
    memset(b64out_data, 0, sign_len*2);
    ret = mbedtls_base64_decode(b64out_data, sign_len*2, &b64out_len, (const unsigned char *)sign_base64, sign_len);
    if(ret != 0)
    {
        ret = -1;
        goto exit;
    }

    // 验证签名

    ret = mbedtls_pk_verify(&pk, MBEDTLS_MD_SHA256, hash, sizeof (hash), b64out_data, b64out_len);


exit:
    if(b64out_data)
    {
        free(b64out_data);
    }
    mbedtls_pk_free( &pk );

    return ret;

}

static void test_rsa_pkcs1_sign(void)
{

    int ret = 0;

    char *private_key = "-----BEGIN RSA PRIVATE KEY-----\n"
                        "MIICXQIBAAKBgQDTt8tp4xNp29CMxy6QS0NzpR6t8bAcv7ei3NkVM/Nzg3K5wWZR\n"
                        "aBTMovbzKCXdXYdC6GutVkG+CEetO3XHM4LhDqW0vwISTO65/XrvR3zqXD5ZjrJF\n"
                        "mtCAvkCwtMAPjqXZ/RJnd8yrXuoz5cRqVgKmq5TZlGIIiTPIklxGIGof8QIDAQAB\n"
                        "AoGAFf1BJoiD5+sBdFmsq6ZxhUWZU+ImEzpTUZpD/riEWNNGe2YLoTlg7acgZH1f\n"
                        "P2hbJ9cZdemfTuQvw52JHE0sktCUM6R0wq5rlbDj740+5yZYzs9FlUntm6UtoU9w\n"
                        "tpd62/iPxovFkguunJB2KBbtP8q0dYQntATEce1TZuS3trUCQQDl7VRYygSb3/HY\n"
                        "ij2ya1592WpgNWgmPvbpmUjGGBvjmnO8Ye1lEy6x69RmGjRrLvFfhWYwcF2HpmYQ\n"
                        "9wXKEwT1AkEA67nc/CdeT4j9jRE/QFXlhVrW8Gq8IfjXFGbGK5BqlTRbty3OpW+L\n"
                        "M9GPqiMC2XxN60peEiANlQ8aUnvbHZexjQJAcz4RGK+ov7fvL+maIuNN6SYf+zjJ\n"
                        "iuHkQBFkOGW9FMdFWxZ6Nj73GJZrTwGzZEWTFZ13KrAnMOZmIfquHCqMQQJBAL+u\n"
                        "x9ATg1FRqDyKBdEfCCDEmXuuj4VggCUK3aKXMNRbWyk9iohkh+F/Sz+icLLBreri\n"
                        "8lPy1JidS14/cRJDRBECQQCT4oNvmV5CYzqkqbgwtLPi/FIjc6Zi26DGxBzL01V+\n"
                        "yTO1ZlOOUOtY4dPBnU4COkdq6hWqum/Q6kiVj91qAUHN\n"
                        "-----END RSA PRIVATE KEY-----";
    char *msg = "A message for signing";

    char sign[1024] = {0};

    ret = rsa_pkcs1v15_sha256_sign((const unsigned char *)msg, strlen(msg), private_key, sign, sizeof (sign));


    printf("rsa_pkcs1v15_sha256_sign ret=%d\r\n", ret);

    if(ret == 0)
    {
        printf("sign:%s\r\n", sign);
    }

}

 int main(void)
{

    int ret = 0;
 // 公钥
    char *pub_key = "-----BEGIN PUBLIC KEY-----\n"
                    "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDTt8tp4xNp29CMxy6QS0NzpR6t\n"
                    "8bAcv7ei3NkVM/Nzg3K5wWZRaBTMovbzKCXdXYdC6GutVkG+CEetO3XHM4LhDqW0\n"
                    "vwISTO65/XrvR3zqXD5ZjrJFmtCAvkCwtMAPjqXZ/RJnd8yrXuoz5cRqVgKmq5TZ\n"
                    "lGIIiTPIklxGIGof8QIDAQAB\n"
                    "-----END PUBLIC KEY-----";
    // 原始消息
    char *msg = "A message for signing";
 
    // base64 编码之后的签名数据
    char * sign = "KYiZF/C18O3wgCZvDptfM8Vh/OPMrcAf6ne9eszSuxgGMK57cKCQuWc33JF8iQmKWrSo"
                  "+ezzkPJIfXGTj3z3Js9vv1DC2tX3oBh9CdZF+yc5MqZAT5LEEqmwNKWiT4iNwwnbXiJt"
                  "NSy8/T2PRRN0PBy/TZn3HKc1AMKMYMLUjf8=";

    ret = rsa_pkcs1v15_sha256_verify((const unsigned char *)msg, strlen(msg), pub_key, sign);


    printf("rsa_pkcs1v15_sha256_verify ret=%d\r\n", ret);
	

    test_rsa_pkcs1_sign();
	return 0;

}
相关推荐
佛州小李哥5 天前
宇宙第一大厂亚马逊云科技AWS人工智能/机器学习证书即将上线,一篇文章教你轻松拿下
人工智能·ai·云计算·证书·职场·aws·亚马逊云科技
半斗米5 天前
VSCode + GDB + J-Link 单片机程序调试实践
vscode·单片机·mcu·gdb·嵌入式系统·调试·j-link
worthsen7 天前
嵌入式 Linux 设备刷系统具体组成
linux·嵌入式系统
我想学LINUX10 天前
嵌入式期末复习模拟题——模拟题2
linux·嵌入式硬件·嵌入式系统·上机作业
一屁小肥咩21 天前
前端RSA加解密(支持超长分段)
前端·jsencrypt·rsa
yuzhongrun24 天前
编码与加密(对称加密与非对称加密)
非对称加密·aes·编码·md5·对称加密·bcb
任磊abc1 个月前
前端加密的方式汇总
前端·哈希算法·base64·非对称加密·md5·对称加密·前端加密
极术社区1 个月前
【XR806开发板试用】基础篇,从零开始搭建一个LCD彩屏时钟(ST7735S驱动)
嵌入式系统·xr806·星辰
Tmac自留1 个月前
内网安全之证书模版的管理
安全·证书·内网安全