diff --git a/CMakeLists.txt b/CMakeLists.txt index 36ccf42..81b5916 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) set(CMAKE_INCLUDE_CURRENT_DIR ON) option(BUILD_SHARED_LIBS "Build in shared lib mode" ON) +option(BUILD_WITH_SMIME "Build with SMIME Support" OFF) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -78,6 +79,10 @@ install(EXPORT SimpleMailTargets COMPONENT Devel ) +if(BUILD_WITH_SMIME) + find_package(OpenSSL REQUIRED) +endif() + add_subdirectory(src) if (BUILD_DEMOS) add_subdirectory(demos) diff --git a/demos/CMakeLists.txt b/demos/CMakeLists.txt index cac7c2f..4c93284 100644 --- a/demos/CMakeLists.txt +++ b/demos/CMakeLists.txt @@ -2,4 +2,7 @@ add_subdirectory(demo1) add_subdirectory(demo2) add_subdirectory(demo3) add_subdirectory(demo4) +if(BUILD_WITH_SMIME) +add_subdirectory(demo5) +endif() add_subdirectory(async1) diff --git a/demos/demo5/CMakeLists.txt b/demos/demo5/CMakeLists.txt new file mode 100644 index 0000000..785fb85 --- /dev/null +++ b/demos/demo5/CMakeLists.txt @@ -0,0 +1,20 @@ +include_directories( + +) + +set(demo_SRCS + demo5.cpp +) + +add_definitions(-DWITH_SMIME) + +add_executable(demo5 + ${demo_SRCS} +) + +target_link_libraries(demo5 + SimpleMail::Core + Qt${QT_VERSION_MAJOR}::Core + OpenSSL::SSL + OpenSSL::Crypto +) diff --git a/demos/demo5/demo5.cpp b/demos/demo5/demo5.cpp new file mode 100644 index 0000000..a3e2c7a --- /dev/null +++ b/demos/demo5/demo5.cpp @@ -0,0 +1,89 @@ +#include "../../src/SimpleMail" + +#include + +using namespace SimpleMail; + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + // This is a demo that shows you how to sign and/or encrypt an email + + // First we need to create an SmtpClient object + // We will use the Gmail's smtp server (smtp.gmail.com, port 465, ssl) + + Server server; + server.setHost(QLatin1String("smtp.gmail.com")); + server.setPort(465); + server.setConnectionType(Server::SslConnection); + + // We need to set the username (your email address) and password + // for smtp authentification. + + server.setUsername(QLatin1String("your_email_address@host.com")); + server.setPassword(QLatin1String("your_password")); + + // Now we create a SMimeMessage object. This is the email. + + SMimeMessage message; + + EmailAddress sender(QLatin1String("your_email_address@host.com"), QLatin1String("Your Name")); + message.setSender(sender); + + EmailAddress to(QLatin1String("recipient@host.com"), QLatin1String("Recipient's Name")); + message.addTo(to); + + message.setSubject(QLatin1String("SmtpClient for Qt - Demo")); + + // Now add some text to the email. + // First we create a MimeText object. + + auto text = std::make_shared(); + + text->setText(QLatin1String("Hi,\nThis is a simple email message.\n")); + + // Now add it to the mail + + message.addPart(text); + + // Add an attachment + + auto document = + std::make_shared(std::make_shared(QLatin1String("document.pdf"))); + message.addPart(document); + + // Setup private and public key/certificate in PKCS#12 format + + message.setKeyFile(QLatin1String("your_private_key.p12"), + QLatin1String("your_private_key_password")); + message.setPublicKey(QLatin1String("recipient_public_key.cert")); + + // Sign the message. Only your private key is required. + // if(!message.sign()) { + // qDebug() << "Failed to create signed email"; + // return -3; + // } + + // Encrypt the message. Only the recipient's public key/certificate is required. + // if(!message.encrypt()) { + // qDebug() << "Failed to create encrypted email"; + // return -3; + // } + + // Sign and encrypt the message + if (!message.signAndEncrypt()) { + qDebug() << "Failed to create signed and encrypted email"; + return -3; + } + + // Now we can send the mail + ServerReply *reply = server.sendMail(message); + QObject::connect(reply, &ServerReply::finished, [=] { + qDebug() << "ServerReply finished" << reply->error() << reply->responseText(); + reply->deleteLater(); + qApp->exit(reply->error() ? -3 : 0); + }); + + app.exec(); +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7ce6535..99eeb49 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,6 +43,22 @@ set(simplemailqt_HEADERS_PRIVATE # common.h ) +if(BUILD_WITH_SMIME) + add_definitions(-DWITH_SMIME) + list(APPEND simplemailqt_SRC + smimemessage.cpp + smimemessage_p.cpp + smimepart.cpp + smimepart_p.cpp + ) + list(APPEND simplemailqt_HEADERS + smimemessage.h + smimemessage_p.h + smimepart.h + smimepart_p.h + ) +endif() + add_library(SimpleMail${PROJECT_VERSION_MAJOR}Qt${QT_VERSION_MAJOR} ${simplemailqt_SRC} ${simplemailqt_HEADERS} @@ -95,6 +111,14 @@ target_link_libraries(SimpleMail${PROJECT_VERSION_MAJOR}Qt${QT_VERSION_MAJOR} Qt::Network ) +if(BUILD_WITH_SMIME) + target_link_libraries(SimpleMail${PROJECT_VERSION_MAJOR}Qt${QT_VERSION_MAJOR} + PUBLIC + OpenSSL::SSL + OpenSSL::Crypto + ) +endif() + set_property(TARGET SimpleMail${PROJECT_VERSION_MAJOR}Qt${QT_VERSION_MAJOR} PROPERTY PUBLIC_HEADER ${simplemailqt_HEADERS}) install(TARGETS SimpleMail${PROJECT_VERSION_MAJOR}Qt${QT_VERSION_MAJOR} EXPORT SimpleMailTargets DESTINATION ${CMAKE_INSTALL_LIBDIR} diff --git a/src/SimpleMail b/src/SimpleMail index 0d2526f..d9e5505 100644 --- a/src/SimpleMail +++ b/src/SimpleMail @@ -26,3 +26,6 @@ #include "mimefile.h" #include "server.h" #include "serverreply.h" +#ifdef WITH_SMIME +#include "smimemessage.h" +#endif diff --git a/src/mimemessage.cpp b/src/mimemessage.cpp index a9579b3..fc1d71f 100644 --- a/src/mimemessage.cpp +++ b/src/mimemessage.cpp @@ -55,9 +55,9 @@ MimeMessage &MimeMessage::operator=(const MimeMessage &other) return *this; } -MimePart &MimeMessage::getContent() +std::shared_ptr MimeMessage::getContent() { - return *d->content; + return d->content; } void MimeMessage::setContent(const std::shared_ptr &content) @@ -135,6 +135,16 @@ bool MimeMessage::write(QIODevice *device) const return true; } +MimeMessage::MimeMessage(MimeMessagePrivate *d, bool createAutoMimeContent) + : d(d) +{ + if (createAutoMimeContent) { + d->content = std::make_shared(); + } + + d->autoMimeContentCreated = createAutoMimeContent; +} + void MimeMessage::setSender(const EmailAddress &sender) { d->sender = sender; diff --git a/src/mimemessage.h b/src/mimemessage.h index c0b79f3..11b725b 100644 --- a/src/mimemessage.h +++ b/src/mimemessage.h @@ -66,12 +66,13 @@ class SMTP_EXPORT MimeMessage QString subject() const; QList> parts() const; - MimePart &getContent(); + std::shared_ptr getContent(); void setContent(const std::shared_ptr &content); bool write(QIODevice *device) const; protected: + MimeMessage(MimeMessagePrivate *d, bool createAutoMimeContent = true); QSharedDataPointer d; }; diff --git a/src/mimemultipart.cpp b/src/mimemultipart.cpp index 78efe3f..c4df53a 100644 --- a/src/mimemultipart.cpp +++ b/src/mimemultipart.cpp @@ -69,6 +69,7 @@ bool MimeMultiPart::writeData(QIODevice *device) if (!part->write(device)) { return false; } + device->write("\r\n"); } device->write("--" + d->contentBoundary + "--\r\n"); diff --git a/src/mimepart.cpp b/src/mimepart.cpp index 71a732c..12703dc 100644 --- a/src/mimepart.cpp +++ b/src/mimepart.cpp @@ -32,6 +32,7 @@ MimePart::MimePart() } MimePart::MimePart(const MimePart &other) + : d_ptr(new MimePartPrivate) { Q_D(MimePart); d->contentCharset = other.charset(); @@ -45,6 +46,7 @@ MimePart::MimePart(const MimePart &other) d->contentType = other.contentType(); d->contentEncoding = other.encoding(); d->header = other.header(); + d->contentIsBase64 = other.contentIsBase64(); } MimePart::~MimePart() @@ -65,6 +67,7 @@ MimePart &MimePart::operator=(const MimePart &other) d->contentType = other.contentType(); d->contentEncoding = other.encoding(); d->header = other.header(); + d->contentIsBase64 = other.contentIsBase64(); return *this; } @@ -228,6 +231,9 @@ bool MimePart::write(QIODevice *device) headers.append("; name=\"?UTF-8?B?" + d->contentName.toBase64(QByteArray::Base64Encoding) + "?=\""); } + if (headers.contains("signed")) { + headers.append("; protocol=\"application/pkcs7-signature\"; micalg=sha1"); + } if (!d->contentCharset.isEmpty()) { headers.append("; charset=" + d->contentCharset); } @@ -274,6 +280,18 @@ MimePart::MimePart(MimePartPrivate *d) { } +void MimePart::setContentIsBase64(bool isBase64) +{ + Q_D(MimePart); + d->contentIsBase64 = isBase64; +} + +bool MimePart::contentIsBase64() const +{ + Q_D(const MimePart); + return d->contentIsBase64; +} + bool MimePart::writeData(QIODevice *device) { Q_D(MimePart); @@ -296,8 +314,14 @@ bool MimePart::writeData(QIODevice *device) } break; case MimePart::Base64: - if (!d->writeBase64(input, device)) { - return false; + if (!d->contentIsBase64) { + if (!d->writeBase64(input, device)) { + return false; + } + } else { + if (!d->writeRaw(input, device)) { + return false; + } } break; case MimePart::QuotedPrintable: diff --git a/src/mimepart.h b/src/mimepart.h index 8d8c8b8..a8c1b02 100644 --- a/src/mimepart.h +++ b/src/mimepart.h @@ -67,6 +67,10 @@ class SMTP_EXPORT MimePart bool write(QIODevice *device); + void setContentIsBase64(bool isBase64); + + bool contentIsBase64() const; + protected: MimePart(MimePartPrivate *d); virtual bool writeData(QIODevice *device); diff --git a/src/mimepart_p.h b/src/mimepart_p.h index fea6a0b..e9eb723 100644 --- a/src/mimepart_p.h +++ b/src/mimepart_p.h @@ -44,6 +44,7 @@ class MimePartPrivate : public QSharedData QByteArray contentType; QByteArray contentCharset; QByteArray contentBoundary; + bool contentIsBase64 = false; MimeContentFormatter formatter; MimePart::Encoding contentEncoding = MimePart::_7Bit; diff --git a/src/smimemessage.cpp b/src/smimemessage.cpp new file mode 100644 index 0000000..28f5a3c --- /dev/null +++ b/src/smimemessage.cpp @@ -0,0 +1,59 @@ +#include "smimemessage.h" + +#include "mimemultipart.h" +#include "smimemessage_p.h" + +using namespace SimpleMail; + +SMimeMessage::SMimeMessage() + : MimeMessage(new SMimeMessagePrivate()) +{ +} + +SMimeMessage::~SMimeMessage() +{ +} + +void SMimeMessage::setKeyFile(const QString &filename, const QString &password) +{ + SMimeMessagePrivate *dPtr = static_cast(d.data()); + dPtr->_smimePart->setKeyFile(filename, password); +} + +void SMimeMessage::setPublicKey(const QString &filename) +{ + SMimeMessagePrivate *dPtr = static_cast(d.data()); + dPtr->_smimePart->setPublicKey(filename); +} + +bool SMimeMessage::sign() +{ + SMimeMessagePrivate *dPtr = static_cast(d.data()); + dPtr->_smimePart->writeMimeMessageBuffer(this->getContent()); + + MimeMultiPart *multiPartSigned = new MimeMultiPart(MimeMultiPart::Signed); + multiPartSigned->addPart(getContent()); + setContent(std::shared_ptr(multiPartSigned)); + + bool ret = dPtr->_smimePart->sign(); + addPart(dPtr->_smimePart); + return ret; +} + +bool SMimeMessage::encrypt() +{ + SMimeMessagePrivate *dPtr = static_cast(d.data()); + dPtr->_smimePart->writeMimeMessageBuffer(this->getContent()); + bool ret = dPtr->_smimePart->encrypt(); + setContent(dPtr->_smimePart); + return ret; +} + +bool SMimeMessage::signAndEncrypt() +{ + SMimeMessagePrivate *dPtr = static_cast(d.data()); + dPtr->_smimePart->writeMimeMessageBuffer(this->getContent()); + bool ret = dPtr->_smimePart->signAndEncrypt(); + setContent(dPtr->_smimePart); + return ret; +} diff --git a/src/smimemessage.h b/src/smimemessage.h new file mode 100644 index 0000000..eaf6f2a --- /dev/null +++ b/src/smimemessage.h @@ -0,0 +1,28 @@ +#ifndef SMIMEMESSAGE_H +#define SMIMEMESSAGE_H + +#include "mimemessage.h" +#include "smtpexports.h" + +class QIODevice; +namespace SimpleMail { + +class SMimeMessagePrivate; +class SMTP_EXPORT SMimeMessage : public MimeMessage +{ +public: + explicit SMimeMessage(); + virtual ~SMimeMessage(); + + void setKeyFile(const QString &filename, const QString &password); + + void setPublicKey(const QString &filename); + + bool sign(); + bool encrypt(); + bool signAndEncrypt(); +}; + +} // namespace SimpleMail + +#endif // SMIMEMESSAGE_H diff --git a/src/smimemessage_p.cpp b/src/smimemessage_p.cpp new file mode 100644 index 0000000..82c7191 --- /dev/null +++ b/src/smimemessage_p.cpp @@ -0,0 +1,8 @@ +#include "smimemessage_p.h" + +using namespace SimpleMail; + +SMimeMessagePrivate::SMimeMessagePrivate() +{ + _smimePart = std::make_shared(); +} diff --git a/src/smimemessage_p.h b/src/smimemessage_p.h new file mode 100644 index 0000000..805112a --- /dev/null +++ b/src/smimemessage_p.h @@ -0,0 +1,18 @@ +#ifndef SMIMEMESSAGE_P_H +#define SMIMEMESSAGE_P_H + +#include "mimemessage_p.h" +#include "smimepart.h" + +namespace SimpleMail { + +class SMimeMessagePrivate : public MimeMessagePrivate +{ +public: + SMimeMessagePrivate(); + std::shared_ptr _smimePart; +}; + +} // namespace SimpleMail + +#endif // SMIMEMESSAGE_P_H diff --git a/src/smimepart.cpp b/src/smimepart.cpp new file mode 100644 index 0000000..82d071e --- /dev/null +++ b/src/smimepart.cpp @@ -0,0 +1,342 @@ +#include "smimepart.h" + +#include "mimemultipart.h" +#include "mimepart_p.h" +#include "smimepart_p.h" + +#include +#include +#include +#include + +#include +#include +#include + +Q_LOGGING_CATEGORY(SIMPLEMAIL_SMIMEPART, "simplemail.smimepart", QtInfoMsg) + +using namespace SimpleMail; + +SMimePart::SMimePart() + : MimePart(new SMimePartPrivate) +{ + initOpenSSL(); +} + +SMimePart::~SMimePart() +{ +} + +void SMimePart::setKeyFile(const QString &filename, const QString &password) +{ + SMimePartPrivate *d = static_cast(d_ptr.data()); + d->_keyfile = filename; + d->_password = password; + loadPKCS12PrivateKey(); +} + +void SMimePart::setPublicKey(const QString &filename) +{ + SMimePartPrivate *d = static_cast(d_ptr.data()); + d->_publicKeyfile = filename; + loadPKCS12PublicKey(); +} + +bool SMimePart::sign() +{ + SMimePartPrivate *d = static_cast(d_ptr.data()); + bool ret = false; + PKCS7 *p7 = nullptr; + + if (!d->_certificate || !d->_privateKey) { + qCDebug(SIMPLEMAIL_SMIMEPART) << "no certificate or private key"; + return ret; + } + + setSignedHeader(); + + int flags = PKCS7_DETACHED | PKCS7_STREAM | PKCS7_BINARY; + + BIO *out = NULL; + + if (!writeInputBuffer()) + goto err; + + /* Sign content */ + p7 = PKCS7_sign(d->_certificate.get(), d->_privateKey.get(), NULL, d->_input.get(), flags); + if (!p7) + goto err; + + out = BIO_new(BIO_s_mem()); + if (!out) + goto err; + + /* Write out S/MIME message */ + if (!SMIME_write_PKCS7(out, + p7, + d->_input.get(), + flags | + PKCS7_CRLFEOL)) // needed for intializing/finalizing SMIME structure + goto err; + + if (!handleData(p7, nullptr, 0)) + goto err; + + ret = true; +err: + if (!ret) { + qCDebug(SIMPLEMAIL_SMIMEPART) << "Error Signing Data"; + } + PKCS7_free(p7); + BIO_free(out); + return ret; +} + +bool SMimePart::encrypt() +{ + SMimePartPrivate *d = static_cast(d_ptr.data()); + bool ret = false; + PKCS7 *p7 = nullptr; + + if (!d->_recipsReceiver) { + qCDebug(SIMPLEMAIL_SMIMEPART) << "no public key"; + return ret; + } + + setEncryptionHeader(); + + int flags = PKCS7_STREAM; + + if (!writeInputBuffer()) + goto err; + + /* encrypt content */ + p7 = PKCS7_encrypt(d->_recipsReceiver.get(), d->_input.get(), EVP_des_ede3_cbc(), flags); + + if (!p7) + goto err; + + if (!handleData(p7, d->_input.get(), flags)) + goto err; + + ret = true; +err: + if (!ret) { + qCDebug(SIMPLEMAIL_SMIMEPART) << "Error Encrypting Data"; + } + PKCS7_free(p7); + return ret; +} + +bool SMimePart::signAndEncrypt() +{ + SMimePartPrivate *d = static_cast(d_ptr.data()); + bool ret = false; + PKCS7 *p7 = nullptr; + BIO *signedContent = nullptr; + if (!d->_certificate || !d->_privateKey) { + qCDebug(SIMPLEMAIL_SMIMEPART) << "no certificate or private key"; + return ret; + } + if (!d->_recipsReceiver) { + qCDebug(SIMPLEMAIL_SMIMEPART) << "no public key"; + return ret; + } + + setEncryptionHeader(); + + int flags = PKCS7_STREAM; + + if (!writeInputBuffer()) + goto err; + + /* Sign content */ + p7 = PKCS7_sign(d->_certificate.get(), d->_privateKey.get(), NULL, d->_input.get(), flags); + if (!p7) + goto err; + + signedContent = BIO_new(BIO_s_mem()); + if (!SMIME_write_PKCS7(signedContent, p7, d->_input.get(), flags | PKCS7_CRLFEOL)) + goto err; + + PKCS7_free(p7); + p7 = NULL; + p7 = PKCS7_encrypt(d->_recipsReceiver.get(), signedContent, EVP_des_ede3_cbc(), flags); + + if (!p7) + goto err; + + if (!handleData(p7, signedContent, flags)) + goto err; + + ret = true; +err: + if (!ret) { + qCDebug(SIMPLEMAIL_SMIMEPART) << "Error Signing/Encrypting Data"; + } + PKCS7_free(p7); + BIO_free(signedContent); + return ret; +} + +void SMimePart::setSignedHeader() +{ + Q_D(MimePart); + d->contentType = QByteArrayLiteral("application/pkcs7-signature; name=\"smime.p7s\""); + d->contentCharset.clear(); + d->contentEncoding = Base64; + d->contentIsBase64 = true; + + d->header.append("Content-Disposition: attachment; filename=\"smime.p7s\"\r\n"); +} + +void SMimePart::setEncryptionHeader() +{ + Q_D(MimePart); + d->contentType = QByteArrayLiteral( + "application/x-pkcs7-mime; smime-type=enveloped-data; name=\"smime.p7m\""); + d->contentCharset.clear(); + d->contentEncoding = Base64; + d->contentIsBase64 = true; + + d->header.append("Content-Disposition: attachment; filename=\"smime.p7m\"\r\n"); +} + +void SMimePart::loadPKCS12PrivateKey() +{ + SMimePartPrivate *d = static_cast(d_ptr.data()); + QFile file(d->_keyfile); + if (!file.exists()) + return; + file.open(QFile::ReadOnly); + QByteArray buffer = file.readAll(); + file.close(); + + BIO *keyBuffer = BIO_new(BIO_s_mem()); + if (keyBuffer == NULL) { + qCDebug(SIMPLEMAIL_SMIMEPART) << "Error opening file " << d->_keyfile; + return; + } + + BIO_write(keyBuffer, (void *) buffer.data(), buffer.length()); + PKCS12 *p12 = d2i_PKCS12_bio(keyBuffer, NULL); + BIO_free(keyBuffer); + + if (p12 == NULL) { + qCDebug(SIMPLEMAIL_SMIMEPART) << "Error reading PKCS#12 file"; + } + + EVP_PKEY *privateKey = nullptr; + X509 *certificate = nullptr; + STACK_OF(X509) *certificateCA = nullptr; + if (!PKCS12_parse( + p12, d->_password.toStdString().c_str(), &privateKey, &certificate, &certificateCA)) { + OSSL_PROVIDER_try_load(nullptr, "legacy", 1); + + if (!PKCS12_parse(p12, + d->_password.toStdString().c_str(), + &privateKey, + &certificate, + &certificateCA)) { + qCDebug(SIMPLEMAIL_SMIMEPART) << "Error parsing PKCS#12 file"; + } + } + d->_privateKey.reset(privateKey); + d->_certificate.reset(certificate); + d->_certificateCA.reset(certificateCA); + + PKCS12_free(p12); +} + +void SMimePart::loadPKCS12PublicKey() +{ + SMimePartPrivate *d = static_cast(d_ptr.data()); + QFile file(d->_publicKeyfile); + if (!file.exists()) + return; + file.open(QFile::ReadOnly); + QByteArray buffer = file.readAll(); + file.close(); + + BIO *keyBuffer = BIO_new(BIO_s_mem()); + if (keyBuffer == NULL) { + qCDebug(SIMPLEMAIL_SMIMEPART) << "Error opening file " << d->_publicKeyfile; + return; + } + BIO_write(keyBuffer, (void *) buffer.data(), buffer.length()); + + X509 *publicrcert = PEM_read_bio_X509(keyBuffer, NULL, 0, NULL); + BIO_free(keyBuffer); + + STACK_OF(X509) *recipsReceiver = sk_X509_new_null(); + + if (!recipsReceiver || !sk_X509_push(recipsReceiver, publicrcert)) { + return; + } + d->_recipsReceiver.reset(recipsReceiver); + publicrcert = nullptr; +} + +void SMimePart::initOpenSSL() +{ + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); +} + +bool SMimePart::writeInputBuffer() +{ + SMimePartPrivate *d = static_cast(d_ptr.data()); + BIO *input = BIO_new(BIO_s_mem()); + d->_input = std::unique_ptr(input); + if (!d->_input) + return false; + if (!BIO_write(d->_input.get(), (void *) d->_message.data(), d->_message.length())) + return false; + return true; +} + +bool SMimePart::writeMimeMessageBuffer(const std::shared_ptr &mimeParts) +{ + SMimePartPrivate *d = static_cast(d_ptr.data()); + QBuffer buffer; + buffer.open(QBuffer::ReadWrite); + + mimeParts->write(&buffer); + buffer.close(); + + d->_message = buffer.data(); + + return true; +} + +bool SMimePart::handleData(PKCS7 *p7, BIO *dataIn, int flags) +{ + QByteArray contentBuffer; + + // Buffer for signed and/or encrypted data; + BIO *encryptedData = BIO_new(BIO_s_mem()); + if (!encryptedData) + return false; + + BIO *b64 = BIO_new(BIO_f_base64()); + BIO *tmp = BIO_push(b64, encryptedData); + + i2d_ASN1_bio_stream(tmp, (ASN1_VALUE *) p7, dataIn, flags, ASN1_ITEM_rptr(PKCS7)); + + BIO_flush(tmp); + BIO_pop(tmp); + BIO_free(b64); + + char buffer[1024]; + while (BIO_get_line(encryptedData, &buffer[0], sizeof(buffer))) { + qCDebug(SIMPLEMAIL_SMIMEPART) << QString::fromLatin1(buffer); + if (strncmp(&buffer[0], "-----", strlen("-----")) != 0) { + contentBuffer += QByteArray(&buffer[0], strlen(buffer) - 1); + contentBuffer += QByteArray("\r\n"); + } + } + + setContent(contentBuffer); + BIO_free(encryptedData); + return true; +} diff --git a/src/smimepart.h b/src/smimepart.h new file mode 100644 index 0000000..e47fbc1 --- /dev/null +++ b/src/smimepart.h @@ -0,0 +1,42 @@ +#ifndef SMIMEPART_H +#define SMIMEPART_H + +#include "mimemessage.h" +#include "mimepart.h" + +#include + +#include + +namespace SimpleMail { +class SMimePart : public MimePart +{ +public: + SMimePart(); + virtual ~SMimePart(); + + void setKeyFile(const QString &filename, const QString &password); + + void setPublicKey(const QString &filename); + + bool sign(); + bool encrypt(); + bool signAndEncrypt(); + + bool writeMimeMessageBuffer(const std::shared_ptr &mimeParts); + +private: + void setSignedHeader(); + void setEncryptionHeader(); + + void loadPKCS12PrivateKey(); + void loadPKCS12PublicKey(); + + void initOpenSSL(); + + bool writeInputBuffer(); + bool handleData(PKCS7 *p7, BIO *dataIn, int flags); +}; +} // namespace SimpleMail + +#endif // SMIME_H diff --git a/src/smimepart_p.cpp b/src/smimepart_p.cpp new file mode 100644 index 0000000..ff72b0c --- /dev/null +++ b/src/smimepart_p.cpp @@ -0,0 +1,12 @@ +#include "smimepart_p.h" + +using namespace SimpleMail; + +SMimePartPrivate::~SMimePartPrivate() +{ + _input = nullptr; + _privateKey = nullptr; + _certificate = nullptr; + _certificateCA = nullptr; + _recipsReceiver = nullptr; +} diff --git a/src/smimepart_p.h b/src/smimepart_p.h new file mode 100644 index 0000000..1f11b48 --- /dev/null +++ b/src/smimepart_p.h @@ -0,0 +1,62 @@ +#ifndef SMIMEPART_P_H +#define SMIMEPART_P_H + +#include "mimepart_p.h" + +#include +#include + +template <> +class std::default_delete +{ +public: + void operator()(BIO *ptr) const { BIO_free(ptr); } +}; + +template <> +class std::default_delete +{ +public: + void operator()(EVP_PKEY *ptr) const { EVP_PKEY_free(ptr); } +}; + +template <> +class std::default_delete +{ +public: + void operator()(X509 *ptr) const { X509_free(ptr); } +}; + +template <> +class std::default_delete +{ +public: + void operator()(STACK_OF(X509) * ptr) const { sk_X509_pop_free(ptr, X509_free); } +}; + +namespace SimpleMail { +class SMimePartPrivate : public MimePartPrivate +{ +public: + virtual ~SMimePartPrivate(); + + QByteArray _message; + QString _keyfile; + QString _publicKeyfile; + QString _password; + + // private key for signing + std::unique_ptr _privateKey = nullptr; + + // certificate from private Key + std::unique_ptr _certificate = nullptr; + std::unique_ptr _certificateCA = nullptr; + // certificate from public key, used for encrypting + std::unique_ptr _recipsReceiver = nullptr; + + // buffer to be signed/encrypted + std::unique_ptr _input; +}; + +} // namespace SimpleMail +#endif // SMIMEPART_P_H