2 * Copyright (C) 2012 Martin Willi
3 * Copyright (C) 2012 revosec AG
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 #include <openssl/opensslconf.h>
18 #ifndef OPENSSL_NO_CMS
20 #include "openssl_pkcs7.h"
21 #include "openssl_util.h"
24 #include <utils/debug.h>
26 #include <credentials/sets/mem_cred.h>
28 #include <openssl/cms.h>
30 typedef struct private_openssl_pkcs7_t private_openssl_pkcs7_t
;
33 * Private data of an openssl_pkcs7_t object.
35 struct private_openssl_pkcs7_t
{
38 * Public pkcs7_t interface.
43 * Type of this container
45 container_type_t type
;
48 * OpenSSL CMS structure
54 * OpenSSL does not allow us to read the signature to verify it with our own
55 * crypto API. We define the internal CMS_SignerInfo structure here to get it.
57 struct CMS_SignerInfo_st
{
60 X509_ALGOR
*digestAlgorithm
;
61 STACK_OF(X509_ATTRIBUTE
) *signedAttrs
;
62 X509_ALGOR
*signatureAlgorithm
;
63 ASN1_OCTET_STRING
*signature
;
68 * And we also need access to the wrappend CMS_KeyTransRecipientInfo to
69 * read the encrypted key
71 struct CMS_KeyTransRecipientInfo_st
{
74 X509_ALGOR
*keyEncryptionAlgorithm
;
75 ASN1_OCTET_STRING
*encryptedKey
;
78 struct CMS_RecipientInfo_st
{
80 struct CMS_KeyTransRecipientInfo_st
*ktri
;
81 /* and more in union... */
84 struct CMS_EncryptedContentInfo_st
{
85 ASN1_OBJECT
*contentType
;
86 X509_ALGOR
*contentEncryptionAlgorithm
;
87 ASN1_OCTET_STRING
*encryptedContent
;
91 struct CMS_EnvelopedData_st
{
94 STACK_OF(CMS_RecipientInfo
) *recipientInfos
;
95 struct CMS_EncryptedContentInfo_st
*encryptedContentInfo
;
99 struct CMS_ContentInfo_st
{
100 ASN1_OBJECT
*contentType
;
101 struct CMS_EnvelopedData_st
*envelopedData
;
102 /* and more in union... */
106 * We can't include asn1.h, declare function prototypes directly
108 chunk_t
asn1_wrap(int, const char *mode
, ...);
109 int asn1_unwrap(chunk_t
*, chunk_t
*);
112 * Enumerator over certificates
115 /** implements enumerator_t */
117 /** Stack of X509 certificates */
118 STACK_OF(X509
) *certs
;
119 /** current enumerator position in certificates */
121 /** currently enumerating certificate_t */
125 METHOD(enumerator_t
, cert_destroy
, void,
126 cert_enumerator_t
*this)
128 DESTROY_IF(this->cert
);
132 METHOD(enumerator_t
, cert_enumerate
, bool,
133 cert_enumerator_t
*this, certificate_t
**out
)
139 while (this->i
< sk_X509_num(this->certs
))
144 /* clean up previous round */
145 DESTROY_IF(this->cert
);
148 x509
= sk_X509_value(this->certs
, this->i
++);
149 encoding
= openssl_i2chunk(X509
, x509
);
150 this->cert
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
, CERT_X509
,
151 BUILD_BLOB_ASN1_DER
, encoding
,
164 METHOD(pkcs7_t
, create_cert_enumerator
, enumerator_t
*,
165 private_openssl_pkcs7_t
*this)
167 cert_enumerator_t
*enumerator
;
169 if (this->type
== CONTAINER_PKCS7_SIGNED_DATA
)
173 .enumerate
= (void*)_cert_enumerate
,
174 .destroy
= _cert_destroy
,
176 .certs
= CMS_get1_certs(this->cms
),
178 return &enumerator
->public;
180 return enumerator_create_empty();
184 * Enumerator for signatures
187 /** implements enumerator_t */
189 /** Stack of signerinfos */
190 STACK_OF(CMS_SignerInfo
) *signers
;
191 /** current enumerator position in signers */
193 /** currently enumerating auth config */
196 CMS_ContentInfo
*cms
;
197 /** credential set containing wrapped certificates */
199 } signature_enumerator_t
;
202 * Verify signerInfo signature
204 static auth_cfg_t
*verify_signature(CMS_SignerInfo
*si
, int hash_oid
)
206 enumerator_t
*enumerator
;
209 auth_cfg_t
*auth
, *found
= NULL
;
210 identification_t
*issuer
, *serial
;
211 chunk_t attrs
= chunk_empty
, sig
, attr
;
216 if (CMS_SignerInfo_get0_signer_id(si
, NULL
, &name
, &snr
) != 1)
220 issuer
= openssl_x509_name2id(name
);
225 serial
= identification_create_from_encoding(
226 ID_KEY_ID
, openssl_asn1_str2chunk(snr
));
228 /* reconstruct DER encoded attributes to verify signature */
229 for (i
= 0; i
< CMS_signed_get_attr_count(si
); i
++)
231 attr
= openssl_i2chunk(X509_ATTRIBUTE
, CMS_signed_get_attr(si
, i
));
232 attrs
= chunk_cat("mm", attrs
, attr
);
234 /* wrap in a ASN1_SET */
235 attrs
= asn1_wrap(0x31, "m", attrs
);
237 /* TODO: find a better way to access and verify the signature */
238 sig
= openssl_asn1_str2chunk(si
->signature
);
239 enumerator
= lib
->credmgr
->create_trusted_enumerator(lib
->credmgr
,
240 KEY_RSA
, serial
, FALSE
);
241 while (enumerator
->enumerate(enumerator
, &cert
, &auth
))
243 if (issuer
->equals(issuer
, cert
->get_issuer(cert
)))
245 key
= cert
->get_public_key(cert
);
248 if (key
->verify(key
, signature_scheme_from_oid(hash_oid
),
251 found
= auth
->clone(auth
);
259 enumerator
->destroy(enumerator
);
260 issuer
->destroy(issuer
);
261 serial
->destroy(serial
);
268 * Verify the message digest in the signerInfo attributes
270 static bool verify_digest(CMS_ContentInfo
*cms
, CMS_SignerInfo
*si
, int hash_oid
)
272 ASN1_OCTET_STRING
*os
, **osp
;
273 hash_algorithm_t hash_alg
;
274 chunk_t digest
, content
, hash
;
277 os
= CMS_signed_get0_data_by_OBJ(si
,
278 OBJ_nid2obj(NID_pkcs9_messageDigest
), -3, V_ASN1_OCTET_STRING
);
283 digest
= openssl_asn1_str2chunk(os
);
284 osp
= CMS_get0_content(cms
);
289 content
= openssl_asn1_str2chunk(*osp
);
291 hash_alg
= hasher_algorithm_from_oid(hash_oid
);
292 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, hash_alg
);
295 DBG1(DBG_LIB
, "hash algorithm %N not supported",
296 hash_algorithm_names
, hash_alg
);
299 if (!hasher
->allocate_hash(hasher
, content
, &hash
))
301 hasher
->destroy(hasher
);
304 hasher
->destroy(hasher
);
306 if (!chunk_equals(digest
, hash
))
309 DBG1(DBG_LIB
, "invalid messageDigest");
316 METHOD(enumerator_t
, signature_enumerate
, bool,
317 signature_enumerator_t
*this, auth_cfg_t
**out
)
323 while (this->i
< sk_CMS_SignerInfo_num(this->signers
))
326 X509_ALGOR
*digest
, *sig
;
329 /* clean up previous round */
330 DESTROY_IF(this->auth
);
333 si
= sk_CMS_SignerInfo_value(this->signers
, this->i
++);
335 CMS_SignerInfo_get0_algs(si
, NULL
, NULL
, &digest
, &sig
);
336 hash_oid
= openssl_asn1_known_oid(digest
->algorithm
);
337 if (openssl_asn1_known_oid(sig
->algorithm
) != OID_RSA_ENCRYPTION
)
339 DBG1(DBG_LIB
, "only RSA digest encryption supported");
342 this->auth
= verify_signature(si
, hash_oid
);
345 DBG1(DBG_LIB
, "unable to verify pkcs7 attributes signature");
348 if (!verify_digest(this->cms
, si
, hash_oid
))
358 METHOD(enumerator_t
, signature_destroy
, void,
359 signature_enumerator_t
*this)
361 lib
->credmgr
->remove_local_set(lib
->credmgr
, &this->creds
->set
);
362 this->creds
->destroy(this->creds
);
363 DESTROY_IF(this->auth
);
367 METHOD(container_t
, create_signature_enumerator
, enumerator_t
*,
368 private_openssl_pkcs7_t
*this)
370 signature_enumerator_t
*enumerator
;
372 if (this->type
== CONTAINER_PKCS7_SIGNED_DATA
)
379 .enumerate
= (void*)_signature_enumerate
,
380 .destroy
= _signature_destroy
,
383 .signers
= CMS_get0_SignerInfos(this->cms
),
384 .creds
= mem_cred_create(),
387 /* make available wrapped certs during signature checking */
388 certs
= create_cert_enumerator(this);
389 while (certs
->enumerate(certs
, &cert
))
391 enumerator
->creds
->add_cert(enumerator
->creds
, FALSE
,
392 cert
->get_ref(cert
));
394 certs
->destroy(certs
);
396 lib
->credmgr
->add_local_set(lib
->credmgr
, &enumerator
->creds
->set
,
399 return &enumerator
->public;
401 return enumerator_create_empty();
405 METHOD(container_t
, get_type
, container_type_t
,
406 private_openssl_pkcs7_t
*this)
411 METHOD(pkcs7_t
, get_attribute
, bool,
412 private_openssl_pkcs7_t
*this, int oid
,
413 enumerator_t
*enumerator
, chunk_t
*value
)
415 signature_enumerator_t
*e
;
417 X509_ATTRIBUTE
*attr
;
419 chunk_t chunk
, wrapped
;
422 e
= (signature_enumerator_t
*)enumerator
;
428 /* "i" gets incremeneted after enumerate(), hence read from previous */
429 si
= sk_CMS_SignerInfo_value(e
->signers
, e
->i
- 1);
430 for (i
= 0; i
< CMS_signed_get_attr_count(si
); i
++)
432 attr
= CMS_signed_get_attr(si
, i
);
433 if (!attr
->single
&& sk_ASN1_TYPE_num(attr
->value
.set
) == 1 &&
434 openssl_asn1_known_oid(attr
->object
) == oid
)
436 /* get first value in SET */
437 type
= sk_ASN1_TYPE_value(attr
->value
.set
, 0);
438 chunk
= wrapped
= openssl_i2chunk(ASN1_TYPE
, type
);
439 if (asn1_unwrap(&chunk
, &chunk
) != 0x100 /* ASN1_INVALID */)
441 *value
= chunk_clone(chunk
);
452 * Find a private key for issuerAndSerialNumber
454 static private_key_t
*find_private(identification_t
*issuer
,
455 identification_t
*serial
)
457 enumerator_t
*enumerator
;
459 public_key_t
*public;
460 private_key_t
*private = NULL
;
461 identification_t
*id
;
464 enumerator
= lib
->credmgr
->create_cert_enumerator(lib
->credmgr
,
465 CERT_X509
, KEY_RSA
, serial
, FALSE
);
466 while (enumerator
->enumerate(enumerator
, &cert
))
468 if (issuer
->equals(issuer
, cert
->get_issuer(cert
)))
470 public = cert
->get_public_key(cert
);
473 if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1
, &fp
))
475 id
= identification_create_from_encoding(ID_KEY_ID
, fp
);
476 private = lib
->credmgr
->get_private(lib
->credmgr
,
480 public->destroy(public);
488 enumerator
->destroy(enumerator
);
493 * Decrypt enveloped-data with a decrypted symmetric key
495 static bool decrypt_symmetric(private_openssl_pkcs7_t
*this, chunk_t key
,
496 chunk_t encrypted
, chunk_t
*plain
)
498 encryption_algorithm_t encr
;
504 /* read encryption algorithm from interal structures; TODO fixup */
505 alg
= this->cms
->envelopedData
->encryptedContentInfo
->
506 contentEncryptionAlgorithm
;
507 encr
= encryption_algorithm_from_oid(openssl_asn1_known_oid(alg
->algorithm
),
509 if (alg
->parameter
->type
!= V_ASN1_OCTET_STRING
)
513 iv
= openssl_asn1_str2chunk(alg
->parameter
->value
.octet_string
);
515 crypter
= lib
->crypto
->create_crypter(lib
->crypto
, encr
, key_size
/ 8);
518 DBG1(DBG_LIB
, "crypter %N-%d not available",
519 encryption_algorithm_names
, alg
, key_size
);
522 if (key
.len
!= crypter
->get_key_size(crypter
))
524 DBG1(DBG_LIB
, "symmetric key length is wrong");
525 crypter
->destroy(crypter
);
528 if (iv
.len
!= crypter
->get_iv_size(crypter
))
530 DBG1(DBG_LIB
, "IV length is wrong");
531 crypter
->destroy(crypter
);
534 if (!crypter
->set_key(crypter
, key
) ||
535 !crypter
->decrypt(crypter
, encrypted
, iv
, plain
))
537 crypter
->destroy(crypter
);
540 crypter
->destroy(crypter
);
545 * Remove enveloped-data PKCS#7 padding from plain data
547 static bool remove_padding(chunk_t
*data
)
557 pos
= data
->ptr
+ data
->len
- 1;
558 padding
= pattern
= *pos
;
560 if (padding
> data
->len
)
562 DBG1(DBG_LIB
, "padding greater than data length");
565 data
->len
-= padding
;
567 while (padding
-- > 0)
569 if (*pos
-- != pattern
)
571 DBG1(DBG_LIB
, "wrong padding pattern");
579 * Decrypt PKCS#7 enveloped-data
581 static bool decrypt(private_openssl_pkcs7_t
*this,
582 chunk_t encrypted
, chunk_t
*plain
)
584 STACK_OF(CMS_RecipientInfo
) *ris
;
585 CMS_RecipientInfo
*ri
;
586 chunk_t chunk
, key
= chunk_empty
;
589 ris
= CMS_get0_RecipientInfos(this->cms
);
590 for (i
= 0; i
< sk_CMS_RecipientInfo_num(ris
); i
++)
592 ri
= sk_CMS_RecipientInfo_value(ris
, i
);
593 if (CMS_RecipientInfo_type(ri
) == CMS_RECIPINFO_TRANS
)
595 identification_t
*serial
, *issuer
;
596 private_key_t
*private;
603 if (CMS_RecipientInfo_ktri_get0_algs(ri
, NULL
, NULL
, &alg
) == 1 &&
604 CMS_RecipientInfo_ktri_get0_signer_id(ri
, NULL
, &name
, &sn
) == 1)
606 oid
= openssl_asn1_known_oid(alg
->algorithm
);
607 if (oid
!= OID_RSA_ENCRYPTION
)
609 DBG1(DBG_LIB
, "only RSA encryption supported in PKCS#7");
612 issuer
= openssl_x509_name2id(name
);
617 chunk
= openssl_asn1_str2chunk(sn
);
618 if (chunk
.len
&& chunk
.ptr
[0] & 0x80)
619 { /* if MSB is set, append a zero to make it non-negative */
620 chunk
= chunk_cata("cc", chunk_from_thing(zero
), chunk
);
622 serial
= identification_create_from_encoding(ID_KEY_ID
, chunk
);
623 private = find_private(issuer
, serial
);
624 issuer
->destroy(issuer
);
625 serial
->destroy(serial
);
629 /* get encryptedKey from internal structure; TODO fixup */
630 chunk
= openssl_asn1_str2chunk(ri
->ktri
->encryptedKey
);
631 if (private->decrypt(private, ENCRYPT_RSA_PKCS1
,
634 private->destroy(private);
637 private->destroy(private);
644 DBG1(DBG_LIB
, "no private key found to decrypt PKCS#7");
647 if (!decrypt_symmetric(this, key
, encrypted
, plain
))
653 if (!remove_padding(plain
))
661 METHOD(container_t
, get_data
, bool,
662 private_openssl_pkcs7_t
*this, chunk_t
*data
)
664 ASN1_OCTET_STRING
**os
;
667 os
= CMS_get0_content(this->cms
);
670 chunk
= openssl_asn1_str2chunk(*os
);
673 case CONTAINER_PKCS7_DATA
:
674 case CONTAINER_PKCS7_SIGNED_DATA
:
675 *data
= chunk_clone(chunk
);
677 case CONTAINER_PKCS7_ENVELOPED_DATA
:
678 return decrypt(this, chunk
, data
);
686 METHOD(container_t
, get_encoding
, bool,
687 private_openssl_pkcs7_t
*this, chunk_t
*data
)
692 METHOD(container_t
, destroy
, void,
693 private_openssl_pkcs7_t
*this)
695 CMS_ContentInfo_free(this->cms
);
700 * Generic constructor
702 static private_openssl_pkcs7_t
* create_empty()
704 private_openssl_pkcs7_t
*this;
709 .get_type
= _get_type
,
710 .create_signature_enumerator
= _create_signature_enumerator
,
711 .get_data
= _get_data
,
712 .get_encoding
= _get_encoding
,
715 .get_attribute
= _get_attribute
,
716 .create_cert_enumerator
= _create_cert_enumerator
,
724 * Parse a PKCS#7 container
726 static bool parse(private_openssl_pkcs7_t
*this, chunk_t blob
)
730 bio
= BIO_new_mem_buf(blob
.ptr
, blob
.len
);
731 this->cms
= d2i_CMS_bio(bio
, NULL
);
738 switch (openssl_asn1_known_oid((ASN1_OBJECT
*)CMS_get0_type(this->cms
)))
741 this->type
= CONTAINER_PKCS7_DATA
;
743 case OID_PKCS7_SIGNED_DATA
:
744 this->type
= CONTAINER_PKCS7_SIGNED_DATA
;
746 case OID_PKCS7_ENVELOPED_DATA
:
747 this->type
= CONTAINER_PKCS7_ENVELOPED_DATA
;
759 pkcs7_t
*openssl_pkcs7_load(container_type_t type
, va_list args
)
761 chunk_t blob
= chunk_empty
;
762 private_openssl_pkcs7_t
*this;
766 switch (va_arg(args
, builder_part_t
))
768 case BUILD_BLOB_ASN1_DER
:
769 blob
= va_arg(args
, chunk_t
);
780 this = create_empty();
781 if (parse(this, blob
))
783 return &this->public;
790 #endif /* OPENSSL_NO_CMS */