【发布时间】:2016-10-30 20:11:00
【问题描述】:
我尝试编写一个小邮件过滤器来使用 S/MIME 对邮件进行签名。到目前为止,我已经完成了对邮件进行签名的代码。我使用了 openssl 中的 demos/smime 代码示例来完成这项工作。不幸的是,这些示例演示了如何将输入消息写入输出文件,但我需要将结果作为字符串。
这是我的 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;
}
由于 openssl 的手册页超过 1600 个,我不知道在哪里查找信息。
我很想使用“p7”并将其写入一个简单的 std::string(或 char *,如果需要)。我编写的 milter 应用程序将获取此字符串并执行 change-body(尚未编写,但这是我的想法)。
有人可以指出我的例程/手册页,或者有一个可以帮助我的代码示例吗?
提前致谢
【问题讨论】:
-
我不相信你可以把它放在
char*中,因为可能有一个嵌入的NULL,这会截断结果。 -
示例代码调用 = BIO_new_file("smout.txt", "w"); ... SMIME_write_PKCS7(out, p7, in, flags) 结果是 base64 编码的。我很确定,只有一两个功能可以完成这项工作。但是哪个? :-)