I'm trying to generate a RSA key pair in C with the following function:
int generate_key(const int bits, char* public_key_name, char* private_key_name){
int ret = 0;
RSA *r = NULL;
BIGNUM *bne = NULL;
FILE *bp_public = NULL;
FILE* bp_private = NULL;
unsigned long e = RSA_F4;
// 1. generate rsa key
bne = BN_secure_new();
ret = BN_set_word(bne,e);
if(ret != 1){
goto free_all;
}
r = RSA_new();
ret = RSA_generate_key_ex(r, bits, bne, NULL);
if(ret != 1){
goto free_all;
}
// 2. save public key
bp_public = fopen(public_key_name, "w+");
ret = PEM_write_RSAPublicKey(bp_public, r);
fclose(bp_public);
if(ret != 1){
goto free_all;
}
// 3. save private key
bp_private = fopen(private_key_name, "w+");
ret = PEM_write_RSAPrivateKey(bp_private, r, NULL, NULL, 0, NULL, NULL);
fclose(bp_private);
// 4. free
free_all:
RSA_free(r);
BN_clear_free(bne);
return ret;
}
It works fine except for the fact that when I used the public key to verify a signed message an error pops out (error:0909006C:PEM routines:get_name:no
) as result of EVP_VerifyFinal
. Notice that, weird thing, the returning value of EVP_VerifyFinal
is not 0
but -1
: if it was 0
then It would be unrefutably a signature verification failure but it is not, it happens some other thing that I could not figure out. In the following I past the code used to sign and verify.
void sign(const unsigned char* const restrict clear_buf, const unsigned long clear_size, unsigned char* restrict* const restrict sgnt_buf, unsigned long* const restrict sgnt_size, const char* const restrict prvkey_file_name){
const EVP_MD* md;
EVP_MD_CTX* md_ctx;
int ret;
FILE* prvkey_file;
EVP_PKEY* prvkey;
unsigned expected_sgn_size;
unsigned tmp;
if(clear_size > INT_MAX){
fprintf(stderr, "Buffer to sign too big\n");
exit(1);
}
if((prvkey_file = fopen(prvkey_file_name, "r")) == NULL){
fprintf(stderr, "Error: cannot open file '%s' (missing?)\n", prvkey_file_name);
exit(1);
}
prvkey = PEM_read_PrivateKey(prvkey_file, NULL, NULL, NULL);
fclose(prvkey_file);
expected_sgn_size = (unsigned) EVP_PKEY_size(prvkey);
if((*sgnt_buf = (unsigned char*)malloc((size_t)expected_sgn_size)) == NULL){
fprintf(stderr, "Error in allocating memory for signature. Error: %s\n", strerror(errno));
exit(1);
}
// create the signature context:
md = EVP_sha256();
md_ctx = EVP_MD_CTX_new();
if(!md_ctx){
fprintf(stderr, "Error: EVP_MD_CTX_new returned NULL\n");
exit(1);
}
if(EVP_SignInit(md_ctx, md) == 0){
fprintf(stderr, "Error: EVP_SignInit returned %d\n", ret);
exit(1);
}
if(EVP_SignUpdate(md_ctx, clear_buf, (unsigned)clear_size) == 0){
fprintf(stderr, "Error: EVP_SignUpdate returned %d\n", ret);
exit(1);
}
if(EVP_SignFinal(md_ctx, *sgnt_buf, &tmp, prvkey) == 0){
fprintf(stderr, "Error: EVP_SignFinal returned %d\n", ret);
exit(1);
}
if(tmp < expected_sgn_size){
fprintf(stderr, "Error in signing, signature size does not match expected size\n");
exit(1);
}
*sgnt_size = (unsigned long)tmp;
// delete the digest and the private key from memory:
EVP_MD_CTX_free(md_ctx);
EVP_PKEY_free(prvkey);
}
void verify(const unsigned char* const restrict file_buf, unsigned long* const restrict file_size, const char* const restrict pubkey_file_name){
// declare some useful variables:
int ret;
FILE* pubkey_file;
EVP_PKEY* pubkey;
unsigned char* sgnt_buf;
unsigned sgnt_size;
pubkey_file = fopen(pubkey_file_name, "r");
if(!pubkey_file){
fprintf(stderr, "Error: cannot open file '%s' (missing?)\n", pubkey_file_name);
exit(1);
}
pubkey = PEM_read_PUBKEY(pubkey_file, NULL, NULL, NULL);
sgnt_size = (unsigned) EVP_PKEY_size(pubkey);
fclose(pubkey_file);
const EVP_MD* md = EVP_sha256();
EVP_MD_CTX* md_ctx;
*file_size -= (unsigned long)sgnt_size;
if((sgnt_buf = (unsigned char*)malloc((size_t)sgnt_size)) == NULL){
fprintf(stderr, "Error in allocating memory for signature. Error: %s\n", strerror(errno));
exit(1);
}
memcpy((void*)sgnt_buf, (void*)(file_buf + *file_size), (size_t)sgnt_size);
// create the signature context:
md_ctx = EVP_MD_CTX_new();
if(!md_ctx){
fprintf(stderr, "Error: EVP_MD_CTX_new returned NULL\n");
exit(1);
}
if(EVP_VerifyInit(md_ctx, md) == 0){
fprintf(stderr, "Error in EVP_VerifyInit\n");
exit(1);
}
if(EVP_VerifyUpdate(md_ctx, file_buf, *file_size) == 0){
fprintf(stderr, "Error in EVP_VerifyUpdate\n");
exit(1);
}
ret = EVP_VerifyFinal(md_ctx, sgnt_buf, sgnt_size, pubkey);
if(ret == 0){ // it is 0 if invalid signature, -1 if some other error, 1 if success.
fprintf(stderr, "Error: EVP_VerifyFinal failed: invalid signature\n");
exit(1);
} else if(ret == -1){
fprintf(stderr, "Some error occured during signature verification\n");
exit(1);
}else if (ret == 1){
// fprintf(stdout, "Signature verified\n");
}else{
fprintf(stderr, "I shouldn't be printed. EVP_VerifyFinal returned %d\n", ret);
exit(1);
}
EVP_MD_CTX_free(md_ctx);
EVP_PKEY_free(pubkey);
free(sgnt_buf);
}
On the web I found some solution but either they are not in C, or/and they are deprecated don't solve the problem (one solution that I found here generates a private key that it doesn't even sign for some reason...)