How to get privatekey in openssl ecc with the format hex - c++

code:
EC_KEY *key;
if (NULL == (key = EC_KEY_new_by_curve_name(NID_secp224r1)))
handleErrors();
if (1 > EC_KEY_generate_key(key)) handleErrors();
const BIGNUM *prv = EC_KEY_get0_private_key(key);
const EC_GROUP *group = EC_KEY_get0_group(key);
const EC_POINT *pub = EC_KEY_get0_public_key(key);
char* hexPubkey = EC_POINT_point2hex(group, pub, EC_KEY_get_conv_form(key), NULL);
char* hexPrikey=BN_bn2hex(prv);
my result:
hexPubkey:04EAD3AF4BA89F513B2D89FC749C43CC7B95523F1BD40A5713C5228F91B5F928D43B396C64A3293053550065C02E9A06B4FB078C4944BD0933
length:57
hexPrikey:5D5356F3551602A89710DA40CC24FB6CDBB851FE612C977C9AB0F393
length:114
correct result:
hexPubkey:04381987517AEEDB6F83FAA0EB60EFF4C1B7A78D66FF17CB04D92B37588826A64B78E0B3A965C72438860D4B3897893BB31397D1625EEA0E41
length:57
hexPrikey:30820144020101041C20F21D6B7FE0D5E1D21C6B57AFE6FAD8A5A5AC31AE0596B972E01877A081E23081DF020101302806072A8648CE3D0101021D00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000013053041CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE041CB4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4031500BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5043904B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34021D00FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D020101A13C033A0004381987517AEEDB6F83FAA0EB60EFF4C1B7A78D66FF17CB04D92B37588826A64B78E0B3A965C72438860D4B3897893BB31397D1625EEA0E41
length:328
From the length of the private key, my private key is obviously wrong, isn't it my writing method wrong?

BIO *bio = BIO_new(BIO_s_mem());
PEM_write_bio_ECPrivateKey(bio, key, NULL, NULL, 0, NULL, NULL);
int keylen = BIO_pending(bio);
char *pem_key = (char *) calloc(keylen + 1, 1);
BIO_read(bio, pem_key, keylen);
cout << pem_key << endl;
BIO_free_all(bio);
EC_KEY_free(key);
free(pem_key);

Related

EVP_KEY_sign gives signature length 0 unexpectedly

I'm trying to sign a message with a private key in C++ with openssl. I have followed an example but for some reason I can't get EVP_PKEY_sign to work because it sets the signature length to 0. Is there anything wrong with my code?
function to sign message
void sign_buff(const string& priv_key_file_path, const unsigned char* buff, int buff_len, string& sig) {
FILE* f = fopen(priv_key_file_path.c_str(), "r");
EC_KEY* ec_key = PEM_read_ECPrivateKey(f, NULL, NULL, NULL);
fclose(f);
assert(1 == EC_KEY_check_key(ec_key));
EVP_PKEY* key = EVP_PKEY_new();
assert(1 == EVP_PKEY_assign_EC_KEY(key, ec_key));
EVP_PKEY_CTX* key_ctx = EVP_PKEY_CTX_new(key, NULL);
assert(1 == EVP_PKEY_sign_init(key_ctx));
assert(1 == EVP_PKEY_CTX_set_signature_md(key_ctx, EVP_sha256()));
size_t sig_len = 0;
assert(1 == EVP_PKEY_sign(key_ctx, NULL, &sig_len, buff, buff_len));
cout << sig_len; //gives 0
sig.assign(sig_len, 0);
assert(1 == EVP_PKEY_sign(key_ctx, (unsigned char*)&sig[0], &sig_len, buff, buff_len));
EVP_PKEY_CTX_free(key_ctx);
EVP_PKEY_free(key);
}
I call the function with
string priv="priv.pem";
string sig;
unsigned char buff[] = "Random message to be signed";
sign_buff(priv, &buff[0], sizeof(buff), sig);
where a private key is stored in priv.pem

Self sign with SAN with OpenSSL in Chrome and Firefox

I created a minimal code that serves as an HTTPS server with CA created as follows:
$ openssl req -config openssl.conf -x509 -sha256 -nodes -extensions v3_ca -days 3650 -subj '/CN=OpenSSL CA/O=Example Company/C=SE' -newkey rsa:4096 -keyout ca.key -out ca.pem
I added the CA to the Trusted Certificate in Chrome and Firefox, and I added a new line to hosts file to resolve test.com as 127.0.0.1.
In Firefox it works as expected, but Chrome returns a 'NET::ERR_CERT_COMMON_NAME_INVALID
' error. It makes sense that Chrome starting version 58 no longer supports a common name and instead needs to use subjectAltName.
So I added a snippet of code (currently comment-out) to support this, then Firefox returns the error SEC_ERROR_BAD_SIGNATURE, and Chrome returns the NET::ERR_CERT_AUTHORITY_INVALID.
#include <openssl/err.h>
#include <openssl/conf.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <openssl/x509v3.h>
#define RSA_KEY_BITS (4096)
#define REQ_DN_C "SE"
#define REQ_DN_ST ""
#define REQ_DN_L ""
#define REQ_DN_O "Example Company"
#define REQ_DN_OU ""
#define REQ_DN_CN "test.com"
static void crt_to_pem(X509 *crt, uint8_t **crt_bytes, size_t *crt_size);
static int generate_key_csr(EVP_PKEY **key, X509_REQ **req);
static int generate_set_random_serial(X509 *crt);
static int generate_signed_key_pair(EVP_PKEY *ca_key, X509 *ca_crt, EVP_PKEY **key, X509 **crt);
static void key_to_pem(EVP_PKEY *key, uint8_t **key_bytes, size_t *key_size);
static int load_ca(const char *ca_key_path, EVP_PKEY **ca_key, const char *ca_crt_path, X509 **ca_crt);
static void print_bytes(uint8_t *data, size_t size);
int create_socket() {
int sockfd, portno;
char buffer[256];
struct sockaddr_in serv_addr{}, cli_addr{};
int n;
/* First call to socket() function */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}
portno = 8000;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
/* Now bind the host address using bind() call.*/
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR on binding");
exit(1);
}
listen(sockfd,5);
return sockfd;
}
SSL_CTX *create_context()
{
const SSL_METHOD *method;
SSL_CTX *ctx;
method = TLS_server_method();
ctx = SSL_CTX_new(method);
if (!ctx) {
exit(EXIT_FAILURE);
}
return ctx;
}
int main(int argc, char **argv)
{
char *ca_key_path = "ca.key";
char *ca_crt_path = "ca.pem";
/* Load CA key and cert. */
EVP_PKEY *ca_key = NULL;
X509 *ca_crt = NULL;
if (!load_ca(ca_key_path, &ca_key, ca_crt_path, &ca_crt)) {
fprintf(stderr, "Failed to load CA certificate and/or key!\n");
return 1;
}
/* Generate keypair and then print it byte-by-byte for demo purposes. */
EVP_PKEY *key = NULL;
X509 *crt = NULL;
int ret = generate_signed_key_pair(ca_key, ca_crt, &key, &crt);
if (!ret) {
fprintf(stderr, "Failed to generate key pair!\n");
return 1;
}
/*
X509_EXTENSION *cert_ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_subject_alt_name, "DNS:test.com");
X509_add_ext(crt, cert_ex, -1);
*/
/* Convert key and certificate to PEM format. */
uint8_t *key_bytes = NULL;
uint8_t *crt_bytes = NULL;
size_t key_size = 0;
size_t crt_size = 0;
key_to_pem(key, &key_bytes, &key_size);
crt_to_pem(crt, &crt_bytes, &crt_size);
/* Print key and certificate. */
print_bytes(key_bytes, key_size);
print_bytes(crt_bytes, crt_size);
auto ctx = create_context();
auto lfd = create_socket();
while (true) {
int clilen = 0;
struct sockaddr_in serv_addr{}, cli_addr{};
auto s = accept(lfd, (struct sockaddr *)&cli_addr, reinterpret_cast<socklen_t *>(&clilen));
auto ssl = SSL_new(ctx);
SSL_set_fd(ssl, s);
SSL_use_certificate(ssl, crt);
SSL_use_PrivateKey(ssl, key);
SSL_accept(ssl);
char msg_200ok[] = "HTTP/1.1 404 \n"
"Content-Length: 0\n\n";
SSL_write(ssl, msg_200ok, sizeof msg_200ok - 1);
}
/* Free stuff. */
EVP_PKEY_free(ca_key);
EVP_PKEY_free(key);
X509_free(ca_crt);
X509_free(crt);
free(key_bytes);
free(crt_bytes);
return 0;
}
void crt_to_pem(X509 *crt, uint8_t **crt_bytes, size_t *crt_size)
{
/* Convert signed certificate to PEM format. */
BIO *bio = BIO_new(BIO_s_mem());
PEM_write_bio_X509(bio, crt);
*crt_size = BIO_pending(bio);
*crt_bytes = (uint8_t *)malloc(*crt_size + 1);
BIO_read(bio, *crt_bytes, *crt_size);
BIO_free_all(bio);
}
int generate_signed_key_pair(EVP_PKEY *ca_key, X509 *ca_crt, EVP_PKEY **key, X509 **crt)
{
/* Generate the private key and corresponding CSR. */
X509_REQ *req = NULL;
if (!generate_key_csr(key, &req)) {
fprintf(stderr, "Failed to generate key and/or CSR!\n");
return 0;
}
/* Sign with the CA. */
*crt = X509_new();
X509_set_version(*crt, 2); /* Set version to X509v3 */
/* Generate random 20 byte serial. */
generate_set_random_serial(*crt);
/* Set issuer to CA's subject. */
X509_set_issuer_name(*crt, X509_get_subject_name(ca_crt));
/* Set validity of certificate to 2 years. */
X509_gmtime_adj(X509_get_notBefore(*crt), 0);
X509_gmtime_adj(X509_get_notAfter(*crt), (long)2*365*24*3600);
/* Get the request's subject and just use it (we don't bother checking it since we generated
* it ourself). Also take the request's public key. */
X509_set_subject_name(*crt, X509_REQ_get_subject_name(req));
EVP_PKEY *req_pubkey = X509_REQ_get_pubkey(req);
X509_set_pubkey(*crt, req_pubkey);
EVP_PKEY_free(req_pubkey);
/* Now perform the actual signing with the CA. */
if (X509_sign(*crt, ca_key, EVP_sha256()) == 0) goto err;
X509_REQ_free(req);
return 1;
err:
EVP_PKEY_free(*key);
X509_REQ_free(req);
X509_free(*crt);
return 0;
}
int generate_key_csr(EVP_PKEY **key, X509_REQ **req)
{
*key = NULL;
*req = NULL;
RSA *rsa = NULL;
BIGNUM *e = NULL;
*key = EVP_PKEY_new();
*req = X509_REQ_new();
rsa = RSA_new();
e = BN_new();
BN_set_word(e, 65537);
RSA_generate_key_ex(rsa, RSA_KEY_BITS, e, NULL);
EVP_PKEY_assign_RSA(*key, rsa);
X509_REQ_set_pubkey(*req, *key);
/* Set the DN of the request. */
X509_NAME *name = X509_REQ_get_subject_name(*req);
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (const unsigned char*)REQ_DN_C, -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_ASC, (const unsigned char*)REQ_DN_ST, -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "L", MBSTRING_ASC, (const unsigned char*)REQ_DN_L, -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (const unsigned char*)REQ_DN_O, -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, (const unsigned char*)REQ_DN_OU, -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (const unsigned char*)REQ_DN_CN, -1, -1, 0);
/* Self-sign the request to prove that we posses the key. */
if (!X509_REQ_sign(*req, *key, EVP_sha256())) goto err;
BN_free(e);
return 1;
err:
EVP_PKEY_free(*key);
X509_REQ_free(*req);
RSA_free(rsa);
BN_free(e);
return 0;
}
int generate_set_random_serial(X509 *crt)
{
/* Generates a 20 byte random serial number and sets in certificate. */
unsigned char serial_bytes[20];
if (RAND_bytes(serial_bytes, sizeof(serial_bytes)) != 1) return 0;
serial_bytes[0] &= 0x7f; /* Ensure positive serial! */
BIGNUM *bn = BN_new();
BN_bin2bn(serial_bytes, sizeof(serial_bytes), bn);
ASN1_INTEGER *serial = ASN1_INTEGER_new();
BN_to_ASN1_INTEGER(bn, serial);
X509_set_serialNumber(crt, serial); // Set serial.
ASN1_INTEGER_free(serial);
BN_free(bn);
return 1;
}
void key_to_pem(EVP_PKEY *key, uint8_t **key_bytes, size_t *key_size)
{
/* Convert private key to PEM format. */
BIO *bio = BIO_new(BIO_s_mem());
PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL);
*key_size = BIO_pending(bio);
*key_bytes = (uint8_t *)malloc(*key_size + 1);
BIO_read(bio, *key_bytes, *key_size);
BIO_free_all(bio);
}
int load_ca(const char *ca_key_path, EVP_PKEY **ca_key, const char *ca_crt_path, X509 **ca_crt)
{
BIO *bio = NULL;
*ca_crt = NULL;
*ca_key = NULL;
/* Load CA public key. */
bio = BIO_new(BIO_s_file());
if (!BIO_read_filename(bio, ca_crt_path)) goto err;
*ca_crt = PEM_read_bio_X509(bio, NULL, NULL, NULL);
if (!*ca_crt) goto err;
BIO_free_all(bio);
/* Load CA private key. */
bio = BIO_new(BIO_s_file());
if (!BIO_read_filename(bio, ca_key_path)) goto err;
*ca_key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
if (!ca_key) goto err;
BIO_free_all(bio);
return 1;
err:
BIO_free_all(bio);
X509_free(*ca_crt);
EVP_PKEY_free(*ca_key);
return 0;
}
void print_bytes(uint8_t *data, size_t size)
{
for (size_t i = 0; i < size; i++) {
printf("%c", data[i]);
}
}
What is the problem? How can this be debugged?
Thanks
You are trying to add the extension to the certificate after it was signed. This makes the CA's signature invalid. You have to do that before signing, e.g.:
X509_EXTENSION *cert_ex = X509V3_EXT_conf_nid(nullptr, nullptr, NID_subject_alt_name, "DNS:test.com");
X509_add_ext(*crt, cert_ex, -1);
/* Now perform the actual signing with the CA. */
if (X509_sign(*crt, ca_key, EVP_sha256()) == 0) goto err;
With that it works for me both on Firefox and Chrome.

bad decrypt Error OpenSSL C++ & QT AES 256 CBC

I'm trying to add plaintext encryption/decryption (AES 256 CBC) functionality to a personal project built using QT and C++. I'm using OpenSSL v1.1.1. I followed few guides and build a class to handle encryption and decryption. Encryption seems to work fine. But decryption function sometimes throws error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
This works:
sample text here....
These does not ( . = newline ):
i.
sample text here...
.
.
.
.
.
ii.
sample text here... sample text here...
I'm new to QT, C++ and OpenSSL so I have no idea how to fix this.
My Class:
#define KEYSIZE 32
#define IVSIZE 32
#define BLOCKSIZE 256
#define SALTSIZE 8
QByteArray Encryptor::randBytes(int size) {
unsigned char array[size];
RAND_bytes(array, size);
QByteArray output = QByteArray(reinterpret_cast<char*> (array), size);
return output;
}
QByteArray Encryptor::encrypt(QByteArray passphrase, QByteArray &content) {
QByteArray msalt = randBytes(SALTSIZE);
int rounds = 1;
unsigned char key[KEYSIZE];
unsigned char iv[IVSIZE];
const unsigned char *password = (const unsigned char*) passphrase.constData();
const unsigned char *salt = (const unsigned char*) msalt.constData();
int i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, password, passphrase.length(), rounds, key, iv);
if (i != KEYSIZE) {
qCritical() << "EVP_BytesToKey() -- " << ERR_error_string(ERR_get_error(), NULL);
return QByteArray();
}
EVP_CIPHER_CTX *en = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_init(en);
if (!EVP_EncryptInit_ex(en, EVP_aes_256_cbc(), NULL, key, iv)) {
qCritical() << "EVP_EncryptInit_ex() -- " << ERR_error_string(ERR_get_error(), NULL);
return QByteArray();
}
char *input = content.data();
char *out;
int length = content.size();
int cLength = length + AES_BLOCK_SIZE;
int fLength = 0;
unsigned char *cipherText = (unsigned char*) malloc(cLength);
if (!EVP_EncryptInit_ex(en, NULL, NULL, NULL, NULL)) {
qCritical() << "EVP_EncryptInit_ex() -- " << ERR_error_string(ERR_get_error(), NULL);
return QByteArray();
}
if (!EVP_EncryptUpdate(en, cipherText, &cLength, (unsigned char*) input, length)) {
qCritical() << "EVP_EncryptUpdate() -- " << ERR_error_string(ERR_get_error(), NULL);
free(cipherText);
return QByteArray();
}
if (!EVP_EncryptFinal(en, cipherText + cLength, &fLength)) {
qCritical() << "EVP_EncryptFinal() -- " << ERR_error_string(ERR_get_error(), NULL);
free(cipherText);
return QByteArray();
}
length = cLength + fLength;
out = (char*) cipherText;
EVP_CIPHER_CTX_cipher(en);
free(cipherText);
QByteArray output;
output.append("Salted__");
output.append(msalt);
output.append(out, length);
return output;
}
QByteArray Encryptor::decrypt(QByteArray passphrase, QByteArray &content) {
QByteArray msalt;
if (QString(content.mid(0, 8)) != "Salted__") {
qCritical() << "can not extrect the salt...";
return QByteArray();
}
msalt = content.mid(8, 8);
content = content.mid(16);
int rounds = 1;
unsigned char key[KEYSIZE];
unsigned char iv[IVSIZE];
const unsigned char *password = (const unsigned char*) passphrase.constData();
const unsigned char *salt = (const unsigned char*) msalt.constData();
int i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt, password, passphrase.length(), rounds, key, iv);
if (i != KEYSIZE) {
qCritical() << "EVP_BytesToKey() -- " << ERR_error_string(ERR_get_error(), NULL);
return QByteArray();
}
EVP_CIPHER_CTX *de = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_init(de);
if (!EVP_DecryptInit_ex(de, EVP_aes_256_cbc(), NULL, key, iv)) {
qCritical() << "EVP_DecryptInit_ex() -- " << ERR_error_string(ERR_get_error(), NULL);
return QByteArray();
}
char *input = content.data();
int length = content.size();
int pLength = length;
int fLength = 0;
unsigned char *plainText = (unsigned char*) malloc(pLength + AES_BLOCK_SIZE);
if (!EVP_DecryptUpdate(de, plainText, &pLength, (unsigned char*) input, length)) {
qCritical() << "EVP_DecryptUpdate() -- " << ERR_error_string(ERR_get_error(), NULL);
free(plainText);
return QByteArray();
}
if (!EVP_DecryptFinal_ex(de, plainText + pLength, &fLength)) {
qCritical() << "EVP_DecryptFinal_ex() -- " << ERR_error_string(ERR_get_error(), NULL);
free(plainText);
return QByteArray();
}
length = pLength + fLength;
EVP_CIPHER_CTX_cleanup(de);
QByteArray output = QByteArray(reinterpret_cast<char*> (plainText), length);
free(plainText);
return output;
}

C++ openssl - Generate RSA Keypair and read

I am trying to generate RSA keypair using openssl library and then read the same keys later. However, it fails. Sometimes it gives me this error:
error:0906D06C:PEM routines:PEM_read_bio:no start line
And sometimes, it gives me this error:
error:0906D06C:lib(9):func(109):reason(108)
What is the correct way to generate the keypair and later be able to read it ? Here is my code. If you run it, you will find that it correctly generates the RSA key pair but not able read them later.
#include <stdio.h>
#include <iostream>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <exception>
bool generate_key() {
size_t pri_len; // Length of private key
size_t pub_len; // Length of public key
char *pri_key; // Private key in PEM
char *pub_key; // Public key in PEM
int ret = 0;
RSA *r = NULL;
BIGNUM *bne = NULL;
BIO *bp_public = NULL, *bp_private = NULL;
int bits = 2048;
unsigned long e = RSA_F4;
EVP_PKEY *evp_pbkey = NULL;
EVP_PKEY *evp_pkey = NULL;
BIO *pbkeybio = NULL;
BIO *pkeybio = NULL;
// 1. generate rsa key
bne = BN_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 = BIO_new_file("public.pem", "w+");
bp_public = BIO_new(BIO_s_mem());
ret = PEM_write_bio_RSAPublicKey(bp_public, r);
if (ret != 1) {
goto free_all;
}
// 3. save private key
//bp_private = BIO_new_file("private.pem", "w+");
bp_private = BIO_new(BIO_s_mem());
ret = PEM_write_bio_RSAPrivateKey(bp_private, r, NULL, NULL, 0, NULL, NULL);
//4. Get the keys are PEM formatted strings
pri_len = BIO_pending(bp_private);
pub_len = BIO_pending(bp_public);
pri_key = (char*) malloc(pri_len + 1);
pub_key = (char*) malloc(pub_len + 1);
BIO_read(bp_private, pri_key, pri_len);
BIO_read(bp_public, pub_key, pub_len);
pri_key[pri_len] = '\0';
pub_key[pub_len] = '\0';
printf("\n%s\n%s\n", pri_key, pub_key);
//verify if you are able to re-construct the keys
pbkeybio = BIO_new_mem_buf((void*) pub_key, -1);
if (pbkeybio == NULL) {
return -1;
}
evp_pbkey = PEM_read_bio_PUBKEY(pbkeybio, &evp_pbkey, NULL, NULL);
if (evp_pbkey == NULL) {
char buffer[120];
ERR_error_string(ERR_get_error(), buffer);
printf("Error reading public key:%s\n", buffer);
}
pkeybio = BIO_new_mem_buf((void*) pri_key, -1);
if (pkeybio == NULL) {
return -1;
}
evp_pkey = PEM_read_bio_PrivateKey(pkeybio, &evp_pkey, NULL, NULL);
if (evp_pbkey == NULL) {
char buffer[120];
ERR_error_string(ERR_get_error(), buffer);
printf("Error reading private key:%s\n", buffer);
}
BIO_free(pbkeybio);
BIO_free(pkeybio);
// 4. free
free_all:
BIO_free_all(bp_public);
BIO_free_all(bp_private);
RSA_free(r);
BN_free(bne);
return (ret == 1);
}
int main(int argc, char* argv[]) {
generate_key();
return 0;
}
Looks good to me. Except on reloading; I would have used PEM_read_bio_RSAPublicKey in stead of PEM_read_bio_PUBKEY.
I am not sure it is the best way to do it though.
--- /tmp/stack_openssl.cpp.back 2018-05-25 12:53:12.366488025 +0000
+++ /tmp/stack_openssl.cpp 2018-05-25 13:57:20.614066828 +0000
## -18,6 +18,8 ##
int bits = 2048;
unsigned long e = RSA_F4;
+ RSA *pb_rsa = NULL;
+ RSA *p_rsa = NULL;
EVP_PKEY *evp_pbkey = NULL;
EVP_PKEY *evp_pkey = NULL;
## -66,27 +68,32 ##
printf("\n%s\n%s\n", pri_key, pub_key);
//verify if you are able to re-construct the keys
- pbkeybio = BIO_new_mem_buf((void*) pub_key, -1);
+ pbkeybio = BIO_new_mem_buf((void*) pub_key, pub_len);
if (pbkeybio == NULL) {
return -1;
}
- evp_pbkey = PEM_read_bio_PUBKEY(pbkeybio, &evp_pbkey, NULL, NULL);
- if (evp_pbkey == NULL) {
+ pb_rsa = PEM_read_bio_RSAPublicKey(pbkeybio, &pb_rsa, NULL, NULL);
+ if (pb_rsa == NULL) {
char buffer[120];
ERR_error_string(ERR_get_error(), buffer);
printf("Error reading public key:%s\n", buffer);
}
+ evp_pbkey = EVP_PKEY_new();
+ EVP_PKEY_assign_RSA(evp_pbkey, pb_rsa);
- pkeybio = BIO_new_mem_buf((void*) pri_key, -1);
+ pkeybio = BIO_new_mem_buf((void*) pri_key, pri_len);
if (pkeybio == NULL) {
return -1;
}
- evp_pkey = PEM_read_bio_PrivateKey(pkeybio, &evp_pkey, NULL, NULL);
- if (evp_pbkey == NULL) {
+ p_rsa = PEM_read_bio_RSAPrivateKey(pkeybio, &p_rsa, NULL, NULL);
+ if (p_rsa == NULL) {
char buffer[120];
ERR_error_string(ERR_get_error(), buffer);
printf("Error reading private key:%s\n", buffer);
}
+ evp_pkey = EVP_PKEY_new();
+ EVP_PKEY_assign_RSA(evp_pkey, p_rsa);
BIO_free(pbkeybio);
BIO_free(pkeybio);

Why is my OpenSSL/libcrypto signature incorrect?

I need to sign a string, publish the public key as a string, and then somewhere else use the public key to verify the signed message. This is the part where the message is signed:
// RSA keypair generation
EVP_PKEY *keypair = NULL;
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
if (1 != EVP_PKEY_keygen_init(ctx)) {
initFail = true;
}
if (1 != EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048)) {
initFail = true;
}
if (1 != EVP_PKEY_keygen(ctx, &keypair)) {
initFail = true;
}
EVP_PKEY_CTX_free(ctx);
// Create public key string.
BIO* bo = BIO_new(BIO_s_mem());
PEM_write_bio_PUBKEY(bo, keypair);
char buff[1000];
BIO_read(bo, &buff[0], 1000);
BIO_free(bo);
pubkey = buff;
// Create signature
size_t *slen = new size_t;
unsigned char *sig = NULL;
std::string msg;
msg = stuffThatCreatesMessage();
EVP_MD_CTX *mdctx = EVP_MD_CTX_create();
if (1 != EVP_DigestSignInit(mdctx, NULL, EVP_sha256(),
NULL, keypair)) {
initFail = true;
}
if (1 != EVP_DigestSignUpdate(mdctx, msg.c_str(), msg.length())) {
initFail = true;
}
if (1 != EVP_DigestSignFinal(mdctx, NULL, slen)) {
initFail = true;
}
sig = (unsigned char *) OPENSSL_malloc(
sizeof(unsigned char) * (*slen));
if (1 != EVP_DigestSignFinal(mdctx, sig, slen)) {
initFail = true;
}
signature = *sig;
OPENSSL_free(sig);
bool isSuccess = verifySignature(signature, pubkey, msg);
Here's the code that takes the string message and key and actually verifies the signature:
bool verifySignature(std::string sig, std::string key_str, std::string msg) {
BIO* bo = BIO_new(BIO_s_mem());
BIO_write(bo, key_str.c_str(), key_str.length());
EVP_PKEY *key = EVP_PKEY_new();
PEM_read_bio_PUBKEY(bo, &key, 0, 0);
BIO_free(bo);
unsigned char *unsigned_sig = new unsigned char[sig.length()+1];
strcpy((char *) unsigned_sig, sig.c_str());
EVP_MD_CTX *mdctx = EVP_MD_CTX_create();
if (1 != EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, key)) {
return false;
}
if (1 != EVP_DigestVerifyUpdate(mdctx, msg.c_str(), msg.length())) {
return false;
}
bool retval = (1 == EVP_DigestVerifyFinal(mdctx, unsigned_sig,
sig.length()));
delete unsigned_sig;
return retval;
}
Every time I do this, the latter function tells me the signature is invalid, with the isSuccess variable equal to 0. It's because the EVP_DigestSignFinal is returning 0, indicating that the signature is incorrect. Where am I going wrong?
The problem, pointed out by #jww, was the line signature = *sig. I was ending up with only the first byte as my signature string. The correct way to do it is:
std::string temp_sig = reinterpret_cast<char*>(sig);
signature = temp_sig.substr(0, *slen);