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

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

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;

}
相关推荐
怒放de生命20105 天前
cerbot 实现通配符子域名证书+续期
证书·ssl·通配符·cerbot
Yaml47 天前
Spring Boot 安全 API 构建:加密解密功能的卓越实践
服务器·网络·spring boot·安全·aes·rsa
许野平8 天前
OpenSSL:生成 DER 格式的 RSA 密钥对
服务器·网络·openssl·rsa·pem·der
阿华的代码王国9 天前
【网络原理】——图解HTTPS如何加密(通俗简单易懂)
网络协议·http·https·非对称加密·对称加密·htttps加密传输·证书加密
SuperHeroWu713 天前
华为应用市场增长优化(一)
华为·证书·harmonyos·鸿蒙·应用市场增长优化
椰椰椰耶2 个月前
【HTTPS】中间人攻击和证书的验证
https·证书
常敲代码手不生2 个月前
开放API接口时要注意的安全处理总结
java·安全·非对称加密·md5·令牌
一丝晨光2 个月前
Https 加密原理
开发语言·网络·网络协议·http·https·加密·rsa
bobo-rs2 个月前
Go基于crypto库实现AES封装加密以及协同PHP8 使用openssl AES加密使用
开发语言·golang·php·php8·aes·对称加密
ChampionDragon2 个月前
汽车一些身份认证技术术语
证书·身份认证·ca