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, certificate_t
**out
)
145 while (this->i
< sk_X509_num(this->certs
))
150 /* clean up previous round */
151 DESTROY_IF(this->cert
);
154 x509
= sk_X509_value(this->certs
, this->i
++);
155 encoding
= openssl_i2chunk(X509
, x509
);
156 this->cert
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
, CERT_X509
,
157 BUILD_BLOB_ASN1_DER
, encoding
,
170 METHOD(pkcs7_t
, create_cert_enumerator
, enumerator_t
*,
171 private_openssl_pkcs7_t
*this)
173 cert_enumerator_t
*enumerator
;
175 if (this->type
== CONTAINER_PKCS7_SIGNED_DATA
)
179 .enumerate
= (void*)_cert_enumerate
,
180 .destroy
= _cert_destroy
,
182 .certs
= CMS_get1_certs(this->cms
),
184 return &enumerator
->public;
186 return enumerator_create_empty();
190 * Enumerator for signatures
193 /** implements enumerator_t */
195 /** Stack of signerinfos */
196 STACK_OF(CMS_SignerInfo
) *signers
;
197 /** current enumerator position in signers */
199 /** currently enumerating auth config */
202 CMS_ContentInfo
*cms
;
203 /** credential set containing wrapped certificates */
205 } signature_enumerator_t
;
208 * Verify signerInfo signature
210 static auth_cfg_t
*verify_signature(CMS_SignerInfo
*si
, int hash_oid
)
212 enumerator_t
*enumerator
;
215 auth_cfg_t
*auth
, *found
= NULL
;
216 identification_t
*issuer
, *serial
;
217 chunk_t attrs
= chunk_empty
, sig
, attr
;
222 if (CMS_SignerInfo_get0_signer_id(si
, NULL
, &name
, &snr
) != 1)
226 issuer
= openssl_x509_name2id(name
);
231 serial
= identification_create_from_encoding(
232 ID_KEY_ID
, openssl_asn1_str2chunk(snr
));
234 /* reconstruct DER encoded attributes to verify signature */
235 for (i
= 0; i
< CMS_signed_get_attr_count(si
); i
++)
237 attr
= openssl_i2chunk(X509_ATTRIBUTE
, CMS_signed_get_attr(si
, i
));
238 attrs
= chunk_cat("mm", attrs
, attr
);
240 /* wrap in a ASN1_SET */
241 attrs
= asn1_wrap(0x31, "m", attrs
);
243 /* TODO: find a better way to access and verify the signature */
244 sig
= openssl_asn1_str2chunk(si
->signature
);
245 enumerator
= lib
->credmgr
->create_trusted_enumerator(lib
->credmgr
,
246 KEY_RSA
, serial
, FALSE
);
247 while (enumerator
->enumerate(enumerator
, &cert
, &auth
))
249 if (issuer
->equals(issuer
, cert
->get_issuer(cert
)))
251 key
= cert
->get_public_key(cert
);
254 if (key
->verify(key
, signature_scheme_from_oid(hash_oid
),
257 found
= auth
->clone(auth
);
265 enumerator
->destroy(enumerator
);
266 issuer
->destroy(issuer
);
267 serial
->destroy(serial
);
274 * Verify the message digest in the signerInfo attributes
276 static bool verify_digest(CMS_ContentInfo
*cms
, CMS_SignerInfo
*si
, int hash_oid
)
278 ASN1_OCTET_STRING
*os
, **osp
;
279 hash_algorithm_t hash_alg
;
280 chunk_t digest
, content
, hash
;
283 os
= CMS_signed_get0_data_by_OBJ(si
,
284 OBJ_nid2obj(NID_pkcs9_messageDigest
), -3, V_ASN1_OCTET_STRING
);
289 digest
= openssl_asn1_str2chunk(os
);
290 osp
= CMS_get0_content(cms
);
295 content
= openssl_asn1_str2chunk(*osp
);
297 hash_alg
= hasher_algorithm_from_oid(hash_oid
);
298 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, hash_alg
);
301 DBG1(DBG_LIB
, "hash algorithm %N not supported",
302 hash_algorithm_names
, hash_alg
);
305 if (!hasher
->allocate_hash(hasher
, content
, &hash
))
307 hasher
->destroy(hasher
);
310 hasher
->destroy(hasher
);
312 if (!chunk_equals_const(digest
, hash
))
315 DBG1(DBG_LIB
, "invalid messageDigest");
322 METHOD(enumerator_t
, signature_enumerate
, bool,
323 signature_enumerator_t
*this, auth_cfg_t
**out
)
329 while (this->i
< sk_CMS_SignerInfo_num(this->signers
))
332 X509_ALGOR
*digest
, *sig
;
335 /* clean up previous round */
336 DESTROY_IF(this->auth
);
339 si
= sk_CMS_SignerInfo_value(this->signers
, this->i
++);
341 CMS_SignerInfo_get0_algs(si
, NULL
, NULL
, &digest
, &sig
);
342 hash_oid
= openssl_asn1_known_oid(digest
->algorithm
);
343 if (openssl_asn1_known_oid(sig
->algorithm
) != OID_RSA_ENCRYPTION
)
345 DBG1(DBG_LIB
, "only RSA digest encryption supported");
348 this->auth
= verify_signature(si
, hash_oid
);
351 DBG1(DBG_LIB
, "unable to verify pkcs7 attributes signature");
354 if (!verify_digest(this->cms
, si
, hash_oid
))
364 METHOD(enumerator_t
, signature_destroy
, void,
365 signature_enumerator_t
*this)
367 lib
->credmgr
->remove_local_set(lib
->credmgr
, &this->creds
->set
);
368 this->creds
->destroy(this->creds
);
369 DESTROY_IF(this->auth
);
373 METHOD(container_t
, create_signature_enumerator
, enumerator_t
*,
374 private_openssl_pkcs7_t
*this)
376 signature_enumerator_t
*enumerator
;
378 if (this->type
== CONTAINER_PKCS7_SIGNED_DATA
)
385 .enumerate
= (void*)_signature_enumerate
,
386 .destroy
= _signature_destroy
,
389 .signers
= CMS_get0_SignerInfos(this->cms
),
390 .creds
= mem_cred_create(),
393 /* make available wrapped certs during signature checking */
394 certs
= create_cert_enumerator(this);
395 while (certs
->enumerate(certs
, &cert
))
397 enumerator
->creds
->add_cert(enumerator
->creds
, FALSE
,
398 cert
->get_ref(cert
));
400 certs
->destroy(certs
);
402 lib
->credmgr
->add_local_set(lib
->credmgr
, &enumerator
->creds
->set
,
405 return &enumerator
->public;
407 return enumerator_create_empty();
411 METHOD(container_t
, get_type
, container_type_t
,
412 private_openssl_pkcs7_t
*this)
417 METHOD(pkcs7_t
, get_attribute
, bool,
418 private_openssl_pkcs7_t
*this, int oid
,
419 enumerator_t
*enumerator
, chunk_t
*value
)
421 signature_enumerator_t
*e
;
423 X509_ATTRIBUTE
*attr
;
425 chunk_t chunk
, wrapped
;
428 e
= (signature_enumerator_t
*)enumerator
;
434 /* "i" gets incremeneted after enumerate(), hence read from previous */
435 si
= sk_CMS_SignerInfo_value(e
->signers
, e
->i
- 1);
436 for (i
= 0; i
< CMS_signed_get_attr_count(si
); i
++)
438 attr
= CMS_signed_get_attr(si
, i
);
439 if (X509_ATTRIBUTE_count(attr
) == 1 &&
440 openssl_asn1_known_oid(X509_ATTRIBUTE_get0_object(attr
)) == oid
)
442 /* get first value in SET */
443 type
= X509_ATTRIBUTE_get0_type(attr
, 0);
444 chunk
= wrapped
= openssl_i2chunk(ASN1_TYPE
, type
);
445 if (asn1_unwrap(&chunk
, &chunk
) != 0x100 /* ASN1_INVALID */)
447 *value
= chunk_clone(chunk
);
458 * Find a private key for issuerAndSerialNumber
460 static private_key_t
*find_private(identification_t
*issuer
,
461 identification_t
*serial
)
463 enumerator_t
*enumerator
;
465 public_key_t
*public;
466 private_key_t
*private = NULL
;
467 identification_t
*id
;
470 enumerator
= lib
->credmgr
->create_cert_enumerator(lib
->credmgr
,
471 CERT_X509
, KEY_RSA
, serial
, FALSE
);
472 while (enumerator
->enumerate(enumerator
, &cert
))
474 if (issuer
->equals(issuer
, cert
->get_issuer(cert
)))
476 public = cert
->get_public_key(cert
);
479 if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1
, &fp
))
481 id
= identification_create_from_encoding(ID_KEY_ID
, fp
);
482 private = lib
->credmgr
->get_private(lib
->credmgr
,
486 public->destroy(public);
494 enumerator
->destroy(enumerator
);
499 * Decrypt enveloped-data with a decrypted symmetric key
501 static bool decrypt_symmetric(private_openssl_pkcs7_t
*this, chunk_t key
,
502 chunk_t encrypted
, chunk_t
*plain
)
504 encryption_algorithm_t encr
;
510 /* read encryption algorithm from internal structures; TODO fixup */
511 alg
= this->cms
->envelopedData
->encryptedContentInfo
->
512 contentEncryptionAlgorithm
;
513 encr
= encryption_algorithm_from_oid(openssl_asn1_known_oid(alg
->algorithm
),
515 if (alg
->parameter
->type
!= V_ASN1_OCTET_STRING
)
519 iv
= openssl_asn1_str2chunk(alg
->parameter
->value
.octet_string
);
521 crypter
= lib
->crypto
->create_crypter(lib
->crypto
, encr
, key_size
/ 8);
524 DBG1(DBG_LIB
, "crypter %N-%d not available",
525 encryption_algorithm_names
, alg
, key_size
);
528 if (key
.len
!= crypter
->get_key_size(crypter
))
530 DBG1(DBG_LIB
, "symmetric key length is wrong");
531 crypter
->destroy(crypter
);
534 if (iv
.len
!= crypter
->get_iv_size(crypter
))
536 DBG1(DBG_LIB
, "IV length is wrong");
537 crypter
->destroy(crypter
);
540 if (!crypter
->set_key(crypter
, key
) ||
541 !crypter
->decrypt(crypter
, encrypted
, iv
, plain
))
543 crypter
->destroy(crypter
);
546 crypter
->destroy(crypter
);
551 * Remove enveloped-data PKCS#7 padding from plain data
553 static bool remove_padding(chunk_t
*data
)
563 pos
= data
->ptr
+ data
->len
- 1;
564 padding
= pattern
= *pos
;
566 if (padding
> data
->len
)
568 DBG1(DBG_LIB
, "padding greater than data length");
571 data
->len
-= padding
;
573 while (padding
-- > 0)
575 if (*pos
-- != pattern
)
577 DBG1(DBG_LIB
, "wrong padding pattern");
585 * Decrypt PKCS#7 enveloped-data
587 static bool decrypt(private_openssl_pkcs7_t
*this,
588 chunk_t encrypted
, chunk_t
*plain
)
590 STACK_OF(CMS_RecipientInfo
) *ris
;
591 CMS_RecipientInfo
*ri
;
592 chunk_t chunk
, key
= chunk_empty
;
595 ris
= CMS_get0_RecipientInfos(this->cms
);
596 for (i
= 0; i
< sk_CMS_RecipientInfo_num(ris
); i
++)
598 ri
= sk_CMS_RecipientInfo_value(ris
, i
);
599 if (CMS_RecipientInfo_type(ri
) == CMS_RECIPINFO_TRANS
)
601 identification_t
*serial
, *issuer
;
602 private_key_t
*private;
609 if (CMS_RecipientInfo_ktri_get0_algs(ri
, NULL
, NULL
, &alg
) == 1 &&
610 CMS_RecipientInfo_ktri_get0_signer_id(ri
, NULL
, &name
, &sn
) == 1)
612 oid
= openssl_asn1_known_oid(alg
->algorithm
);
613 if (oid
!= OID_RSA_ENCRYPTION
)
615 DBG1(DBG_LIB
, "only RSA encryption supported in PKCS#7");
618 issuer
= openssl_x509_name2id(name
);
623 chunk
= openssl_asn1_str2chunk(sn
);
624 if (chunk
.len
&& chunk
.ptr
[0] & 0x80)
625 { /* if MSB is set, append a zero to make it non-negative */
626 chunk
= chunk_cata("cc", chunk_from_thing(zero
), chunk
);
628 serial
= identification_create_from_encoding(ID_KEY_ID
, chunk
);
629 private = find_private(issuer
, serial
);
630 issuer
->destroy(issuer
);
631 serial
->destroy(serial
);
635 /* get encryptedKey from internal structure; TODO fixup */
636 chunk
= openssl_asn1_str2chunk(ri
->ktri
->encryptedKey
);
637 if (private->decrypt(private, ENCRYPT_RSA_PKCS1
,
640 private->destroy(private);
643 private->destroy(private);
650 DBG1(DBG_LIB
, "no private key found to decrypt PKCS#7");
653 if (!decrypt_symmetric(this, key
, encrypted
, plain
))
659 if (!remove_padding(plain
))
667 METHOD(container_t
, get_data
, bool,
668 private_openssl_pkcs7_t
*this, chunk_t
*data
)
670 ASN1_OCTET_STRING
**os
;
673 os
= CMS_get0_content(this->cms
);
676 chunk
= openssl_asn1_str2chunk(*os
);
679 case CONTAINER_PKCS7_DATA
:
680 case CONTAINER_PKCS7_SIGNED_DATA
:
681 *data
= chunk_clone(chunk
);
683 case CONTAINER_PKCS7_ENVELOPED_DATA
:
684 return decrypt(this, chunk
, data
);
692 METHOD(container_t
, get_encoding
, bool,
693 private_openssl_pkcs7_t
*this, chunk_t
*data
)
698 METHOD(container_t
, destroy
, void,
699 private_openssl_pkcs7_t
*this)
701 CMS_ContentInfo_free(this->cms
);
706 * Generic constructor
708 static private_openssl_pkcs7_t
* create_empty()
710 private_openssl_pkcs7_t
*this;
715 .get_type
= _get_type
,
716 .create_signature_enumerator
= _create_signature_enumerator
,
717 .get_data
= _get_data
,
718 .get_encoding
= _get_encoding
,
721 .get_attribute
= _get_attribute
,
722 .create_cert_enumerator
= _create_cert_enumerator
,
730 * Parse a PKCS#7 container
732 static bool parse(private_openssl_pkcs7_t
*this, chunk_t blob
)
736 bio
= BIO_new_mem_buf(blob
.ptr
, blob
.len
);
737 this->cms
= d2i_CMS_bio(bio
, NULL
);
744 switch (openssl_asn1_known_oid((ASN1_OBJECT
*)CMS_get0_type(this->cms
)))
747 this->type
= CONTAINER_PKCS7_DATA
;
749 case OID_PKCS7_SIGNED_DATA
:
750 this->type
= CONTAINER_PKCS7_SIGNED_DATA
;
752 case OID_PKCS7_ENVELOPED_DATA
:
753 this->type
= CONTAINER_PKCS7_ENVELOPED_DATA
;
765 pkcs7_t
*openssl_pkcs7_load(container_type_t type
, va_list args
)
767 chunk_t blob
= chunk_empty
;
768 private_openssl_pkcs7_t
*this;
772 switch (va_arg(args
, builder_part_t
))
774 case BUILD_BLOB_ASN1_DER
:
775 blob
= va_arg(args
, chunk_t
);
786 this = create_empty();
787 if (parse(this, blob
))
789 return &this->public;
796 #endif /* OPENSSL_NO_CMS */
797 #endif /* OPENSSL_VERSION_NUMBER */