如何获得PKCS7_sign结果为char *或std :: string [英] How to get PKCS7_sign result into a char * or std::string

查看:620
本文介绍了如何获得PKCS7_sign结果为char *或std :: string的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试写一个小邮件以使用S / MIME签名邮件。到目前为止,我已经完成了对代码的签名邮件。我在openssl中使用了demos / smime代码示例来完成这项工作。不幸的是,这些示例演示了如何将输入消息写入输出文件,但我需要将结果作为字符串。



这是我的Smime方法:

  void Smime :: sign(){
if(!isLoaded())
return;

//空邮件或未知
if(mailFrom.empty())
return;

auto * client = util :: mlfipriv(ctx);
bool signedOrEncrypted = false;
std :: vector< std :: string>内容类型;

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:
*捕获更多案例,其中电子邮件已经可以被加密
*或在别处签名。
* /

mapfile :: Map email {mailFrom};

auto cert = fs :: path(email.getSmimeFilename< mapfile :: Smime :: CERT>());
auto key = fs :: path(email.getSmimeFilename< mapfile :: Smime :: KEY>());

if(!fs :: exist(cert)&&!fs :: is_regular(cert))
return;
if(!fs :: exists(key)&&!fs :: is_regular(key))
return;

//签名从这里开始

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证书
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键
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<< 错误:未加载cert或密钥< std :: endl;
return;
}

//从临时文件加载邮件内容
in = BIO_new_file(client-> getTempFile()。c_str(),r);

if(!in){
std :: cerr<< 错误:无法从临时文件加载内容
<< std :: endl;
return;
}

//签名
p7 = PKCS7_sign(scert,skey,nullptr,in,flags);

if(!p7){
std :: cerr<< 错误:邮件无法签名< std :: endl;
return;
}

//清除
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应用程序将拾起这个字符串,并做一个改变体(还没有写,但这是我的想法)。



有人可能指向例程/

解决方案

/ div>


我想使用p7并将其写入一个简单的std :: string(或char *,如果需要)。我写的milter应用程序将拾起这个字符串,并做一个改变体(尚未写,但这是我的想法)。


我不相信你可以把它放在 char * ,因为可能有一个嵌入的 NULL 截断结果。



使用 std :: string 和(1) i2d_PKCS7_bio 用于PEM的ASN.1 / DER或(2) PEM_write_bio_PKCS7 。想法是你像往常一样使用库,将输出写入 MEM_BIO ,然后使用 BUF_MEM 获取bio的内容>。 BUF_MEM 包含指向数据及其长度的指针。类似...

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

如果 c> PEM_write_bio_PKCS7 和 char * ,则PEM编码将缺少终止 NULL 。一定要说明它,因为它不是一个C字符串。另请参阅生成随机n字节Base64字符串后的不可打印字符,其中讨论了如何在不编码的情况下写入NULL。 / p>





由于openssl有超过1600个手册页,我不知道在哪里查找


检查子命令的源代码。它显示了库如何使用API​​做事情。例如,当您使用 openssl pkcs7 时,它使用 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






使用 unique_ptr 和dtor函数可以确保对象被自动清理,并且有助于保持代码的清洁。当OpenSSL使用C ++穿越路径时,我尝试使用它(请参阅另一个示例,如何使用openssl生成RSA私钥)。



以下是我使用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)> ;;

使用DH_ptr = std :: unique_ptr< DH,decltype(& :: DH_free)> ;;

使用RSA_ptr = std :: unique_ptr< RSA,decltype(& :: RSA_free)> ;;

使用DSA_ptr = std :: unique_ptr< DSA,decltype(& :: DSA_free)> ;;

使用EVP_PKEY_ptr = std :: unique_ptr< EVP_PKEY,decltype(& :: EVP_PKEY_free)> ;;

使用BN_ptr = std :: unique_ptr< BIGNUM,decltype(& :: BN_free)> ;;

using FILE_ptr = std :: unique_ptr< FILE,decltype(& :: fclose)>

使用BIO_MEM_ptr = std :: unique_ptr< BIO,decltype(& :: BIO_free)> ;;
using BIO_FILE_ptr = std :: unique_ptr< BIO,decltype(& :: BIO_free)> ;;

使用EVP_MD_CTX_ptr = std :: unique_ptr< EVP_MD_CTX,decltype(& :: EVP_MD_CTX_destroy)> ;;

使用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)> ;;
使用X509_EXTENSION_ptr = std :: unique_ptr< X509_EXTENSION,decltype(& :: X509_EXTENSION_free)> ;;

使用X509_NAME_ptr = std :: unique_ptr< X509_NAME,decltype(& :: X509_NAME_free)> ;;
使用X509_NAME_ENTRY_ptr = std :: unique_ptr< X509_NAME_ENTRY,decltype(& :: X509_NAME_ENTRY_free)> ;;

使用X509_STORE_ptr = std :: unique_ptr< X509_STORE,decltype(& :: X509_STORE_free)> ;;
使用X509_LOOKUP_ptr = std :: unique_ptr< X509_LOOKUP,decltype(& :: X509_LOOKUP_free)> ;;
使用X509_STORE_CTX_ptr = std :: unique_ptr< X509_STORE_CTX,decltype(& :: X509_STORE_CTX_free)> ;;


I try to write a little mail milter to sign mails with S/MIME. So far, I have completed to the code up to signing the mail. I used the demos/smime code examples in openssl to do the job. Unfortunately the examples demonstrate how to write a input message to an output file, but I need the result as a string.

This is my Smime-method:

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

As there are more than 1600 man pages for openssl, I have no idea where to look for information.

I would love to use the "p7" and write it to a simple std::string (or char *, if required). The milter application I write will pick up this string and does a change-body (Not yet written, but this is my idea).

Can somebody point me to routines/man pages or does have a code example that may help me?

Thanks in advance

解决方案

I would love to use the "p7" and write it to a simple std::string (or char *, if required). The milter application I write will pick up this string and does a change-body (Not yet written, but this is my idea).

I don't believe you can put it in a char* because there may be an embedded NULL, which would truncate the result.

Use a std::string and either (1) i2d_PKCS7_bio for ASN.1/DER or (2) PEM_write_bio_PKCS7 for PEM. The idea is you use the library as usual, write output to a MEM_BIO and then get the contents of the bio using BUF_MEM. The BUF_MEM holds a pointer to the data and its length. Something like...

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

If you use PEM_write_bio_PKCS7 and a char*, then the PEM encoding will lack the terminating NULL. Be sure to account for it because its not a C-string. Also see Non-printable character after generating random n-byte Base64 string, which discusses how to write a NULL without it being encoded.


As there are more than 1600 man pages for openssl, I have no idea where to look for information...

Checkout the source code for the subcommands. It shows you how the library does things with the API. For example, when you use openssl pkcs7, it uses the pkcs7 app.

$ 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


Using unique_ptr with the dtor function ensures the objects are automatically cleaned up, and it helps keep the code clean. I try to use it whenever OpenSSL crosses paths with C++ (see How to generate RSA private key using openssl for another example).

Here's something from one of my C++ projects which uses 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)>;

这篇关于如何获得PKCS7_sign结果为char *或std :: string的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆