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