#include <stdio.h>
#include "cryptlib.h"
#include <openssl/x509.h>
#include <openssl/x509v3.h>
PKCS7 *PKCS7_sign(X509 *signcert, EVP_PKEY *pkey, STACK_OF(X509) *certs,
BIO *data, int flags)
{
PKCS7 *p7 = NULL;
PKCS7_SIGNER_INFO *si;
BIO *p7bio = NULL;
STACK_OF(X509_ALGOR) *smcap = NULL;
int i;
if(!X509_check_private_key(signcert, pkey)) {
PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
return NULL;
}
if(!(p7 = PKCS7_new())) {
PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE);
return NULL;
}
if (!PKCS7_set_type(p7, NID_pkcs7_signed))
goto err;
if (!PKCS7_content_new(p7, NID_pkcs7_data))
goto err;
if (!(si = PKCS7_add_signature(p7,signcert,pkey,EVP_sha1()))) {
PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PKCS7_ADD_SIGNATURE_ERROR);
goto err;
}
if(!(flags & PKCS7_NOCERTS)) {
if (!PKCS7_add_certificate(p7, signcert))
goto err;
if(certs) for(i = 0; i < sk_X509_num(certs); i++)
if (!PKCS7_add_certificate(p7, sk_X509_value(certs, i)))
goto err;
}
if(!(flags & PKCS7_NOATTR)) {
if (!PKCS7_add_signed_attribute(si, NID_pkcs9_contentType,
V_ASN1_OBJECT, OBJ_nid2obj(NID_pkcs7_data)))
goto err;
if(!(flags & PKCS7_NOSMIMECAP))
{
if(!(smcap = sk_X509_ALGOR_new_null())) {
PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE);
goto err;
}
#ifndef OPENSSL_NO_DES
if (!PKCS7_simple_smimecap (smcap, NID_des_ede3_cbc, -1))
goto err;
#endif
#ifndef OPENSSL_NO_RC2
if (!PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 128))
goto err;
if (!PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 64))
goto err;
#endif
#ifndef OPENSSL_NO_DES
if (!PKCS7_simple_smimecap (smcap, NID_des_cbc, -1))
goto err;
#endif
#ifndef OPENSSL_NO_RC2
if (!PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 40))
goto err;
#endif
if (!PKCS7_add_attrib_smimecap (si, smcap))
goto err;
sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free);
smcap = NULL;
}
}
if(flags & PKCS7_DETACHED)PKCS7_set_detached(p7, 1);
if (flags & PKCS7_STREAM)
return p7;
if (!(p7bio = PKCS7_dataInit(p7, NULL))) {
PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE);
goto err;
}
SMIME_crlf_copy(data, p7bio, flags);
if (!PKCS7_dataFinal(p7,p7bio)) {
PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PKCS7_DATASIGN);
goto err;
}
BIO_free_all(p7bio);
return p7;
err:
sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free);
BIO_free_all(p7bio);
PKCS7_free(p7);
return NULL;
}
int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store,
BIO *indata, BIO *out, int flags)
{
STACK_OF(X509) *signers;
X509 *signer;
STACK_OF(PKCS7_SIGNER_INFO) *sinfos;
PKCS7_SIGNER_INFO *si;
X509_STORE_CTX cert_ctx;
char buf[4096];
int i, j=0, k, ret = 0;
BIO *p7bio;
BIO *tmpin, *tmpout;
if(!p7) {
PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_INVALID_NULL_POINTER);
return 0;
}
if(!PKCS7_type_is_signed(p7)) {
PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_WRONG_CONTENT_TYPE);
return 0;
}
if(PKCS7_get_detached(p7) && !indata) {
PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_NO_CONTENT);
return 0;
}
#if 0
if(!PKCS7_get_detached(p7) && indata) {
PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_CONTENT_AND_DATA_PRESENT);
return 0;
}
#endif
sinfos = PKCS7_get_signer_info(p7);
if(!sinfos || !sk_PKCS7_SIGNER_INFO_num(sinfos)) {
PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_NO_SIGNATURES_ON_DATA);
return 0;
}
signers = PKCS7_get0_signers(p7, certs, flags);
if(!signers) return 0;
if (!(flags & PKCS7_NOVERIFY)) for (k = 0; k < sk_X509_num(signers); k++) {
signer = sk_X509_value (signers, k);
if (!(flags & PKCS7_NOCHAIN)) {
if(!X509_STORE_CTX_init(&cert_ctx, store, signer,
p7->d.sign->cert))
{
PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_X509_LIB);
sk_X509_free(signers);
return 0;
}
X509_STORE_CTX_set_default(&cert_ctx, "smime_sign");
} else if(!X509_STORE_CTX_init (&cert_ctx, store, signer, NULL)) {
PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_X509_LIB);
sk_X509_free(signers);
return 0;
}
if (!(flags & PKCS7_NOCRL))
X509_STORE_CTX_set0_crls(&cert_ctx, p7->d.sign->crl);
i = X509_verify_cert(&cert_ctx);
if (i <= 0) j = X509_STORE_CTX_get_error(&cert_ctx);
X509_STORE_CTX_cleanup(&cert_ctx);
if (i <= 0) {
PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR);
ERR_add_error_data(2, "Verify error:",
X509_verify_cert_error_string(j));
sk_X509_free(signers);
return 0;
}
}
if (indata && (BIO_method_type(indata) == BIO_TYPE_MEM))
{
char *ptr;
long len;
len = BIO_get_mem_data(indata, &ptr);
tmpin = BIO_new_mem_buf(ptr, len);
if (tmpin == NULL)
{
PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_MALLOC_FAILURE);
return 0;
}
}
else
tmpin = indata;
if (!(p7bio=PKCS7_dataInit(p7,tmpin)))
goto err;
if(flags & PKCS7_TEXT) {
if(!(tmpout = BIO_new(BIO_s_mem()))) {
PKCS7err(PKCS7_F_PKCS7_VERIFY,ERR_R_MALLOC_FAILURE);
goto err;
}
BIO_set_mem_eof_return(tmpout, 0);
} else tmpout = out;
for (;;)
{
i=BIO_read(p7bio,buf,sizeof(buf));
if (i <= 0) break;
if (tmpout) BIO_write(tmpout, buf, i);
}
if(flags & PKCS7_TEXT) {
if(!SMIME_text(tmpout, out)) {
PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_SMIME_TEXT_ERROR);
BIO_free(tmpout);
goto err;
}
BIO_free(tmpout);
}
if (!(flags & PKCS7_NOSIGS))
for (i=0; i<sk_PKCS7_SIGNER_INFO_num(sinfos); i++)
{
si=sk_PKCS7_SIGNER_INFO_value(sinfos,i);
signer = sk_X509_value (signers, i);
j=PKCS7_signatureVerify(p7bio,p7,si, signer);
if (j <= 0) {
PKCS7err(PKCS7_F_PKCS7_VERIFY,PKCS7_R_SIGNATURE_FAILURE);
goto err;
}
}
ret = 1;
err:
if (tmpin == indata)
{
if (indata) BIO_pop(p7bio);
}
BIO_free_all(p7bio);
sk_X509_free(signers);
return ret;
}
STACK_OF(X509) *PKCS7_get0_signers(PKCS7 *p7, STACK_OF(X509) *certs, int flags)
{
STACK_OF(X509) *signers;
STACK_OF(PKCS7_SIGNER_INFO) *sinfos;
PKCS7_SIGNER_INFO *si;
PKCS7_ISSUER_AND_SERIAL *ias;
X509 *signer;
int i;
if(!p7) {
PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,PKCS7_R_INVALID_NULL_POINTER);
return NULL;
}
if(!PKCS7_type_is_signed(p7)) {
PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,PKCS7_R_WRONG_CONTENT_TYPE);
return NULL;
}
sinfos = PKCS7_get_signer_info(p7);
if(sk_PKCS7_SIGNER_INFO_num(sinfos) <= 0) {
PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,PKCS7_R_NO_SIGNERS);
return NULL;
}
if(!(signers = sk_X509_new_null())) {
PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,ERR_R_MALLOC_FAILURE);
return NULL;
}
for (i = 0; i < sk_PKCS7_SIGNER_INFO_num(sinfos); i++)
{
si = sk_PKCS7_SIGNER_INFO_value(sinfos, i);
ias = si->issuer_and_serial;
signer = NULL;
if (certs) signer = X509_find_by_issuer_and_serial (certs,
ias->issuer, ias->serial);
if (!signer && !(flags & PKCS7_NOINTERN)
&& p7->d.sign->cert) signer =
X509_find_by_issuer_and_serial (p7->d.sign->cert,
ias->issuer, ias->serial);
if (!signer) {
PKCS7err(PKCS7_F_PKCS7_GET0_SIGNERS,PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND);
sk_X509_free(signers);
return NULL;
}
if (!sk_X509_push(signers, signer)) {
sk_X509_free(signers);
return NULL;
}
}
return signers;
}
PKCS7 *PKCS7_encrypt(STACK_OF(X509) *certs, BIO *in, const EVP_CIPHER *cipher,
int flags)
{
PKCS7 *p7;
BIO *p7bio = NULL;
int i;
X509 *x509;
if(!(p7 = PKCS7_new())) {
PKCS7err(PKCS7_F_PKCS7_ENCRYPT,ERR_R_MALLOC_FAILURE);
return NULL;
}
if (!PKCS7_set_type(p7, NID_pkcs7_enveloped))
goto err;
if(!PKCS7_set_cipher(p7, cipher)) {
PKCS7err(PKCS7_F_PKCS7_ENCRYPT,PKCS7_R_ERROR_SETTING_CIPHER);
goto err;
}
for(i = 0; i < sk_X509_num(certs); i++) {
x509 = sk_X509_value(certs, i);
if(!PKCS7_add_recipient(p7, x509)) {
PKCS7err(PKCS7_F_PKCS7_ENCRYPT,
PKCS7_R_ERROR_ADDING_RECIPIENT);
goto err;
}
}
if(!(p7bio = PKCS7_dataInit(p7, NULL))) {
PKCS7err(PKCS7_F_PKCS7_ENCRYPT,ERR_R_MALLOC_FAILURE);
goto err;
}
SMIME_crlf_copy(in, p7bio, flags);
(void)BIO_flush(p7bio);
if (!PKCS7_dataFinal(p7,p7bio)) {
PKCS7err(PKCS7_F_PKCS7_ENCRYPT,PKCS7_R_PKCS7_DATAFINAL_ERROR);
goto err;
}
BIO_free_all(p7bio);
return p7;
err:
BIO_free_all(p7bio);
PKCS7_free(p7);
return NULL;
}
int PKCS7_decrypt(PKCS7 *p7, EVP_PKEY *pkey, X509 *cert, BIO *data, int flags)
{
BIO *tmpmem;
int ret, i;
char buf[4096];
if(!p7) {
PKCS7err(PKCS7_F_PKCS7_DECRYPT,PKCS7_R_INVALID_NULL_POINTER);
return 0;
}
if(!PKCS7_type_is_enveloped(p7)) {
PKCS7err(PKCS7_F_PKCS7_DECRYPT,PKCS7_R_WRONG_CONTENT_TYPE);
return 0;
}
if(cert && !X509_check_private_key(cert, pkey)) {
PKCS7err(PKCS7_F_PKCS7_DECRYPT,
PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
return 0;
}
if(!(tmpmem = PKCS7_dataDecode(p7, pkey, NULL, cert))) {
PKCS7err(PKCS7_F_PKCS7_DECRYPT, PKCS7_R_DECRYPT_ERROR);
return 0;
}
if (flags & PKCS7_TEXT) {
BIO *tmpbuf, *bread;
if(!(tmpbuf = BIO_new(BIO_f_buffer()))) {
PKCS7err(PKCS7_F_PKCS7_DECRYPT, ERR_R_MALLOC_FAILURE);
BIO_free_all(tmpmem);
return 0;
}
if(!(bread = BIO_push(tmpbuf, tmpmem))) {
PKCS7err(PKCS7_F_PKCS7_DECRYPT, ERR_R_MALLOC_FAILURE);
BIO_free_all(tmpbuf);
BIO_free_all(tmpmem);
return 0;
}
ret = SMIME_text(bread, data);
BIO_free_all(bread);
return ret;
} else {
for(;;) {
i = BIO_read(tmpmem, buf, sizeof(buf));
if(i <= 0) break;
BIO_write(data, buf, i);
}
BIO_free_all(tmpmem);
return 1;
}
}