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/opensslv.h>
17 #include <openssl/opensslconf.h>
19 #if OPENSSL_VERSION_NUMBER >= 0x0090807fL
20 #ifndef OPENSSL_NO_CMS
22 #include "openssl_pkcs7.h"
23 #include "openssl_util.h"
26 #include <utils/debug.h>
28 #include <credentials/sets/mem_cred.h>
30 #include <openssl/cms.h>
32 #if OPENSSL_VERSION_NUMBER < 0x10100000L
33 #define X509_ATTRIBUTE_get0_object(attr) ({ (attr)->object; })
36 typedef struct private_openssl_pkcs7_t private_openssl_pkcs7_t
;
39 * Private data of an openssl_pkcs7_t object.
41 struct private_openssl_pkcs7_t
{
44 * Public pkcs7_t interface.
49 * Type of this container
51 container_type_t type
;
54 * OpenSSL CMS structure
60 * OpenSSL does not allow us to read the signature to verify it with our own
61 * crypto API. We define the internal CMS_SignerInfo structure here to get it.
63 struct CMS_SignerInfo_st
{
66 X509_ALGOR
*digestAlgorithm
;
67 STACK_OF(X509_ATTRIBUTE
) *signedAttrs
;
68 X509_ALGOR
*signatureAlgorithm
;
69 ASN1_OCTET_STRING
*signature
;
74 * And we also need access to the wrappend CMS_KeyTransRecipientInfo to
75 * read the encrypted key
77 struct CMS_KeyTransRecipientInfo_st
{
80 X509_ALGOR
*keyEncryptionAlgorithm
;
81 ASN1_OCTET_STRING
*encryptedKey
;
84 struct CMS_RecipientInfo_st
{
86 struct CMS_KeyTransRecipientInfo_st
*ktri
;
87 /* and more in union... */
90 struct CMS_EncryptedContentInfo_st
{
91 ASN1_OBJECT
*contentType
;
92 X509_ALGOR
*contentEncryptionAlgorithm
;
93 ASN1_OCTET_STRING
*encryptedContent
;
97 struct CMS_EnvelopedData_st
{
100 STACK_OF(CMS_RecipientInfo
) *recipientInfos
;
101 struct CMS_EncryptedContentInfo_st
*encryptedContentInfo
;
105 struct CMS_ContentInfo_st
{
106 ASN1_OBJECT
*contentType
;
107 struct CMS_EnvelopedData_st
*envelopedData
;
108 /* and more in union... */
112 * We can't include asn1.h, declare function prototypes directly
114 chunk_t
asn1_wrap(int, const char *mode
, ...);
115 int asn1_unwrap(chunk_t
*, chunk_t
*);
118 * Enumerator over certificates
121 /** implements enumerator_t */
123 /** Stack of X509 certificates */
124 STACK_OF(X509
) *certs
;
125 /** current enumerator position in certificates */
127 /** currently enumerating certificate_t */
131 METHOD(enumerator_t
, cert_destroy
, void,
132 cert_enumerator_t
*this)
134 DESTROY_IF(this->cert
);
138 METHOD(enumerator_t
, cert_enumerate
, bool,
139 cert_enumerator_t
*this, va_list args
)
143 VA_ARGS_VGET(args
, out
);
149 while (this->i
< sk_X509_num(this->certs
))
154 /* clean up previous round */
155 DESTROY_IF(this->cert
);
158 x509
= sk_X509_value(this->certs
, this->i
++);
159 encoding
= openssl_i2chunk(X509
, x509
);
160 this->cert
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
, CERT_X509
,
161 BUILD_BLOB_ASN1_DER
, encoding
,
174 METHOD(pkcs7_t
, create_cert_enumerator
, enumerator_t
*,
175 private_openssl_pkcs7_t
*this)
177 cert_enumerator_t
*enumerator
;
179 if (this->type
== CONTAINER_PKCS7_SIGNED_DATA
)
183 .enumerate
= enumerator_enumerate_default
,
184 .venumerate
= _cert_enumerate
,
185 .destroy
= _cert_destroy
,
187 .certs
= CMS_get1_certs(this->cms
),
189 return &enumerator
->public;
191 return enumerator_create_empty();
195 * Enumerator for signatures
198 /** implements enumerator_t */
200 /** Stack of signerinfos */
201 STACK_OF(CMS_SignerInfo
) *signers
;
202 /** current enumerator position in signers */
204 /** currently enumerating auth config */
207 CMS_ContentInfo
*cms
;
208 /** credential set containing wrapped certificates */
210 } signature_enumerator_t
;
213 * Verify signerInfo signature
215 static auth_cfg_t
*verify_signature(CMS_SignerInfo
*si
, int hash_oid
)
217 enumerator_t
*enumerator
;
220 auth_cfg_t
*auth
, *found
= NULL
;
221 identification_t
*issuer
, *serial
;
222 chunk_t attrs
= chunk_empty
, sig
, attr
;
227 if (CMS_SignerInfo_get0_signer_id(si
, NULL
, &name
, &snr
) != 1)
231 issuer
= openssl_x509_name2id(name
);
236 serial
= identification_create_from_encoding(
237 ID_KEY_ID
, openssl_asn1_str2chunk(snr
));
239 /* reconstruct DER encoded attributes to verify signature */
240 for (i
= 0; i
< CMS_signed_get_attr_count(si
); i
++)
242 attr
= openssl_i2chunk(X509_ATTRIBUTE
, CMS_signed_get_attr(si
, i
));
243 attrs
= chunk_cat("mm", attrs
, attr
);
245 /* wrap in a ASN1_SET */
246 attrs
= asn1_wrap(0x31, "m", attrs
);
248 /* TODO: find a better way to access and verify the signature */
249 sig
= openssl_asn1_str2chunk(si
->signature
);
250 enumerator
= lib
->credmgr
->create_trusted_enumerator(lib
->credmgr
,
251 KEY_RSA
, serial
, FALSE
);
252 while (enumerator
->enumerate(enumerator
, &cert
, &auth
))
254 if (issuer
->equals(issuer
, cert
->get_issuer(cert
)))
256 key
= cert
->get_public_key(cert
);
259 if (key
->verify(key
, signature_scheme_from_oid(hash_oid
), NULL
,
262 found
= auth
->clone(auth
);
270 enumerator
->destroy(enumerator
);
271 issuer
->destroy(issuer
);
272 serial
->destroy(serial
);
279 * Verify the message digest in the signerInfo attributes
281 static bool verify_digest(CMS_ContentInfo
*cms
, CMS_SignerInfo
*si
, int hash_oid
)
283 ASN1_OCTET_STRING
*os
, **osp
;
284 hash_algorithm_t hash_alg
;
285 chunk_t digest
, content
, hash
;
288 os
= CMS_signed_get0_data_by_OBJ(si
,
289 OBJ_nid2obj(NID_pkcs9_messageDigest
), -3, V_ASN1_OCTET_STRING
);
294 digest
= openssl_asn1_str2chunk(os
);
295 osp
= CMS_get0_content(cms
);
300 content
= openssl_asn1_str2chunk(*osp
);
302 hash_alg
= hasher_algorithm_from_oid(hash_oid
);
303 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, hash_alg
);
306 DBG1(DBG_LIB
, "hash algorithm %N not supported",
307 hash_algorithm_names
, hash_alg
);
310 if (!hasher
->allocate_hash(hasher
, content
, &hash
))
312 hasher
->destroy(hasher
);
315 hasher
->destroy(hasher
);
317 if (!chunk_equals_const(digest
, hash
))
320 DBG1(DBG_LIB
, "invalid messageDigest");
327 METHOD(enumerator_t
, signature_enumerate
, bool,
328 signature_enumerator_t
*this, va_list args
)
332 VA_ARGS_VGET(args
, out
);
338 while (this->i
< sk_CMS_SignerInfo_num(this->signers
))
341 X509_ALGOR
*digest
, *sig
;
344 /* clean up previous round */
345 DESTROY_IF(this->auth
);
348 si
= sk_CMS_SignerInfo_value(this->signers
, this->i
++);
350 CMS_SignerInfo_get0_algs(si
, NULL
, NULL
, &digest
, &sig
);
351 hash_oid
= openssl_asn1_known_oid(digest
->algorithm
);
352 if (openssl_asn1_known_oid(sig
->algorithm
) != OID_RSA_ENCRYPTION
)
354 DBG1(DBG_LIB
, "only RSA digest encryption supported");
357 this->auth
= verify_signature(si
, hash_oid
);
360 DBG1(DBG_LIB
, "unable to verify pkcs7 attributes signature");
363 if (!verify_digest(this->cms
, si
, hash_oid
))
373 METHOD(enumerator_t
, signature_destroy
, void,
374 signature_enumerator_t
*this)
376 lib
->credmgr
->remove_local_set(lib
->credmgr
, &this->creds
->set
);
377 this->creds
->destroy(this->creds
);
378 DESTROY_IF(this->auth
);
382 METHOD(container_t
, create_signature_enumerator
, enumerator_t
*,
383 private_openssl_pkcs7_t
*this)
385 signature_enumerator_t
*enumerator
;
387 if (this->type
== CONTAINER_PKCS7_SIGNED_DATA
)
394 .enumerate
= enumerator_enumerate_default
,
395 .venumerate
= _signature_enumerate
,
396 .destroy
= _signature_destroy
,
399 .signers
= CMS_get0_SignerInfos(this->cms
),
400 .creds
= mem_cred_create(),
403 /* make available wrapped certs during signature checking */
404 certs
= create_cert_enumerator(this);
405 while (certs
->enumerate(certs
, &cert
))
407 enumerator
->creds
->add_cert(enumerator
->creds
, FALSE
,
408 cert
->get_ref(cert
));
410 certs
->destroy(certs
);
412 lib
->credmgr
->add_local_set(lib
->credmgr
, &enumerator
->creds
->set
,
415 return &enumerator
->public;
417 return enumerator_create_empty();
421 METHOD(container_t
, get_type
, container_type_t
,
422 private_openssl_pkcs7_t
*this)
427 METHOD(pkcs7_t
, get_attribute
, bool,
428 private_openssl_pkcs7_t
*this, int oid
,
429 enumerator_t
*enumerator
, chunk_t
*value
)
431 signature_enumerator_t
*e
;
433 X509_ATTRIBUTE
*attr
;
435 chunk_t chunk
, wrapped
;
438 e
= (signature_enumerator_t
*)enumerator
;
444 /* "i" gets incremented after enumerate(), hence read from previous */
445 si
= sk_CMS_SignerInfo_value(e
->signers
, e
->i
- 1);
446 for (i
= 0; i
< CMS_signed_get_attr_count(si
); i
++)
448 attr
= CMS_signed_get_attr(si
, i
);
449 if (X509_ATTRIBUTE_count(attr
) == 1 &&
450 openssl_asn1_known_oid(X509_ATTRIBUTE_get0_object(attr
)) == oid
)
452 /* get first value in SET */
453 type
= X509_ATTRIBUTE_get0_type(attr
, 0);
454 chunk
= wrapped
= openssl_i2chunk(ASN1_TYPE
, type
);
455 if (asn1_unwrap(&chunk
, &chunk
) != 0x100 /* ASN1_INVALID */)
457 *value
= chunk_clone(chunk
);
468 * Find a private key for issuerAndSerialNumber
470 static private_key_t
*find_private(identification_t
*issuer
,
471 identification_t
*serial
)
473 enumerator_t
*enumerator
;
475 public_key_t
*public;
476 private_key_t
*private = NULL
;
477 identification_t
*id
;
480 enumerator
= lib
->credmgr
->create_cert_enumerator(lib
->credmgr
,
481 CERT_X509
, KEY_RSA
, serial
, FALSE
);
482 while (enumerator
->enumerate(enumerator
, &cert
))
484 if (issuer
->equals(issuer
, cert
->get_issuer(cert
)))
486 public = cert
->get_public_key(cert
);
489 if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1
, &fp
))
491 id
= identification_create_from_encoding(ID_KEY_ID
, fp
);
492 private = lib
->credmgr
->get_private(lib
->credmgr
,
496 public->destroy(public);
504 enumerator
->destroy(enumerator
);
509 * Decrypt enveloped-data with a decrypted symmetric key
511 static bool decrypt_symmetric(private_openssl_pkcs7_t
*this, chunk_t key
,
512 chunk_t encrypted
, chunk_t
*plain
)
514 encryption_algorithm_t encr
;
520 /* read encryption algorithm from internal structures; TODO fixup */
521 alg
= this->cms
->envelopedData
->encryptedContentInfo
->
522 contentEncryptionAlgorithm
;
523 encr
= encryption_algorithm_from_oid(openssl_asn1_known_oid(alg
->algorithm
),
525 if (alg
->parameter
->type
!= V_ASN1_OCTET_STRING
)
529 iv
= openssl_asn1_str2chunk(alg
->parameter
->value
.octet_string
);
531 crypter
= lib
->crypto
->create_crypter(lib
->crypto
, encr
, key_size
/ 8);
534 DBG1(DBG_LIB
, "crypter %N-%d not available",
535 encryption_algorithm_names
, alg
, key_size
);
538 if (key
.len
!= crypter
->get_key_size(crypter
))
540 DBG1(DBG_LIB
, "symmetric key length is wrong");
541 crypter
->destroy(crypter
);
544 if (iv
.len
!= crypter
->get_iv_size(crypter
))
546 DBG1(DBG_LIB
, "IV length is wrong");
547 crypter
->destroy(crypter
);
550 if (!crypter
->set_key(crypter
, key
) ||
551 !crypter
->decrypt(crypter
, encrypted
, iv
, plain
))
553 crypter
->destroy(crypter
);
556 crypter
->destroy(crypter
);
561 * Remove enveloped-data PKCS#7 padding from plain data
563 static bool remove_padding(chunk_t
*data
)
573 pos
= data
->ptr
+ data
->len
- 1;
574 padding
= pattern
= *pos
;
576 if (padding
> data
->len
)
578 DBG1(DBG_LIB
, "padding greater than data length");
581 data
->len
-= padding
;
583 while (padding
-- > 0)
585 if (*pos
-- != pattern
)
587 DBG1(DBG_LIB
, "wrong padding pattern");
595 * Decrypt PKCS#7 enveloped-data
597 static bool decrypt(private_openssl_pkcs7_t
*this,
598 chunk_t encrypted
, chunk_t
*plain
)
600 STACK_OF(CMS_RecipientInfo
) *ris
;
601 CMS_RecipientInfo
*ri
;
602 chunk_t chunk
, key
= chunk_empty
;
605 ris
= CMS_get0_RecipientInfos(this->cms
);
606 for (i
= 0; i
< sk_CMS_RecipientInfo_num(ris
); i
++)
608 ri
= sk_CMS_RecipientInfo_value(ris
, i
);
609 if (CMS_RecipientInfo_type(ri
) == CMS_RECIPINFO_TRANS
)
611 identification_t
*serial
, *issuer
;
612 private_key_t
*private;
619 if (CMS_RecipientInfo_ktri_get0_algs(ri
, NULL
, NULL
, &alg
) == 1 &&
620 CMS_RecipientInfo_ktri_get0_signer_id(ri
, NULL
, &name
, &sn
) == 1)
622 oid
= openssl_asn1_known_oid(alg
->algorithm
);
623 if (oid
!= OID_RSA_ENCRYPTION
)
625 DBG1(DBG_LIB
, "only RSA encryption supported in PKCS#7");
628 issuer
= openssl_x509_name2id(name
);
633 chunk
= openssl_asn1_str2chunk(sn
);
634 if (chunk
.len
&& chunk
.ptr
[0] & 0x80)
635 { /* if MSB is set, append a zero to make it non-negative */
636 chunk
= chunk_cata("cc", chunk_from_thing(zero
), chunk
);
638 serial
= identification_create_from_encoding(ID_KEY_ID
, chunk
);
639 private = find_private(issuer
, serial
);
640 issuer
->destroy(issuer
);
641 serial
->destroy(serial
);
645 /* get encryptedKey from internal structure; TODO fixup */
646 chunk
= openssl_asn1_str2chunk(ri
->ktri
->encryptedKey
);
647 if (private->decrypt(private, ENCRYPT_RSA_PKCS1
, NULL
,
650 private->destroy(private);
653 private->destroy(private);
660 DBG1(DBG_LIB
, "no private key found to decrypt PKCS#7");
663 if (!decrypt_symmetric(this, key
, encrypted
, plain
))
669 if (!remove_padding(plain
))
677 METHOD(container_t
, get_data
, bool,
678 private_openssl_pkcs7_t
*this, chunk_t
*data
)
680 ASN1_OCTET_STRING
**os
;
683 os
= CMS_get0_content(this->cms
);
686 chunk
= openssl_asn1_str2chunk(*os
);
689 case CONTAINER_PKCS7_DATA
:
690 case CONTAINER_PKCS7_SIGNED_DATA
:
691 *data
= chunk_clone(chunk
);
693 case CONTAINER_PKCS7_ENVELOPED_DATA
:
694 return decrypt(this, chunk
, data
);
702 METHOD(container_t
, get_encoding
, bool,
703 private_openssl_pkcs7_t
*this, chunk_t
*data
)
708 METHOD(container_t
, destroy
, void,
709 private_openssl_pkcs7_t
*this)
711 CMS_ContentInfo_free(this->cms
);
716 * Generic constructor
718 static private_openssl_pkcs7_t
* create_empty()
720 private_openssl_pkcs7_t
*this;
725 .get_type
= _get_type
,
726 .create_signature_enumerator
= _create_signature_enumerator
,
727 .get_data
= _get_data
,
728 .get_encoding
= _get_encoding
,
731 .get_attribute
= _get_attribute
,
732 .create_cert_enumerator
= _create_cert_enumerator
,
740 * Parse a PKCS#7 container
742 static bool parse(private_openssl_pkcs7_t
*this, chunk_t blob
)
746 bio
= BIO_new_mem_buf(blob
.ptr
, blob
.len
);
747 this->cms
= d2i_CMS_bio(bio
, NULL
);
754 switch (openssl_asn1_known_oid((ASN1_OBJECT
*)CMS_get0_type(this->cms
)))
757 this->type
= CONTAINER_PKCS7_DATA
;
759 case OID_PKCS7_SIGNED_DATA
:
760 this->type
= CONTAINER_PKCS7_SIGNED_DATA
;
762 case OID_PKCS7_ENVELOPED_DATA
:
763 this->type
= CONTAINER_PKCS7_ENVELOPED_DATA
;
775 pkcs7_t
*openssl_pkcs7_load(container_type_t type
, va_list args
)
777 chunk_t blob
= chunk_empty
;
778 private_openssl_pkcs7_t
*this;
782 switch (va_arg(args
, builder_part_t
))
784 case BUILD_BLOB_ASN1_DER
:
785 blob
= va_arg(args
, chunk_t
);
796 this = create_empty();
797 if (parse(this, blob
))
799 return &this->public;
806 #endif /* OPENSSL_NO_CMS */
807 #endif /* OPENSSL_VERSION_NUMBER */