Related
There is problem found while signing Nonce(Base64 string) with PKCS#7 using openssl
the problem is when i decode the signature the nonce is trimmed (get 4 char and the expected is 8 char)
Here is the code.
int main(int argc, char *argv[])
{
QString nonce = "Jd0VAO74";
QDateTime dateTime = QDateTime::fromString("2022-12-15T13:51:46Z", Qt::ISODateWithMs);
unsigned char*signature = signNonce(nonce, dateTime);
qDebug() << signature;
return 0;
}
unsigned char* signNonce(nonce, dateTime){
QContentInfo contentInfo = QContentInfo(QByteArray::fromBase64(nonce.toLatin1()));
auto signedCms = QSignedCms(contentInfo);
QOpenssl::QOpensslCertificate qOpensslCertificate(getCertificate());
QCmsSigner cmsSigner = QCmsSigner(qOpensslCertificate);
cmsSigner.setDigestType(QOpenssl::DigestType::SHA256);
cmsSigner.setPkcs9SigningTime(serverDateTime);
signedCms.computeSignatureNew(cmsSigner);
auto l_pSignedCms = PKCS7_PTR(PKCS7_new(),::PKCS7_free);
// set certificate and private key in a signer info.
QSignerInfo qsignerInfo;
PKCS7_SIGNER_INFO* signerInfo = PKCS7_SIGNER_INFO_new();
X509_PTR pX509 = cmsSigner.getCertificate().getCertificate();
EVP_PKEY_PTR pKey = cmsSigner.getCertificate().getPrivateKey();
const EVP_MD* pMD = EVP_sha256();
PKCS7_SIGNER_INFO_set(signerInfo, pX509.get(), pKey.get(), pMD);
// set signing time attribute.
ASN1_TIME* pSigningTime = ASN1_TIME_set(nullptr, cmsSigner.getPkcs9SigningTime().toTime_t());
PKCS7_add0_attrib_signing_time(signerInfo, pSigningTime);
qsignerInfo.setPkcs9SigningTime(cmsSigner.getPkcs9SigningTime());
// set message digest attribute.
QCryptographicHash::Algorithm algo = cmsSigner.getDigestType() == DigestType::SHA256
? QCryptographicHash::Algorithm::Sha256
: QCryptographicHash::Algorithm::Sha1;
QByteArray hash = QCryptographicHash::hash(m_ContentInfo.getContent(), algo);
const auto* pHash = reinterpret_cast<const unsigned char*>(hash.constData());
PKCS7_add1_attrib_digest(signerInfo, pHash, m_ContentInfo.getContent().length());
qsignerInfo.setDigestType(cmsSigner.getDigestType());
qsignerInfo.setHash(hash);
// set content type attribute.
PKCS7_add_attrib_content_type(signerInfo, OBJ_nid2obj(NID_pkcs7_data));
// sign signerinfo.
if(PKCS7_SIGNER_INFO_sign(signerInfo) <= 0) {
qCritical() << ERR_error_string(ERR_get_error(), nullptr);
return;
}
// add signer info to cms.
PKCS7_add_signer(l_pSignedCms.get(), signerInfo);
// set data to cms.
// set certificate to cms.
PKCS7_add_certificate(l_pSignedCms.get(), pX509.get());
// set certificate chain
for(const QOpensslCertificate& cert : cmsSigner.getCertificate().getCertificateChain()) {
if(!cert.isSelfSigned())
PKCS7_add_certificate(l_pSignedCms.get(), cert.getCertificate().get());
}
// set content data.
BIO_PTR pContent = BIO_PTR(BIO_new(BIO_s_mem()), ::BIO_free);
BIO_puts(pContent.get(), m_ContentInfo.getContent().constData());
m_pSignedCms = PKCS7_PTR(
PKCS7_sign(pX509.get(), pKey.get(), nullptr, pContent.get(), 0),
::PKCS7_free);
unsigned char* pSignedValue = nullptr;
int result = i2d_PKCS7(m_pSignedCms.get(), &pSignedValue);
return pSignedValue ;
}
after decodeing online decoder the signature, we found nonce in hex 0x25 DD 15
the nonce only contains 4 char Jd0V
any one has a clue ?
I try to figure out why the decoded signature only contains 4 char not 8
I'm a hobbyist and this is my first dive into the world of C++ and embedded systems. I'm struggling a bit with mbedtls.
I'm trying to create a JWT to authenticate against google services. This involves sha256 hashing some base64 encoded json objects (header.claim) and then RSASSA-PKCS1-V1_5-SIGN signing that.
I seem to be ok up to the signing part. My understanding is I can parse the private key with mbedtls_pk_parse_key, and then use that mbedtls_pk_context with mbedtls_rsa_rsassa_pkcs1_v15_sign to sign the hash.
My issue is two fold, I'm not certain I am using mbedtls_rsa_rsassa_pkcs1_v15_sign correctly as the resulting base64 encoded signature comes out as the following in the console. While the encoded json objects are readable.
encodedHeader: ewoJImFsZyI6CSJSUzI1NiIsCgkidHlwIjoJIkpXVCIKfQ==
encodedSignature: ������������������������������������������������������...
Additionally right as this method returns, I get a stack overflow which I am not sure where it's coming from.
***ERROR*** A stack overflow in task main has been detected.
Backtrace:0x40081c0a:0x3ffb82700x40085a4d:0x3ffb8290 0x40088716:0x3ffb82b0 0x4008748f:0x3ffb8330 0x40085b4c:0x3ffb8350 0x40085afe:0x00000000 |<-CORRUPTED
ELF file SHA256: 4307a925ab3c9b48
Here is my code, any help would be greatly appreciated.
// Encode Header
unsigned char encodedHeader[92];
size_t encodedHeaderSize;
const unsigned char* headerRecast = reinterpret_cast<const unsigned char *>(cJSON_Print((const cJSON*) fHeader.toJSON()));
mbedtls_base64_encode(encodedHeader, 92, &encodedHeaderSize, headerRecast, strlen((const char *) headerRecast));
ESP_LOGI("encodedHeader", "%s", encodedHeader);
// Encode Claim
unsigned char encodedClaim[400];
size_t encodedClaimSize;
const unsigned char* claimRecast = reinterpret_cast<const unsigned char *>(cJSON_Print((const cJSON*) fClaim.toJSON()));
mbedtls_base64_encode(encodedClaim, 400, &encodedClaimSize, claimRecast, strlen((const char *) claimRecast));
ESP_LOGI("encodedClaim", "%s", encodedClaim);
// Concat the header and claim together
std::string headerStr((const char*) encodedHeader);
std::string claimStr((const char*) encodedClaim);
std::string auth = headerStr + "." + claimStr;
// Hash the header.claim
const unsigned char* resultRecast = reinterpret_cast<const unsigned char *>(auth.c_str());
mbedtls_sha256_context shaContext;
unsigned char outHash[32];
mbedtls_sha256_init(&shaContext);
mbedtls_sha256_starts(&shaContext, 0);
mbedtls_sha256_update(&shaContext, resultRecast, strlen((const char *) resultRecast));
mbedtls_sha256_finish(&shaContext, outHash);
mbedtls_sha256_free(&shaContext);
ESP_LOGI("outHash", "%s", outHash);
std::string shaStr;
for (int i=0; i<32; i++) {
char str[3];
sprintf(str, "%02x", (int)outHash[i]);
shaStr += reinterpret_cast<const char*>(str);
};
// pkcs1_v15_sign sign the hash
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_entropy_init( &entropy );
mbedtls_ctr_drbg_init( &ctr_drbg );
mbedtls_pk_context pkContext;
mbedtls_pk_init(&pkContext);
mbedtls_pk_parse_key(
&pkContext,
privateKey,
2000,
NULL,
0);
auto rsa = mbedtls_pk_rsa(pkContext);
unsigned char signature[2000];
int res = mbedtls_rsa_rsassa_pkcs1_v15_sign(rsa,
mbedtls_ctr_drbg_random, &ctr_drbg,
MBEDTLS_RSA_PRIVATE,
MBEDTLS_MD_SHA256,
32, reinterpret_cast<const unsigned char*>(shaStr.c_str()),
signature);
// Release Resources
mbedtls_rsa_free( rsa );
mbedtls_ctr_drbg_free( &ctr_drbg );
mbedtls_entropy_free( &entropy );
// Encode Signature
unsigned char encodedSignature[2000];
size_t encodedSignatureSize;
mbedtls_base64_encode(encodedSignature, 2000, &encodedSignatureSize, (const unsigned char*) signature, strlen((const char *) signature));
ESP_LOGI("encodedSignature", "%s", encodedSignature);
// Concat encoded hash.signature
std::string sig((const char*) encodedSignature);
std::string token = shaStr + "." + sig;
return token.c_str();
If it helps to understand my goal, here is a proof of concept I made in python
import requests
from Crypto.Signature import PKCS1_v1_5
from Crypto.PublicKey import RSA
from Crypto.Hash import SHA256
from base64 import urlsafe_b64encode, urlsafe_b64decode
import json
import time
now = int(time.time())
header = {"alg": "RS256", "typ": "JWT"}
claim = {
"iss": "something",
"scope": "something",
"aud": "https://oauth2.googleapis.com/token",
"exp": now + 3600,
"iat": now
}
key = "{}.{}".format(urlsafe_b64encode(json.dumps(header).encode()).decode(), urlsafe_b64encode(json.dumps(claim).encode()).decode())
with open('key.json', 'r') as f:
creds = json.load(f)
keyPub = RSA.importKey(creds.get('private_key'))
h = SHA256.new(key.encode())
signer = PKCS1_v1_5.new(keyPub)
signature = signer.sign(h)
key = "{}.{}".format(key, urlsafe_b64encode(signature).decode())
print(key)
I would like to sign a certificate request with a smartcard's private key with PKCS#11. I do mean signing the CSR itself and not writing a certificate in response.
So I am using OpenSSL to create the X509 certificate request, and I want to use PKCS#11 and C_Sign to create the signature. How can I do that?
This is what I have currently, but when I try to generate a certificate from this request with OpenSSL CLI, it says the signature doesn't match so I must be doing something wrong. I'm not sure what to pass C_Sign —right now I've tried the output of i2d_X509_REQ()— and how to set the signature back in X509_REQ once it's been created (I've tried building an ASN1_BIT_STRING object).
Note: this is not a duplicate of this question because this one is for a certificate and works for an old version of OpenSSL's API. Although, I have tried to use the answer by manually exposing the internals of the X509_REQ structure (see last code block).
X509_REQ makeCSR() {
/* Create OpenSSL EVP_PKEY from exported public key components */
RSA* openssl_rsa = RSA_new();
BIGNUM* bn_modulus = BN_bin2bn(modulus.data(), (int) modulus.size(), nullptr);
BIGNUM* bn_public_exponent = BN_bin2bn(public_exponent.data(), (int) public_exponent.size(), nullptr);
int success = RSA_set0_key(openssl_rsa, bn_modulus, bn_public_exponent, nullptr);
EVP_PKEY* evp_pkey = EVP_PKEY_new();
/* Add public key to certificate request */
EVP_PKEY_assign(evp_pkey, EVP_PKEY_RSA, openssl_rsa);
X509_REQ* request = X509_REQ_new();
X509_REQ_set_pubkey(request, evp_pkey);
/* Set certificate request attributes */
// ...
/* Sign certificate request with smart card */
unsigned char* buffer { nullptr };
int size = i2d_X509_REQ(request, &buffer);
std::vector<unsigned char> der_encoded_request(buffer, buffer + size);
std::vector<unsigned char> signature = smartcard->signCertificateRequest(der_encoded_request);
/* Build signature object */
ASN1_BIT_STRING* asn1_signature = ASN1_BIT_STRING_new();
ASN1_BIT_STRING_set(asn1_signature, signature.data(), (int) signature.size());
asn1_signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
asn1_signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
X509_ALGOR* x509_algor = X509_ALGOR_new();
ASN1_OBJECT* a = OBJ_nid2obj(pkcs11SignatureAlgorithmToNid(CKM_SHA1_RSA_PKCS));
X509_ALGOR_set0(x509_algor, a, V_ASN1_NULL, nullptr);
/* Add signature to X509_REQ */
X509_REQ_set1_signature_algo(request, x509_algor);
X509_REQ_set0_signature(request, asn1_signature);
return request;
}
std::vector<unsigned char> signCertificateRequest(std::vector<unsigned char>& certificate_request)
{
CK_MECHANISM mechanism = { CKM_SHA1_RSA_PKCS, nullptr, 0 };
auto result = s_pkcs11->fn->C_SignInit(m_session_handle, &mechanism, private_key_handle);
unsigned long signature_length { 0 };
result = pkcs11->C_Sign(m_session_handle,
certificate_request.data(),
(unsigned long) certificate_request.size(),
nullptr,
&signature_length);
std::vector<unsigned char> signature(signature_length);
result = pkcs11->C_Sign(m_session_handle,
certificate_request.data(),
(unsigned long) certificate_request.size(),
signature.data(),
&signature_length);
return signature;
}
I've also tried exposing the internals of X509_REQ and passing the output of i2d_X509_REQ_INFO(&request->req_info, &buffer) to C_Sign; or using ASN1_item_i2d(); and also copying the signature output directly to request->signature->data.
request->req_info.enc.modified = 1;
X509_ALGOR_set0(&request->sig_alg,
OBJ_nid2obj(pkcs11SignatureAlgorithmToNid(CKM_SHA1_RSA_PKCS)),
V_ASN1_NULL,
nullptr);
unsigned char* certDerBuf = NULL;
const auto certDerLen = ASN1_item_i2d(ASN1_VALUE*) &request->req_info,
&certDerBuf,
ASN1_ITEM_rptr(X509_REQ_INFO));
std::vector<unsigned char> certDerVec(certDerBuf, certDerBuf + certDerLen);
std::vector<unsigned char> signature = smartcard->signCertificateRequest(certDerVec);
request->signature->data = (unsigned char*) OPENSSL_malloc(signature.size());
request->signature->length = (int) signature.size();
request->signature->data = signature.data();
I was able to resolve my problem in the end; this is my complete solution. Error handling and PKCS#11 session handling have been omitted. I use a unique_ptr for managing OpenSSL resources. I had to expose OpenSSL's X509_REQ and X509_REQ_INFO structures because I couldn't find accessors for req_info in OpenSSL 1.1.1h.
// OpenSSL internal definitions
using CRYPTO_REF_COUNT = int;
struct X509_req_info_st {
ASN1_ENCODING enc; /* cached encoding of signed part */
ASN1_INTEGER* version; /* version, defaults to v1(0) so can be NULL */
X509_NAME* subject; /* certificate request DN */
X509_PUBKEY* pubkey; /* public key of request */
/*
* Zero or more attributes.
* NB: although attributes is a mandatory field some broken
* encodings omit it so this may be NULL in that case.
*/
STACK_OF(X509_ATTRIBUTE) * attributes;
};
struct X509_req_st {
X509_REQ_INFO req_info; /* signed certificate request data */
X509_ALGOR sig_alg; /* signature algorithm */
ASN1_BIT_STRING* signature; /* signature */
CRYPTO_REF_COUNT references;
CRYPTO_RWLOCK* lock;
};
template<typename T>
using AutoDeletedPtr = std::unique_ptr<T, std::function<void(T*)>>;
void makeCSR(const std::vector<unsigned char>& modulus,
const std::vector<unsigned char>& public_exponent)
{
/* Create OpenSSL EVP_PKEY from exported public key components */
auto* openssl_rsa = RSA_new();
auto bn_modulus = BN_bin2bn(modulus.data(), static_cast<int>(modulus.size()), nullptr);
auto bn_public_exponent = BN_bin2bn(public_exponent.data(),
static_cast<int>(public_exponent.size()),
nullptr);
auto success = RSA_set0_key(openssl_rsa, bn_modulus, bn_public_exponent, nullptr);
auto key_file = AutoDeletedPtr<BIO>(BIO_new_file("key.pem", "wb"), BIO_free);
PEM_write_bio_RSA_PUBKEY(key_file.get(), openssl_rsa);
auto evp_pkey = AutoDeletedPtr<EVP_PKEY>(EVP_PKEY_new(), EVP_PKEY_free);
/* Add public key to certificate request */
EVP_PKEY_assign(evp_pkey.get(), EVP_PKEY_RSA, openssl_rsa);
auto request = AutoDeletedPtr<X509_REQ>(X509_REQ_new(), X509_REQ_free);
X509_REQ_set_pubkey(request.get(), evp_pkey.get());
/* Set certificate request attributes */
// ...
/* Sign certificate request with smart card */
unsigned char* buffer { nullptr };
auto size = i2d_X509_REQ_INFO(&request->req_info, &buffer);
std::vector<unsigned char> der_encoded_request(buffer, buffer + size);
auto signature = signCertificateRequest(der_encoded_request);
auto asn1_signature = ASN1_BIT_STRING_new();
ASN1_BIT_STRING_set(asn1_signature, signature.data(), static_cast<int>(signature.size()));
auto x509_algor = AutoDeletedPtr<X509_ALGOR>(X509_ALGOR_new(), X509_ALGOR_free);
auto* a = OBJ_nid2obj(pkcs11SignatureAlgorithmToNid(CKM_SHA1_RSA_PKCS));
auto algor_set_result = X509_ALGOR_set0(x509_algor.get(), a, V_ASN1_NULL, nullptr);
X509_REQ_set1_signature_algo(request.get(), x509_algor.get());
X509_REQ_set0_signature(request.get(), asn1_signature);
request->signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
request->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
// Do what you want with the request
}
std::vector<unsigned char> signCertificateRequest(std::vector<unsigned char>& certificate_request)
{
// You'll need an open session and be logged in
CK_OBJECT_CLASS private_key_class = CKO_PRIVATE_KEY;
CK_KEY_TYPE private_key_type = CKK_RSA;
std::array<unsigned char, 4> id = "myid";
std::vector<CK_ATTRIBUTE> private_key_template = {
{ CKA_CLASS, &private_key_class, sizeof(private_key_class) },
{ CKA_KEY_TYPE, &private_key_type, sizeof(private_key_type) },
{ CKA_ID, id.data(), static_cast<long>(id.size()) },
};
auto result = s_pkcs11->fn->C_FindObjectsInit(m_session_handle,
private_key_template.data(),
static_cast<unsigned long>(private_key_template.size()));
CK_OBJECT_HANDLE private_key_handle { 0 };
unsigned long object_count { 0 };
result = s_pkcs11->fn->C_FindObjects(m_session_handle, &private_key_handle, 1, &object_count);
result = s_pkcs11->fn->C_FindObjectsFinal(m_session_handle);
CK_MECHANISM mechanism = { CKM_SHA1_RSA_PKCS, nullptr, 0 };
auto result = s_pkcs11->fn->C_SignInit(m_session_handle, &mechanism, private_key_handle);
unsigned long signature_length { 0 };
result = s_pkcs11->fn->C_Sign(m_session_handle,
certificate_request.data(),
static_cast<unsigned long>(certificate_request.size()),
nullptr,
&signature_length);
std::vector<unsigned char> signature(signature_length);
result = s_pkcs11->fn->C_Sign(m_session_handle,
certificate_request.data(),
static_cast<unsigned long>(certificate_request.size()),
signature.data(),
&signature_length);
return signature;
}
I'm trying to use dht to keep mutable data with libtorrent. As far as I can understand, the right way is to use the method dht_put_item from session. The problem is that I need to pass a callback function and I don't know what I'm doing wrong... my code looks like the following
namespace lt = libtorrent;
//The callback function
void cb(lt::entry& cdentry, boost::array<char,64>& cbarray, boost::uint64_t& cbint, std::string const& cbstring){
//My stuff here
}
void main(){
//The session
lt::session ses;
//The data I want to insert into DHT
std::string cadenaStr = "519d818411de49652b4aaf34850321de28bb2dce";
//Now I create the keys
unsigned char seed[32];
unsigned char public_key[32];
unsigned char private_key[64];
unsigned char signature[32];
ed25519_create_seed(seed);
ed25519_create_keypair(public_key, private_key, seed);
ed25519_sign(signature, cadenaStr.c_str(), sizeof(cadenaStr.c_str()), public_key, private_key);
//How can I use this?, where is the data supposed to go? :|
ses.dht_put_item(public_key, cb, false);
}
On libtorrent/session_handler.hpp this method is defined as
void dht_put_item(boost::array<char, 32> key
, boost::function<void(entry&, boost::array<char,64>&
, boost::uint64_t&, std::string const&)> cb
, std::string salt = std::string());
Can someone please tell me what I'm doing wrong.
Thanks!
There is an example in the libtorrent repository that I use for testing. It can generate keys, put and get both mutable and immutable items.
https://github.com/arvidn/libtorrent/blob/master/tools/dht_put.cpp
How can I use this?, where is the data supposed to go? :|
You provide the data in the callback that's called. The reason for this kind of API is that there are use cases where you want to mutate the data, and then you need to first know whether something is already stored under this key, and what it is.
You are missing the settings pack for your session.
lt::settings_pack settings;
settings.set_bool(settings_pack::enable_dht, false);
settings.set_int(settings_pack::alert_mask, 0xffffffff);
ses.apply_settings(settings);
settings.set_bool(settings_pack::enable_dht, true);
ses.apply_settings(settings);
Then you need to wait until you receive a boostrap message by waiting for an alert.
wait_for_alert(ses, dht_bootstrap_alert::alert_type);
Last, your dht_put_item call:
char const* cadenaStr = "519d818411de49652b4aaf34850321de28bb2dce";
dht_put_item(public_key, std::bind(&put_string, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, public_key, private_key, cadenaStr));
You will need these functions:
static alert* wait_for_alert(session* ses, int alert_type)
{
alert* ret = nullptr;
bool found = false;
while (!found)
{
ses->wait_for_alert(seconds(5));
std::vector<alert*> alerts;
ses->pop_alerts(&alerts);
for (std::vector<alert*>::iterator i = alerts.begin()
, end(alerts.end()); i != end; ++i)
{
if ((*i)->type() != alert_type)
{
continue;
}
ret = *i;
found = true;
}
}
return ret;
}
static void put_string(
entry& e
,boost::array<char, 64>& sig
,boost::int64_t& seq
,std::string const& salt
,boost::array<char, 32> const& pk
,boost::array<char, 64> const& sk
,char const* str)
{
using dht::sign_mutable_item;
if (str != NULL) {
e = std::string(str);
std::vector<char> buf;
bencode(std::back_inserter(buf), e);
dht::signature sign;
seq++;
sign = sign_mutable_item(buf, salt, dht::sequence_number(seq)
, dht::public_key(pk.data())
, dht::secret_key(sk.data()));
sig = sign.bytes;
}
}
I have a sample signature generator in C which will create a hash message <count:mac-addr> and generate a signature.
When I use char *message = "120:08:00:27:7c:b6:18";
and sign, the signature is verified successfully.
But when I use
char * generate_hash()
{
xmlDoc *document;
xmlNode *root, *first_child, *node;
char *filename;
char *ap_count;
char *ap_mac_address;
char *message;
filename = "/license.xml";
document = xmlReadFile(filename, NULL, 0);
root = xmlDocGetRootElement(document);
first_child = root->children;
for (node = first_child; node; node = node->next) {
if ( strcmp((char*)node->name, "ap_count") == 0 ) {
ap_count = (char*)xmlNodeGetContent(node);
}
if ( strcmp((char*)node->name, "ap_mac_address") == 0 ){
ap_mac_address = (char*)xmlNodeGetContent(node);
}
}
message = (char *) malloc(strlen(ap_count)+ strlen(ap_mac_address) +1 );
memset(message,0x0,(1 + strlen(ap_count)+ strlen(ap_mac_address)));
strcpy(message,ap_count);
strcat(message,":");
strcat(message,ap_mac_address);
printf(" %d \n", (1 + strlen(ap_count)+ strlen(ap_mac_address)));
return message;
}
--- while verifying,
char* message;
message = generate_hash();
I am using the below function call to generate the signature in both the cases.
if(RSA_sign(NID_sha256, (unsigned char*) message, strlen(message),
signature, &slen, private_key) != 1) {
ERR_print_errors_fp(stdout);
return 1;
}
The signature verification fails with this above procedure. Not sure what I am doing wrong here.
Below is the call I am using to verify the same.
verified = RSA_verify(NID_sha256, (unsigned char*) message,
strlen(message), sign, file_len, public_key);
verified = RSA_verify(NID_sha256, (unsigned char*) message,
strlen(message), sign, file_len, public_key);
The signature could have an embedded NULL. Do not treat it like string data, and don't use strlen on it.
You have to manage a pointer and an explicit length.
The description of RSA_verify tells following:
RSA_verify() verifies that the signature sigbuf of size siglen matches
a given message digest m of size m_len. type denotes the message
digest algorithm that was used to generate the signature. rsa is the
signer's public key.
So, using RSA_verify with original message is not correct: message digest should be used instead.