2016-06-28 20 views
2

S/MIMEを使用してメールに署名するために少しメールmilterを書き込もうとします。これまでのところ、私はメールにサインアップするまでにコードを完成させました。私は仕事をするためにopensslのデモ/スミムコードの例を使用しました。残念ながら、出力ファイルに入力メッセージを書き込む方法を示していますが、結果は文字列として必要です。PKCS7_signの結果をchar *またはstd :: stringに取得する方法

これは私のSMIME-方法である:opensslのための1600の以上のmanページがあるので

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; 
} 

、私はどこの情報を探すには考えています。

私は "p7"を使い、単純なstd :: string(または必要に応じてchar *)に書きたいと思います。私が書いたmilterアプリケーションは、この文字列をピックアップし、チェンジボディを行います(まだ書かれていませんが、これは私の考えです)。

誰かがルーチン/マニュアルページを指すことができますか、私に役立つコード例がありますか?事前に

おかげ

+0

私はあなたが '文字でそれを置くことができるとは思わない*'切り捨てう埋め込ま 'NULL'、があるかもしれないので、結果。 – jww

+0

このコード例は、BIO_new_file( "smout.txt"、 "w")を呼び出します。 ... SMIME_write_PKCS7(out、p7、in、flags) 結果はbase64でエンコードされます。私はかなり確信しています、それは仕事をするだろう1つまたは2つの機能だけです。しかしどちら? :-) –

答えて

2

私は「P7」を使用するのが大好きと(必要な場合、またはcharの*)簡単なのstd ::文字列に書くでしょう。私が書いたmilterアプリケーションは、この文字列をピックアップし、チェンジボディを行います(まだ書かれていませんが、これは私の考えです)。

埋め込まNULLがあるかもしれないので、私は結果を切り捨てることになる、あなたはchar*にそれを置くことができるとは思いません。

std::stringと、ASN.1/DERには(1)i2d_PKCS7_bio、PEMには(2)PEM_write_bio_PKCS7のいずれかを使用します。あなたはいつものようにライブラリを使用して、MEM_BIOに出力を書き込んでからBUF_MEMを使ってバイオの内容を取得するという考えです。 BUF_MEMには、データへのポインタとその長さが格納されます。以下のような何か...

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)); 

あなたはPEM_write_bio_PKCS7char*を使用場合は、PEMエンコードが終了NULLに欠けます。そのC文字列ではないので、必ず考慮してください。 Non-printable character after generating random n-byte Base64 stringを参照してください。これは、エンコードされていないNULLを書き込む方法について説明しています。


opensslのための1600の以上のmanページがあるので、私はどこの情報を探すには考えている...

アウトサブコマンドのソースコード。これは、ライブラリがAPIで何をするかを示しています。たとえば、openssl pkcs7を使用する場合は、pkcs7アプリを使用します。 unique_ptrを使用して

$ 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 

デストラクタ機能を持つオブジェクトが自動的にクリーンアップされることを保証し、それはきれいなコードを保つことができます。私は、OpenSSLがC++でパスを横切るたびに使用しようとしています(別の例ではHow to generate RSA private key using opensslを参照)。

はここでOpenSSLを使用して、私のC++プロジェクトの一つから何か:

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)>; 
+0

ありがとうございます。これはとても素晴らしいです。特にスマートポインタの使用。あなたの例を読んで、私はすばらしいことを学びました。私は明日あなたの提案に従います –

関連する問題