一次最多只能加密 21 个字节的输入

问题描述 投票:0回答:1

我正在尝试使用 mbedtls 库对使用 Arduino 框架和 PlatformIO 的 esp32 上的 PK API 进行一些 RSA 加密。我一次可以成功加密 20 个字节(即 inputArray[21])。但不仅如此,我还收到了 0x4080 错误代码 RSA - 错误的输入参数无法运行

根据他们的文档,我应该能够(假设我使用的是 2048 位密钥)一次至少加密 200 个字节。

运行代码时的命令提示符:

___________________PROGRAM_START_________________________
Seeding the random number generator...
Initializing key context...
Generating the private key...
ABCDEFGHIJKLMNOPQRSTUV
Error encrypting
-16512
��������@�������������
���������������������

这是我的 RSACryptographer 对象:

//
// Created by DripTooHard on 15-04-2023.
//

#include "Cryptographer.h"

class Cryptographer{
protected:
    mbedtls_ctr_drbg_context CTR_ctx;

public:
    Cryptographer(){
    }

    virtual ~Cryptographer(){}

    //TODO: Add PEMformatter

    virtual int encrypt(unsigned char * inputArray, size_t inputLen, unsigned char * outputArray, size_t outSize, size_t * outLen) = 0;
    virtual int decrypt(unsigned char * inputArray, size_t inputLen, unsigned char * outputArray,size_t outSize, size_t * outLen) = 0;
    virtual int generate_key() = 0;
    virtual int validate_key() = 0;

    int generate_CTRX_context(){
        mbedtls_ctr_drbg_free(&CTR_ctx);
        mbedtls_entropy_context entropy; //Used to seed the drbg

        mbedtls_entropy_init(&entropy);
        mbedtls_ctr_drbg_init(&this->CTR_ctx);


        Serial.println("Seeding the random number generator...");
        int ret = (mbedtls_ctr_drbg_seed(&this->CTR_ctx, mbedtls_entropy_func, &entropy, NULL, 0)) != 0;
        if (ret)
        {
            Serial.print("Error in seeding the random number generator\nmbedtls_ctr_drbg_seed returned: ");
            Serial.print(ret);
            return RSABooleanFalse;
            //ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned %d", ret);
        }

        return 0;
    };

    mbedtls_ctr_drbg_context get_CTRX_context(){
        return CTR_ctx;
    }

};

class RSACryptographer : public Cryptographer{

protected:
    mbedtls_pk_context RSA_ctx;

public:
    RSACryptographer()
    {

    }
    ~RSACryptographer(
            ){}


    /**
     *
     * @param inputArray : The array which we would like to do our encryption/decryption on
     * @param inputLen
     * @param outputArray
     * @param outLen : The length of the result (not the size of the outputArray)
     * @param isEncryption : 0 if we're decrypting, 1 if we're encrypting
     * @return 0 if succesfull, otherwise a specified error code
     */
    int use_key(unsigned char * inputArray, size_t inputLen, unsigned char * outputArray,size_t outSize, size_t * outLen, int isEncryption){
        int res;

        //The input is larger than what our encryption algorithm can handle
        if(inputLen>RSA_MAX_INPUT_LEN){
            Serial.print("ERROR: INPUT: \n");
            println_unsignedString(inputArray,inputLen,CHR);
            Serial.print("EXCEEDS MAX ENCRYPTION INPUT LEN: ");
            Serial.print(RSA_MAX_INPUT_LEN);
            return RSA_ERR_INPUT_EXCEEDS_MAX_LEN;
        }

        if(isEncryption) {
            res = mbedtls_pk_encrypt(&this->RSA_ctx, inputArray, inputLen,  outputArray, outLen,
                                     outSize, mbedtls_ctr_drbg_random, &this->CTR_ctx);
        }
        else{
            res =  mbedtls_pk_decrypt(&this->RSA_ctx, inputArray,inputLen, outputArray, outLen, outSize ,mbedtls_ctr_drbg_random,&this->CTR_ctx);
        }

        //If our output is larger than the array
        if(*outLen>outSize){
            Serial.println("ERROR: Output length exceeds size of the outputArray");
            Serial.println(*outLen);
            Serial.println(outSize);
            return RSA_ERR_OUTPUT_EXCEEDS_OUTPUT_ARRAY_LEN;
        }

        return res;

    }

    int encrypt(unsigned char * inputArray, size_t inputLen, unsigned char * outputArray,size_t outSize, size_t * outLen){


        return use_key(inputArray, inputLen, outputArray, outSize, outLen, 1);

    }

    int decrypt(unsigned char * inputArray, size_t inputLen, unsigned char * outputArray,size_t outSize, size_t * outLen) {

        return use_key(inputArray, inputLen, outputArray, outSize, outLen, 0);

    }

    int generate_key(){
        mbedtls_pk_init(&RSA_ctx);


        int ret = mbedtls_pk_setup(&RSA_ctx, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA));
        Serial.println("Initializing key context...");
        ESP_LOGI(TAG_MAIN, "Initializing key context...");
        if (ret != 0)
        {
            ESP_LOGE(TAG_WORDS, "mbedtls_pk_info_from_type returned %d", ret);
            return RSABooleanFalse;
        }

        Serial.println("Generating the private key...");
        ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(RSA_ctx), mbedtls_ctr_drbg_random, &CTR_ctx, pubKeyLen, RSAPubKeyEXPONENT);
        if (ret != 0)
        {
            ESP_LOGE(TAG_WORDS, "mbedtls_rsa_gen_key returned %d", ret);
            return RSABooleanFalse;
        }

        return 0;
    }

    int validate_key(){
        return mbedtls_pk_check_pair(&this->RSA_ctx,&this->RSA_ctx);
    }

    mbedtls_pk_context get_RSA_context(){
        return this->RSA_ctx;
    }

};

我正在运行/主要的代码:

#include <esp_spiffs.h>
#include "Arduino.h"
#include "Cryptography/CryptographicSettings.cpp"
#include "Cryptography/RSAEncryption.h"
#include "esp_dsp.h"
#include "SPIFFS.h"
#include "Flash/CustomSPIFFS.h"
#include "Flash/FileManager.cpp"
#include "Flash/FileManager.h"
#include "FS.h"
#include "sha/sha_parallel_engine.h"
#include "TinyGPSPlus-master/src/TinyGPS++.h"
#include "stddef.h"
#include "Utility.h"
#include "Cryptography/Cryptographer.h"
#include "Cryptography/Cryptographer.cpp"

#include "HardwareMacros.h"


static const char* TAG_main = "Main";


const char * RSAPubKeyPath = "/RSAPubKey";
const char * RSAPrivKeyPath = "RSAPrivKey";
unsigned char PEMKeyBuffer[256];
unsigned char pubKey[pubKeyLen];
unsigned char a[2] = "a";
unsigned char outputBuf[SHA256_OUTPUT_BUFFERLEN];
unsigned char ID[IDLen];
unsigned char outputBufDecrypt[SHA256_OUTPUT_BUFFERLEN];


/**
 * Finds the size of an RSA key formatted in PEM format
 *
 * \arg PEMBUFFER: An unsigned char containing the PEM file in the format
 *                  {PEM_EMPTY_PLACEHOLDER,...,PEM_EMPTY_PLACEHOLDER,PEMFILE....,\0}
 *                  Where PEM_EMPTY_PLACEHOLDER indicates that the value is not a part of the actual PEM file.
 *
 *                  Notice, that there can be nothing inbetween the start of the PEMFILE and the end of the array.
 *
 * \return
 *          The size of the PEMFILE or PEM_ERR_NO_PEM_FILE if there is no PEM file
 */

int findStartingIndexPEMFile(unsigned char * PEMBuffer,size_t sizeOfBuffer){
    for(int i = 0; i<sizeOfBuffer;i++){
        unsigned char c = PEMBuffer[i];

        //We've reached the end of the array without encountering anything but PEM_EMPTY_PLACEHOLDERS
        if(c == '\0'){
            return PEM_ERR_NO_PEM_FILE;
        }

        if(c != PEM_EMPTY_PLACEHOLDER){
            return i;
        }

    }

    return PEM_ERR_NO_PEM_FILE;
}


void setup(){
    Serial.begin(9600);



    int res = 0;

    Serial.println("___________________PROGRAM_START_________________________"); //We get a lot of fluffer at the start of the program


    //SPIFFS
    FileManager * spiff = new SPIFFSFileManager();


    auto * rsaCryptographer = new RSACryptographer();

    if(rsaCryptographer->generate_CTRX_context() != 0){
        Serial.println("Error generating CTR");
    }
    if(rsaCryptographer->generate_key() != 0){
        Serial.println("Error");
    }

    if(rsaCryptographer->validate_key() != 0){
        Serial.println("Error");
    }
    unsigned char inputArray[22];
    unsigned char outputArray[4000];
    size_t oLen;

    fill_alphanumeric_unsignedString(inputArray,sizeof(inputArray));
    println_unsignedString(inputArray,sizeof(inputArray),CHR);

    res = rsaCryptographer->encrypt(inputArray,sizeof(inputArray),outputArray,sizeof(outputArray),&oLen);
    if(res != 0){
        Serial.println("Error encrypting");
        Serial.println(res);
    }


    println_unsignedString(outputArray,sizeof(inputArray),CHR);


    //fill_alphanumeric_unsignedString(outputArray,sizeof(outputArray));
    rsaCryptographer->decrypt(outputArray,oLen,outputArray,sizeof(outputArray),&oLen);

    //mbedtls_pk_encrypt(&key,(const unsigned char *)inputArray,sizeof(inputArray),outputArray,&oLen,sizeof(outputArray),mbedtls_ctr_drbg_random,&ctr_drbg);
    println_unsignedString(outputArray,oLen,CHR);




};

void loop(){

};

加密设置文件:

//
// Created by DripTooHard on 03-04-2023.
//

//Truth values for our RSA functions
//They're defined as such, because the mbedtls libraries define 0 as success
#define RSABooleanTrue 0
#define RSABooleanFalse 1

//RSA related
#define RSAPubKeyEXPONENT 65537
#define pubKeyLen 256 //2048 bits
#define RSA_MAX_INPUT_LEN 245 //245 bytes

//RSA related errors
#define RSA_ERR_INPUT_EXCEEDS_MAX_LEN -69
#define RSA_ERR_OUTPUT_EXCEEDS_OUTPUT_ARRAY_LEN -68

//Identification/Network related
#define IDLen 20 //How many of the chars we take from the hash

//If we iterate over a PEM file and it's "7"
#define PEM_EMPTY_PLACEHOLDER 7
#define PEM_ERR_NO_PEM_FILE -69

//Hashing related
#define SHA256_OUTPUT_BUFFERLEN 32 //256 bits

附言。这是一些混乱的代码,因为我们只是一次构建一个对象。无需帮助我构建代码或链接 .cpp 文件等混乱的东西;)

起初我以为这是输出数组大小的问题,但在设置为 4k 字节后,我相当确定,一定不是这样。除非我使用一些非常奇怪的填充方案。

cryptography rsa esp32 platformio mbedtls
1个回答
0
投票

您定义一个常量,它是用密钥制作的签名和密文的大小,以字节为单位。

#define pubKeyLen 256 //2048 bits

但是然后您将该常量传递给一个函数,该函数期望密钥的大小作为加密(数学)对象。那是 bits.

    ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(RSA_ctx), mbedtls_ctr_drbg_random, &CTR_ctx, pubKeyLen, RSAPubKeyEXPONENT);

所以你正在创建一个 256 位密钥。这对于 RSA 密钥来说太小了:如今 2048 位是绝对推荐的最小值,即使在 RSA 刚刚开始且计算机功能不那么强大的古代,321 位 RSA 也被认为太短。但它在数学上是有效的,不幸的是 mbedtls 让你搬起石头砸自己的脚。

通常,内存缓冲区的大小以字节表示,但数字和其他数学对象的大小以位表示——数组的大小以元素表示。在处理密码学时,“密钥大小”等概念通常以位表示,因为它们是某些基础数字的大小。对于 RSA,该数字是公共模数 (n),它是构成公钥的两个数字之一。

作为一般的编程建议,确保变量名称清楚地表达单位。比如这个常量不要叫

pubKeyLen
,叫它
pubKeyBytes
。或者更好,因为这实际上不是任何意义上的公钥字节数(公钥对模数和公共指数进行编码,因此对于 2048 位 RSA 密钥,它需要多于 256 个字节),称呼它实际上是什么,对你来说:
ciphertextBytes
.

const size_t ciphertextBytes = 256;
const size_t pubKeyBits = 8 * ciphertextBytes;

在低端平台上,我会推荐使用椭圆曲线密码而不是RSA来做混合加密。 ECC私钥运算比RSA快,密文更小。使用 ECC 进行混合加密的标准方法是 ECIES。 Mbed TLS 没有 ECIES API,但它可以执行 ECIES:非对称部分是 ECDH。从你的私钥和对方的公钥,你得到 ECDH 共享秘密(

mbedtls_ecdh_calc_secret
的输出),用 SHA-256 散列它(保持简单因为你只需要一个输出),然后使用前 16 个字节作为 AES 密钥。

© www.soinside.com 2019 - 2024. All rights reserved.