Cómo obtener el resultado de PKCS7_sign en un char * o std :: string

Intento escribir un poco de correo electrónico para firmar correos con S / MIME. Hasta ahora, he completado el código hasta la firma del correo. Usé los ejemplos del código demos / smime en openssl para hacer el trabajo. Desafortunadamente, los ejemplos demuestran cómo escribir un mensaje de entrada en un archivo de salida, pero necesito el resultado como una cadena.

Este es mi 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 hay más de 1600 páginas de manual para openssl, no tengo idea de dónde buscar información.

Me encantaría usar el “p7” y escribirlo en un simple std :: string (o char *, si es necesario). La aplicación Milter que escribo recogerá esta cadena y hará un cuerpo de cambio (Aún no está escrito, pero esta es mi idea).

¿Puede alguien señalarme las páginas de rutinas / manual o tiene un ejemplo de código que me puede ayudar?

Gracias por adelantado

Me encantaría usar el “p7” y escribirlo en un simple std :: string (o char *, si es necesario). La aplicación Milter que escribo recogerá esta cadena y hará un cuerpo de cambio (Aún no está escrito, pero esta es mi idea).

No creo que puedas ponerlo en un char* porque puede haber un NULL incrustado, lo que truncaría el resultado.

Use std::string y (1) i2d_PKCS7_bio para ASN.1 / DER o (2) PEM_write_bio_PKCS7 para PEM. La idea es que use la biblioteca como de costumbre, escriba salida en MEM_BIO y luego obtenga el contenido de la biografía usando BUF_MEM . BUF_MEM contiene un puntero a los datos y su longitud. 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)); 

Si usa PEM_write_bio_PKCS7 y un char* , entonces la encoding PEM carecerá de la terminación NULL . Asegúrese de explicarlo porque no es una cadena de caracteres. Consulte también Caracteres no imprimibles después de generar la secuencia aleatoria de Base64 de n bytes , que explica cómo escribir un NULL sin que esté codificado.


Como hay más de 1600 páginas de manual para openssl, no tengo idea de dónde buscar información …

Verifique el código fuente de los subcomandos. Le muestra cómo la biblioteca hace cosas con la API. Por ejemplo, cuando usa openssl pkcs7 , usa la aplicación 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 

El uso de unique_ptr con la función dtor asegura que los objetos se limpien automáticamente y ayuda a mantener el código limpio. Intento usarlo cada vez que OpenSSL se cruza con C ++ (ver Cómo generar clave privada RSA usando openssl para otro ejemplo).

Aquí hay algo de uno de mis proyectos en C ++ que usa 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;