c++ openssl smime

c++ - Cómo obtener el resultado PKCS7_sign en un char*o std:: string



openssl smime (1)

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 cambio de cuerpo (aún no está escrito, pero esta es mi idea).

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

Use un std::string y (1) i2d_PKCS7_bio para ASN.1 / DER o (2) PEM_write_bio_PKCS7 para PEM. La idea es usar la biblioteca como de costumbre, escribir la salida en un MEM_BIO y luego obtener 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<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));

Si usa PEM_write_bio_PKCS7 y un char* , entonces la codificación PEM carecerá de la terminación NULL . Asegúrese de tenerlo en cuenta porque no es una cadena en C. Consulte también Carácter no imprimible después de generar una cadena Base64 aleatoria 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 las cosas con la API. Por ejemplo, cuando usa openssl pkcs7 , usa la aplicación 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

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

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

Intento escribir un poco de correo electrónico para firmar correos con S / MIME. Hasta ahora, he completado el código hasta firmar el correo. Utilicé los ejemplos de 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<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; }

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 cambio de cuerpo (aún no está escrito, pero esta es mi idea).

¿Alguien puede señalarme rutinas / páginas man o tiene un código de ejemplo que pueda ayudarme?

Gracias por adelantado