提问者:小点点

用于CMS/X.509操作的C库


一些上下文:我有一个符合PKCS#11的加密引擎。该引擎将用于处理签名/封装数据,即验证数据的ECDSA/SHA1签名,使用RSAES-OAEP打开对称密钥,并解密此数据。这意味着对称密钥将与我的引擎的公钥包装在一起: <罢工> 因此,我希望此公钥的证书实际上为“主题公钥算法:RSAES-OAEP” 罢工。

我正在寻找一个C库,它可以让我通过以下方式操作符合加密消息语法(CMS)和X.509标准的对象:

  1. 创建一个X.509证书签名请求(CSR), <罢工> 将主题公钥算法设置为RSAES-OAEP

>

  • 让我来处理签名部分:我的私钥只能通过PKCS#11句柄访问,所以我需要库给我签名的字节,然后让我用我的加密引擎计算的内容设置CSR的ProofOfPossession字段

    将完整的CSR导出到某物(DER或PEM)

    创建CMS结构来保存像SignedData(EnvelopedData(东西))这样的东西。该库可以处理实际的加密/密钥包装/签名,或者它可以让其他软件引擎来做,只允许我设置八位字节字符串

    让我轻松解析回消息并恢复那些八位字节字符串

    • 意思是我想打开一个包含CMS消息的DER/PEM文件,并获取签名的字节、包装的密钥和加密的东西,这样我就可以将它们提供给我的PKCS#11接口

    在任何人建议OpenSSL的libcrypto之前,我已经研究过它(看起来像,“上周花了一周时间试图理解结构是如何工作的,ASN.1表示是如何工作的,我如何从OpenSSL的结构中恢复我感兴趣的字节……”),我对它有一些问题(截至1.0.1f):

    >

  • <罢工> (参见1。)我无法将主题公钥算法设置为RSAE-OAEP。从 demos/x509/mkreq. c,一直追溯到 x509t. h很奇怪 #定义IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(stname, itname,fname)宏,我想我可以肯定 X509_REQ_set_pubkey()不能处理OAEP。 罢工

    cms_RecipientInfo_ktri_encrypt()调用EVP_PKEY_CTX_ctrl(),我想它解析为crypto/rsa/rsa_pmeth. c:pkey_rsa_ctrl():当p2是EVP_PKEY_CTRL_CMS_ENCRYPT时,p2不被解析,所以RSA填充没有设置(如果结果证明我错了,我只是无法正确阅读代码,请告诉我)。

    因此,虽然我很高兴这家伙设法让它“像魅力一样工作”,但我不能分享他的热情。

    我猜是2。我可以使用OpenSSL创建CMS空白结构,计算未来的EnvelopedData内容(即用RSAOAEP包装的对称密钥加密的内容),并将这些内容填充到结构中,完全绕过CMS_encrypt(),然后再封装到SignedData中(我假设CMS_sign()将处理ECDSA/SHA1)。只要我不想为OAEP使用花哨的参数(尽管另一个人设法修补了库以使用SHA-256的OAEP)。

    但是这可能最终需要对OpenSSL的ASN.1API进行太多的摆弄。因此我的问题是:有没有人知道一个C库来构建CMS结构并向它们提供其他引擎计算的八位字节字符串? <罢工> 还有如何构建写着“This KEY Is Meant to be use with RSAES-OAEP”的证书。 罢工

    我已经研究了libksba和cryptlib,虽然我想它们可以工作,但我还不知道如何使用它们(可能与我盯着OpenSSL的代码看得眼睛流血有关——我并不是说OpenSSL的代码不好或什么的,只是我一直在努力研究它,文档有点缺乏)。

    实际上,我想,我可以放弃C要求(主要是因为与加密引擎的通信是在PKCS#11/C中完成的)。这是库应该能够做的:

    >

  • 建立CSR

    <罢工> …以“RSAES-OAEP”作为主题公钥算法 罢工

    给我签署所有权证明部分的字节

    获取签名并输出完整的X.509CSR

    解析CMS结构

    (SignedData)给我signedInfo对应的字节-

    (EnvelopedData)给我keyTransRecipientInfo-对应的字节

    建立一个CMS结构,要么…

    让一些外部引擎设置上面提到的字段,并让我指定算法

    实际实现算法,并仅从数据构建CMS(…使用RSAES-OAEP进行密钥包装)

    现在我正在使用“全OpenSSL”方法,因为我觉得我陷得太深了,不应该开始在其他地方游荡。如果有人有更好的主意,我洗耳恭听。

    <罢工> 至于设置主题公钥算法…好吧,要么我离开常规RSA,让我的应用程序“知道”包装是RSAES-OAEP,要么…我不知道。 至于签署请求…POP有用吗?(这不是一个严肃的问题)

    NB:编辑删除整个我想要我的证书读OAEP,因为我刚刚发现RFC5756(也发现了一个有趣的讨论从2006当这个RFC还没有出来)。


  • 共1个答案

    匿名用户

    到目前为止,我设法做到了这一点。

    我主要遵循demos/x509/mqreq. c,有一些曲折。

    (NB:错误检查,为简洁起见,忽略了花哨的模数长度/标签/主题DN生成/处理,并专注于实际流程)。

    unsigned char* mod = NULL;
    unsigned char* exp = NULL;
    size_t mod_l = 0;
    size_t exp_l = 0;
    
    P11_handle h_key = P11_gen_rsa(&mod, &mod_l, &exp, &exp_l);
    
    RSA* rsa = RSA_new();
    
    rsa->n = BN_bin2bn(rsa_mod, rsa_mod_l, NULL);
    rsa->e = BN_bin2bn(rsa_exp, rsa_exp_l, NULL);
    
    EVP_PKEY* pkey = EVP_PKEY_new();
    
    EVP_PKEY_assign_RSA(pkey, rsa);
    
    X509_REQ* csr = X509_REQ_new();
    X509_REQ_set_pubkey(csr, pkey);
    
    /* Boring X509_NAME/X509_EXTENSION stuff */
    
    X509_REQ_INFO* csr_req = csr->req_info;
    
    unsigned char* pop_in = NULL;
    size_t pop_in_l = ASN1_item_i2d((void*)csr_req, &pop_in,
                                    ASN1_ITEM_rptr(X509_REQ_INFO));
    
    unsigned char* sig = NULL;
    size_t sig_l = 0;
    
    P11_make_pop(SIGN_RSA_PKCS, DIGEST_SHA256,
                 pop_in, pop_in_l, &sig, &sig_l,
                 h_key);
    
    /* Add signature to CSR (heavily inspired from ASN1_item_sign_ctx())
     * (please don't ask about the flags) */
    
    if (csr->signature->data != NULL) OPENSSL_free(csr->signature->data);
    csr->signature->data = sig;
    csr->signature->length = sig_l;
    csr->signature->flags&= ~(ASN1_STRING_FLAG_BITS_LEFT|0x07);
    csr->signature->flags|= ASN1_STRING_FLAG_BITS_LEFT;
    
    /* Add signature algorithm information to CSR */
    
    int sig_algo_nid = 0;
    OBJ_find_sigid_by_algs(&sig_algo_nid,
                           EVP_MD_nid(EVP_sha256()), EVP_PKEY_RSA);
    
    X509_ALGOR_set0(csr->sig_alg, OBJ_nid2obj(sig_algo_nid),
                    V_ASN1_NULL, NULL));
    

    在那之后,X509_REQ结构对PEM导出是好的。openssl req-验证似乎验证了这个过程,所以就我而言,这是可行的。

    最终得到了它,使用1.0.2(任何以前的版本都需要修补或ASN.1级解析)。非常感谢Stephen Henson博士和Tom Francis通过邮件列表帮助我解决这个问题。

    /* Make EnvelopedData structure */
    BIO* in = BIO_new_file(in_path, "rb");
    
    int flags = CMS_BINARY | CMS_USE_KEYID | CMS_PARTIAL | CMS_KEY_PARAM;
    
    CMS_ContentInfo* edata = CMS_encrypt(NULL, NULL, cipher, flags);
    
    CMS_RecipientInfo* r_info = CMS_add1_recipient_cert(edata, r_cert, flags);
    EVP_PKEY_CTX* wrap_ctx = CMS_RecipientInfo_get0_pkey_ctx(r_info);
    
    EVP_PKEY_CTX_set_rsa_padding(wrap_ctx, RSA_PKCS1_OAEP_PADDING);
    EVP_PKEY_CTX_set_rsa_oaep_md(wrap_ctx, EVP_sha256());
    EVP_PKEY_CTX_set_rsa_mgf1_md(wrap_ctx, EVP_sha256());
    EVP_PKEY_CTX_set0_rsa_oaep_label(wrap_ctx, oaep_label, oaep_label_l);
    /* NB: oaep_label must be heap-allocated, and will be freed by OSSL */
    
    CMS_final(edata, in, NULL, flags);
    
    BIO* tmp = BIO_new(BIO_s_mem());
    i2d_CMS_bio(tmp, edata);
    
    /* Make SignedData structure */
    
    flags|= CMS_NOSMIMECAP | CMS_NOCERTS;
    flags&= ~(CMS_KEY_PARAM);
    
    CMS_ContentInfo* sdata = CMS_sign(NULL, NULL, NULL, NULL, flags);
    
    ASN1_OBJECT* ectype_edata = OBJ_nid2obj(NID_pkcs7_enveloped);
    CMS_set1_eContentType(sdata, ectype_edata);
    
    CMS_SignerInfo* s_info =
        CMS_add1_signer(sdata, s_cert, s_key, NULL, flags);
    
    CMS_SignerInfo_sign(s_info);
    
    CMS_final(sdata, tmp, NULL, flags);
    
    BIO* out = BIO_new_file(out_path, "wb");
    i2d_CMS_bio(out, sdata);
    BIO_flush(out);
    

    我基本上编写了自己的CMS解析器。当你知道规范的时候,ASN.1实际上很容易解析。我在RFC尝试过使用一些“ASN.1到C结构”编译器编译ASN.1模块,但没有成功(他们一直被语法窒息)。

    相关问题