Как получить результат PKCS7_sign в char * или std :: string

Я пытаюсь написать письмо для подписи писем с помощью S / MIME. До сих пор я довел код до подписания письма. Я использовал примеры кода demos / smime в openssl, чтобы сделать работу. К сожалению, примеры демонстрируют, как записать входное сообщение в выходной файл, но мне нужен результат в виде строки.

Это мой Smime-метод:

void Smime::sign() {
if (!isLoaded())
return;

// Null-mailer or unknown
if (mailFrom.empty())
return;

auto *client = util::mlfipriv(ctx);
bool signedOrEncrypted = false;
std::vector<std::string> contentType;

contentType.push_back("multipart/signed");
contentType.push_back("multipart/encrypted");
contentType.push_back("application/pkcs7-mime");

if (client->sessionData.count("Content-Type") == 1) {
std::string value {client->sessionData["Content-Type"]};
std::size_t found;

for (int i=0; i<contentType.size(); i++) {
found = value.find(contentType.at(i));
if (found != std::string::npos) {
signedOrEncrypted = true;
break;
}
}
}

if (signedOrEncrypted) {
const char logmsg[] = "Message already signed or encrypted";
syslog(LOG_NOTICE, "%s", logmsg);
return;
}

/*
* TODO:
* Catch more cases, where an email already could have been encrypted
* or signed elsewhere.
*/

mapfile::Map email {mailFrom};

auto cert = fs::path(email.getSmimeFilename<mapfile::Smime::CERT>());
auto key = fs::path(email.getSmimeFilename<mapfile::Smime::KEY>());

if (!fs::exists(cert) && !fs::is_regular(cert))
return;
if (!fs::exists(key) && !fs::is_regular(key))
return;

// Signing starts here

BIO *in = nullptr, *out = nullptr, *tbio = nullptr;
X509 *scert = nullptr;
EVP_PKEY *skey = nullptr;
PKCS7 *p7 = nullptr;

int flags = PKCS7_DETACHED | PKCS7_STREAM;

OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();

// S/MIME certificate
tbio = BIO_new_file(cert.string().c_str(), "r");

if (!tbio) {
std::cerr << "Error: BIO_new_file(Cert) failed" << std::endl;
return;
}

scert = PEM_read_bio_X509(tbio, nullptr, 0, nullptr);

// S/MIME key
tbio = BIO_new_file(key.string().c_str(), "r");

if (!tbio) {
std::cerr << "Error: BIO_new_file(Key) failed" << std::endl;
return;
}

skey = PEM_read_bio_PrivateKey(tbio, nullptr, 0, nullptr);

if (!scert || !skey) {
std::cerr << "Error: Neither cert or key was loaded" << std::endl;
return;
}

// Loading mail content from temp file
in = BIO_new_file(client->getTempFile().c_str(), "r");

if (!in) {
std::cerr << "Error: Unable to load content from temp file"<< std::endl;
return;
}

// Signing
p7 = PKCS7_sign(scert, skey, nullptr, in, flags);

if (!p7) {
std::cerr << "Error: Message could not be signed" << std::endl;
return;
}

// Cleanup
PKCS7_free(p7);
X509_free(scert);
EVP_PKEY_free(skey);
BIO_free(in);
BIO_free(out);
BIO_free(tbio);

smimeSigned = true;
}

Поскольку для openssl существует более 1600 страниц man, я не знаю, где искать информацию.

Я хотел бы использовать «p7» и записать его в простой std :: string (или char *, если требуется). Приложение milter, которое я пишу, подхватит эту строку и выполнит изменение тела (еще не написано, но это моя идея).

Может кто-нибудь указать мне на рутинные / справочные страницы или есть пример кода, который может мне помочь?

заранее спасибо

2

Решение

Я хотел бы использовать «p7» и записать его в простой std :: string (или char *, если требуется). Приложение milter, которое я пишу, подхватит эту строку и выполнит изменение тела (еще не написано, но это моя идея).

Я не верю, что вы можете положить это в char* потому что может быть встроенный NULL, который бы усек результат.

Использовать std::string и либо (1) i2d_PKCS7_bio для ASN.1 / DER или (2) PEM_write_bio_PKCS7 для PEM. Идея в том, что вы используете библиотеку как обычно, записываете вывод в MEM_BIO а затем получить содержимое био, используя BUF_MEM, BUF_MEM содержит указатель на данные и их длину. Что-то вроде…

using BIO_MEM_ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
using BIO_MEM_BUF_ptr = std::unique_ptr<BUF_MEM, decltype(&::BIO_free)>;

BIO_MEM_ptr bio(BIO_new(BIO_s_mem()), ::BIO_free);
int ret = i2d_PKCS7_bio(bio, p7);
ASSERT(ret == 1);

BIO_MEM_BUF_ptr buff;
BIO_get_mem_ptr(bio.get(), &buff.get());

const BUF_MEM& t = *buff.get();
std::string result((t.data ? t.data : ""), (t.data ? t.length : 0));

Если ты используешь PEM_write_bio_PKCS7 и char*тогда в кодировке PEM не будет завершающей NULL, Обязательно учтите это, потому что это не C-строка. Также см Непечатаемый символ после генерации случайной n-байтовой строки Base64, в котором обсуждается, как написать NULL без его кодирования.


Поскольку для openssl существует более 1600 страниц man, я понятия не имею, где искать информацию …

Извлечь исходный код для подкоманд. Он показывает вам, как библиотека работает с API. Например, когда вы используете openssl pkcs7, он использует pkcs7 приложение.

$ cd <openssl src dir>
$ cd apps
$ ls *.c
app_rand.c  dsaparam.c  openssl.c   rehash.c    speed.c
apps.c      ec.c        opt.c       req.c       spkac.c
asn1pars.c  ecparam.c   passwd.c    rsa.c       srp.c
ca.c        enc.c       pkcs12.c    rsautl.c    ts.c
ciphers.c   engine.c    pkcs7.c     s_cb.c      verify.c
cms.c       errstr.c    pkcs8.c     s_client.c  version.c
crl.c       gendsa.c    pkey.c      s_server.c  vms_decc_init.c
crl2p7.c    genpkey.c   pkeyparam.c s_socket.c  x509.c
dgst.c      genrsa.c    pkeyutl.c   s_time.c
dhparam.c   nseq.c      prime.c     sess_id.c
dsa.c       ocsp.c      rand.c      smime.c

С помощью unique_ptr с функцией dtor обеспечивает автоматическую очистку объектов и помогает поддерживать чистоту кода. Я пытаюсь использовать его всякий раз, когда OpenSSL пересекает пути с C ++ (см. Как сгенерировать закрытый ключ RSA с помощью openssl для другого примера).

Вот что-то из одного из моих проектов C ++, использующего OpenSSL:

using EC_KEY_ptr = std::unique_ptr<EC_KEY, decltype(&::EC_KEY_free)>;
using EC_GROUP_ptr = std::unique_ptr<EC_GROUP, decltype(&::EC_GROUP_free)>;
using EC_POINT_ptr = std::unique_ptr<EC_POINT, decltype(&::EC_POINT_free)>;

using DH_ptr = std::unique_ptr<DH, decltype(&::DH_free)>;

using RSA_ptr = std::unique_ptr<RSA, decltype(&::RSA_free)>;

using DSA_ptr = std::unique_ptr<DSA, decltype(&::DSA_free)>;

using EVP_PKEY_ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;

using BN_ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;

using FILE_ptr = std::unique_ptr<FILE, decltype(&::fclose)>;

using BIO_MEM_ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
using BIO_FILE_ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;

using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_destroy)>;

using X509_ptr = std::unique_ptr<X509, decltype(&::X509_free)>;
using ASN1_INTEGER_ptr = std::unique_ptr<ASN1_INTEGER, decltype(&::ASN1_INTEGER_free)>;
using ASN1_TIME_ptr = std::unique_ptr<ASN1_TIME, decltype(&::ASN1_TIME_free)>;
using X509_EXTENSION_ptr = std::unique_ptr<X509_EXTENSION, decltype(&::X509_EXTENSION_free)>;

using X509_NAME_ptr = std::unique_ptr<X509_NAME, decltype(&::X509_NAME_free)>;
using X509_NAME_ENTRY_ptr = std::unique_ptr<X509_NAME_ENTRY, decltype(&::X509_NAME_ENTRY_free)>;

using X509_STORE_ptr = std::unique_ptr<X509_STORE, decltype(&::X509_STORE_free)>;
using X509_LOOKUP_ptr = std::unique_ptr<X509_LOOKUP, decltype(&::X509_LOOKUP_free)>;
using X509_STORE_CTX_ptr = std::unique_ptr<X509_STORE_CTX, decltype(&::X509_STORE_CTX_free)>;
2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]