Como obter o resultado PKCS7_sign em um char * ou std :: string

Eu tento escrever um pequeno mail milter para assinar e-mails com S / MIME. Até agora, completei o código para assinar o email. Eu usei os exemplos de código demos / smime no openssl para fazer o trabalho. Infelizmente, os exemplos demonstram como gravar uma mensagem de input em um arquivo de saída, mas preciso do resultado como uma string.

Este é o meu método 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 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()); auto key = fs::path(email.getSmimeFilename()); 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" <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; } 

Como existem mais de 1600 páginas man para o openssl, não tenho a menor idéia de onde procurar informações.

Eu adoraria usar o “p7” e escrevê-lo em um simples std :: string (ou char *, se necessário). O aplicativo milter que eu escrevo vai pegar essa string e faz um corpo de mudança (ainda não escrito, mas essa é a minha ideia).

Alguém pode me apontar para rotinas / man pages ou tem um exemplo de código que pode me ajudar?

desde já, obrigado

Eu adoraria usar o “p7” e escrevê-lo em um simples std :: string (ou char *, se necessário). O aplicativo milter que eu escrevo vai pegar essa string e faz um corpo de mudança (ainda não escrito, mas essa é a minha ideia).

Eu não acredito que você pode colocá-lo em um char* porque pode haver um NULL incorporado, que truncaria o resultado.

Use um std::string e (1) i2d_PKCS7_bio para ASN.1 / DER ou (2) PEM_write_bio_PKCS7 para PEM. A idéia é que você use a biblioteca como de costume, escreva a saída para um MEM_BIO e depois obtenha o conteúdo da biografia usando BUF_MEM . O BUF_MEM contém um ponteiro para os dados e seu comprimento. Algo como…

 using BIO_MEM_ptr = std::unique_ptr; using BIO_MEM_BUF_ptr = std::unique_ptr; 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)); 

Se você usar PEM_write_bio_PKCS7 e um char* , a codificação PEM não terá o NULL terminação. Certifique-se de contabilizá-lo porque não é uma string C. Veja também Caractere não imprimível depois de gerar a seqüência aleatória Base64 de n bytes , que discute como escrever um NULO sem que ele seja codificado.


Como existem mais de 1600 páginas man para o openssl, não tenho a menor idéia de onde procurar informações …

Verifique o código-fonte dos subcomandos. Mostra como a biblioteca faz coisas com a API. Por exemplo, quando você usa o openssl pkcs7 , ele usa o aplicativo pkcs7 .

 $ cd  $ 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 

Usar unique_ptr com a function dtor garante que os objects sejam limpos automaticamente e isso ajuda a manter o código limpo. Eu tento usá-lo sempre que o OpenSSL cruza caminhos com C ++ (consulte Como gerar uma chave privada RSA usando o openssl para outro exemplo).

Aqui está algo de um dos meus projetos C ++ que usa o OpenSSL:

 using EC_KEY_ptr = std::unique_ptr; using EC_GROUP_ptr = std::unique_ptr; using EC_POINT_ptr = std::unique_ptr; using DH_ptr = std::unique_ptr; using RSA_ptr = std::unique_ptr; using DSA_ptr = std::unique_ptr; using EVP_PKEY_ptr = std::unique_ptr; using BN_ptr = std::unique_ptr; using FILE_ptr = std::unique_ptr; using BIO_MEM_ptr = std::unique_ptr; using BIO_FILE_ptr = std::unique_ptr; using EVP_MD_CTX_ptr = std::unique_ptr; using X509_ptr = std::unique_ptr; using ASN1_INTEGER_ptr = std::unique_ptr; using ASN1_TIME_ptr = std::unique_ptr; using X509_EXTENSION_ptr = std::unique_ptr; using X509_NAME_ptr = std::unique_ptr; using X509_NAME_ENTRY_ptr = std::unique_ptr; using X509_STORE_ptr = std::unique_ptr; using X509_LOOKUP_ptr = std::unique_ptr; using X509_STORE_CTX_ptr = std::unique_ptr;