天天看點

ESP-C3入門19. RSA算法加密、解密的實作

作者:程式設計圈子
ESP-C3入門19. RSA算法加密、解密的實作

一、概述

官方文檔位址: https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/migration-guides/release-5.x/5.0/protocols.html?highlight=mbed

ESP-IDFv5裡內建了 Mbed TLSv3.1.0版本。

實作RSA加密與解密主要是以下步驟:

  1. 生成RSA密鑰對:RSA加密算法需要一個公鑰和一個私鑰。在ESP-IDF提供的加密庫中有生成RSA密鑰對的函數。本文重點放在加密、解密,生成RSA密鑰對的過程本文不作介紹。
  2. 加密資料:要加密資料,需要使用公鑰對資料進行加密。我們使用mbedtls_rsa_pkcs1_encrypt()函數來實作RSA加密。這個函數接受明文資料、公鑰和輸出緩沖區作為參數,并将加密後的資料存儲在輸出緩沖區中。
  3. 解密資料:要解密資料,需要使用私鑰對加密資料進行解密。我們使用mbedtls_rsa_pkcs1_decrypt()函數來實作RSA解密。這個函數接受加密的資料、私鑰和輸出緩沖區作為參數,并将解密後的資料存儲在輸出緩沖區中。

在實作這些步驟之前,需要在ESP-IDF項目中包含相關的頭檔案和庫,并根據項目的需要進行配置。

二、重要函數

1. mbedtls_pk_parse_public_key和mbedtls_pk_parse_key

以 mbedtls_pk_parse_public_key為例:

函數原型:

int mbedtls_pk_parse_public_key(mbedtls_pk_context *ctx, const unsigned char *key, size_t keylen);           

該函數用于解析給定的公鑰資料,将其加載到mbedtls_pk_context結構體中。

函數參數:

  • ctx:指向要填充的mbedtls_pk_context結構體的指針。
  • key:指向包含公鑰資料的緩沖區的指針。
  • keylen:公鑰資料的長度。

該函數将從key緩沖區中解析并填充mbedtls_pk_context結構體,以便後續可以使用該結構體進行公鑰相關操作,如加密或驗證。

2. mbedtls_pk_encrypt

函數原型:

int mbedtls_pk_encrypt(mbedtls_pk_context *ctx, const unsigned char *input, size_t ilen, unsigned char *output, size_t *olen, size_t osize, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng);           

函數參數:

  • ctx:指向包含公鑰的mbedtls_pk_context結構體的指針。
  • input:指向要加密的輸入資料的緩沖區的指針。
  • ilen:要加密的輸入資料的長度。
  • output:指向用于存儲加密結果的輸出緩沖區的指針。
  • olen:指向size_t類型的變量的指針,用于輸入輸出緩沖區的長度。加密後,olen将包含實際加密資料的長度。
  • osize:輸出緩沖區的大小。如果輸出緩沖區不足以容納加密結果,将傳回MBEDTLS_ERR_RSA_BUFFER_TOO_SMALL錯誤。
  • f_rng:指向随機數生成函數的指針,用于生成加密過程中所需的随機數。可以使用預設的mbedtls_ctr_drbg_random作為參數。
  • p_rng:指向随機數生成函數上下文的指針。可以使用預設的mbedtls_ctr_drbg_context上下文作為參數。

函數功能:

  • mbedtls_pk_encrypt()函數使用指定的公鑰對輸入資料進行加密,并将加密結果存儲在輸出緩沖區中。
  • 在加密過程中,函數使用給定的随機數生成函數生成必要的随機數。
  • 函數傳回值為0表示加密成功,非零值表示加密過程中發生錯誤。
  • 使用mbedtls_pk_encrypt()函數時,需要確定已經成功解析了有效的公鑰并填充了mbedtls_pk_context結構體。

三、實作過程

1. CMakeLists.txt 引用Mbed TLS

# 遞歸查找所有c檔案
file(GLOB_RECURSE SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.c")
idf_component_register(SRCS ${SOURCES}
                       INCLUDE_DIRS "." 
        REQUIRES   "nvs_flash" "esp_http_client" "esp-tls" "json" "driver" "esp_event" "esp_wifi"

)           

重點是其中的esp-tls

2. rsa_utils.h 頭檔案定義

//
// 
//

#ifndef WIFI_ESP32_RSA_UTILS_H
#define WIFI_ESP32_RSA_UTILS_H




/**
 * @brief RSA公鑰加密
 *
 * @param public_key_pem 公鑰PEM字元串
 * @param plaintext 待加密的資料
 * @param plaintext_len 待加密資料的長度
 * @param ciphertext 加密後的資料緩沖區
 * @param ciphertext_len 加密後的資料長度緩沖區
 * @return int 加密結果長度,失敗傳回負數
 */
int rsa_public_encrypt(const unsigned char* public_key_pem, const unsigned char* plaintext,
                       size_t plaintext_len, unsigned char* ciphertext, size_t* ciphertext_len);

/**
 * @brief RSA私鑰解密
 *
 * @param private_key_pem 私鑰PEM字元串
 * @param ciphertext 待解密的資料
 * @param ciphertext_len 待解密資料的長度
 * @param plaintext 解密後的資料緩沖區
 * @param plaintext_len 解密後的資料長度緩沖區
 * @return int 解密結果長度,失敗傳回負數
 */
int rsa_private_decrypt(const unsigned char* private_key_pem, const unsigned char* ciphertext,
                        size_t ciphertext_len, unsigned char* plaintext, size_t* plaintext_len);


#endif //WIFI_ESP32_RSA_UTILS_H           

3. rsa_utils.c 函數體

//
// Created by Xundh on 2023/6/19.
//
#include <mbedtls/pk.h>
#include <mbedtls/ctr_drbg.h>
#include <string.h>
#include <esp_log.h>
#include <mbedtls/entropy.h>
#include <mbedtls/error.h>
#include "include/rsa_utils.h"

const char *TAG = "[rsa_utils]";

int rsa_public_encrypt(const unsigned char *public_key_pem, const unsigned char *plaintext,
                       size_t plaintext_len, unsigned char *ciphertext, size_t *ciphertext_len) {

    int ret;

    mbedtls_ctr_drbg_context ctr_drbg;
    mbedtls_entropy_context entropy;

    mbedtls_ctr_drbg_init(&ctr_drbg);
    mbedtls_entropy_init(&entropy);

    if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0) != 0) {
        // Handle error
        ESP_LOGI(TAG, "random init error");
        ret = -1;
    } else {
        mbedtls_pk_context pk;
        mbedtls_pk_init(&pk);
        size_t keylen = strlen((const char *) public_key_pem);
        // 解析公鑰
        ret = mbedtls_pk_parse_public_key(&pk, public_key_pem, keylen + 1);
        if (ret != 0) {
            ESP_LOGI(TAG, "pass public key error");
        } else {
            ESP_LOGI(TAG, "pass public key success");

            // 執行RSA加密
            ret = mbedtls_pk_encrypt(&pk, plaintext, plaintext_len, ciphertext, ciphertext_len,
                                     1024, mbedtls_ctr_drbg_random, &ctr_drbg);
            if (ret != 0) {
                char error_buf[100];
                mbedtls_strerror(ret, error_buf, sizeof(error_buf));
                ESP_LOGI(TAG, "rsa public encrypt error:%s", error_buf);
            }
        }
        mbedtls_pk_free(&pk);
    }
    mbedtls_ctr_drbg_free(&ctr_drbg);
    mbedtls_entropy_free(&entropy);
    return ret;
}

int rsa_private_decrypt(const unsigned char *private_key_pem, const unsigned char *ciphertext,
                        size_t ciphertext_len, unsigned char *plaintext, size_t *plaintext_len) {
    int ret;

    mbedtls_ctr_drbg_context ctr_drbg;
    mbedtls_entropy_context entropy;

    mbedtls_ctr_drbg_init(&ctr_drbg);
    mbedtls_entropy_init(&entropy);

    if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0) != 0) {
        // Handle error
        ESP_LOGI(TAG, "random init error");
        ret = -1;
    } else {
        mbedtls_pk_context pk;
        mbedtls_pk_init(&pk);
        size_t keylen = strlen((char *) private_key_pem);
        // 解析私鑰
        ret = mbedtls_pk_parse_key(&pk, private_key_pem, keylen+1, NULL, 0, NULL, NULL);

        if (ret != 0) {
            ESP_LOGI(TAG, "pass private key error");
        } else {
            ESP_LOGI(TAG, "pass private key success");
            // 執行RSA解密
            ret = mbedtls_pk_decrypt(&pk, ciphertext, ciphertext_len, plaintext, plaintext_len,
                                     512, mbedtls_ctr_drbg_random, &ctr_drbg);
            if (ret != 0) {
                char error_buf[100];
                mbedtls_strerror(ret, error_buf, sizeof(error_buf));
                ESP_LOGI(TAG, "rsa public decrypt error:%s", error_buf);
            }else{
                ESP_LOG_BUFFER_HEXDUMP(TAG, plaintext, *plaintext_len,ESP_LOG_INFO);
            }
        }
        mbedtls_pk_free(&pk);
    }
    mbedtls_ctr_drbg_free(&ctr_drbg);
    mbedtls_entropy_free(&entropy);
    return ret;
}           

4. 使用demo

static const char *TAG = "[main]";


void app_main() {
    // 公鑰加密,私鑰解密
    const unsigned char *public_key_pem = (const unsigned char *) "-----BEGIN PUBLIC KEY-----\n"
                                                                  "放置一個公鑰"
                                                                  "-----END PUBLIC KEY-----\n";

    const unsigned char *private_key_pem = (const unsigned char *) "-----BEGIN PRIVATE KEY-----\n"
                                                                   "放置一個私鑰"
                                                                   "-----END PRIVATE KEY-----\n";

    int ret;
    mbedtls_platform_setup(NULL);
    // 加密和解密的資料
    const unsigned char *plaintext = (const unsigned char *) "Hello, RSA!";
    size_t plaintext_len = strlen((const char *) plaintext);

    unsigned char encrypted[256];
    size_t encrypted_len = 0;

    ret = rsa_public_encrypt(public_key_pem, plaintext, plaintext_len, encrypted, &encrypted_len);

    if (ret != 0) {
        ESP_LOGI(TAG, "mbedtls_pk_encrypt error ");
    } else {
        ESP_LOG_BUFFER_HEXDUMP(TAG, encrypted, encrypted_len, ESP_LOG_INFO);
    }

    unsigned char decrypted[256];
    size_t decrypted_len = 256;
//     // 使用私鑰解密
     ret = rsa_private_decrypt(private_key_pem, encrypted, encrypted_len, decrypted, &decrypted_len);
     if (ret < 0) {
         ESP_LOGI(TAG, "Failed to decrypt data.");
     }
    // 清理Mbed TLS庫
    mbedtls_platform_teardown(NULL);
}           

三、注意要點

由于單片機的棧比較小,在使用rsa時,定義的數組大小不要太大,否則容易造成棧溢出。 運作示例:

ESP-C3入門19. RSA算法加密、解密的實作

在這裡插入圖檔描述

繼續閱讀