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 "pkcs7_signed_data.h"
20 #include <utils/debug.h>
22 #include <asn1/asn1.h>
23 #include <asn1/asn1_parser.h>
24 #include <crypto/pkcs9.h>
25 #include <credentials/sets/mem_cred.h>
26 #include <credentials/certificates/x509.h>
27 #include <credentials/keys/private_key.h>
29 typedef struct private_pkcs7_signed_data_t private_pkcs7_signed_data_t
;
32 * Private data of a PKCS#7 signed-data container.
34 struct private_pkcs7_signed_data_t
{
47 * Encoded PKCS#7 signed-data
52 * list of signerInfos, signerinfo_t
54 linked_list_t
*signerinfos
;
57 * Contained certificates
68 * Signed attributes of signerInfo
73 * Serial of signing certificate
75 identification_t
*serial
;
78 * Issuer of signing certificate
80 identification_t
*issuer
;
85 chunk_t encrypted_digest
;
88 * Digesting algorithm OID
93 * Public key encryption algorithm OID
100 * Destroy a signerinfo_t entry
102 void signerinfo_destroy(signerinfo_t
*this)
104 DESTROY_IF(this->attributes
);
105 DESTROY_IF(this->serial
);
106 DESTROY_IF(this->issuer
);
107 free(this->encrypted_digest
.ptr
);
112 * ASN.1 definition of the PKCS#7 signedData type
114 static const asn1Object_t signedDataObjects
[] = {
115 { 0, "signedData", ASN1_SEQUENCE
, ASN1_NONE
}, /* 0 */
116 { 1, "version", ASN1_INTEGER
, ASN1_BODY
}, /* 1 */
117 { 1, "digestAlgorithms", ASN1_SET
, ASN1_LOOP
}, /* 2 */
118 { 2, "algorithm", ASN1_EOC
, ASN1_RAW
}, /* 3 */
119 { 1, "end loop", ASN1_EOC
, ASN1_END
}, /* 4 */
120 { 1, "contentInfo", ASN1_EOC
, ASN1_RAW
}, /* 5 */
121 { 1, "certificates", ASN1_CONTEXT_C_0
, ASN1_OPT
|
123 { 2, "certificate", ASN1_SEQUENCE
, ASN1_OBJ
}, /* 7 */
124 { 1, "end opt or loop", ASN1_EOC
, ASN1_END
}, /* 8 */
125 { 1, "crls", ASN1_CONTEXT_C_1
, ASN1_OPT
|
127 { 2, "crl", ASN1_SEQUENCE
, ASN1_OBJ
}, /* 10 */
128 { 1, "end opt or loop", ASN1_EOC
, ASN1_END
}, /* 11 */
129 { 1, "signerInfos", ASN1_SET
, ASN1_LOOP
}, /* 12 */
130 { 2, "signerInfo", ASN1_SEQUENCE
, ASN1_NONE
}, /* 13 */
131 { 3, "version", ASN1_INTEGER
, ASN1_BODY
}, /* 14 */
132 { 3, "issuerAndSerialNumber", ASN1_SEQUENCE
, ASN1_BODY
}, /* 15 */
133 { 4, "issuer", ASN1_SEQUENCE
, ASN1_OBJ
}, /* 16 */
134 { 4, "serial", ASN1_INTEGER
, ASN1_BODY
}, /* 17 */
135 { 3, "digestAlgorithm", ASN1_EOC
, ASN1_RAW
}, /* 18 */
136 { 3, "authenticatedAttributes", ASN1_CONTEXT_C_0
, ASN1_OPT
|
138 { 3, "end opt", ASN1_EOC
, ASN1_END
}, /* 20 */
139 { 3, "digestEncryptionAlgorithm", ASN1_EOC
, ASN1_RAW
}, /* 21 */
140 { 3, "encryptedDigest", ASN1_OCTET_STRING
, ASN1_BODY
}, /* 22 */
141 { 3, "unauthenticatedAttributes", ASN1_CONTEXT_C_1
, ASN1_OPT
}, /* 23 */
142 { 3, "end opt", ASN1_EOC
, ASN1_END
}, /* 24 */
143 { 1, "end loop", ASN1_EOC
, ASN1_END
}, /* 25 */
144 { 0, "exit", ASN1_EOC
, ASN1_EXIT
}
146 #define PKCS7_VERSION 1
147 #define PKCS7_DIGEST_ALG 3
148 #define PKCS7_CONTENT_INFO 5
150 #define PKCS7_SIGNER_INFO 13
151 #define PKCS7_SIGNER_INFO_VERSION 14
152 #define PKCS7_ISSUER 16
153 #define PKCS7_SERIAL_NUMBER 17
154 #define PKCS7_DIGEST_ALGORITHM 18
155 #define PKCS7_AUTH_ATTRIBUTES 19
156 #define PKCS7_DIGEST_ENC_ALGORITHM 21
157 #define PKCS7_ENCRYPTED_DIGEST 22
159 METHOD(container_t
, get_type
, container_type_t
,
160 private_pkcs7_signed_data_t
*this)
162 return CONTAINER_PKCS7_SIGNED_DATA
;
166 * Signature enumerator implementation
169 /** implements enumerator */
171 /** inner signerinfos enumerator */
173 /** currently enumerated auth_cfg */
175 /** currently enumerating signerinfo */
177 /** reference to container */
178 private_pkcs7_signed_data_t
*this;
179 } signature_enumerator_t
;
181 METHOD(enumerator_t
, enumerate
, bool,
182 signature_enumerator_t
*this, auth_cfg_t
**out
)
185 signature_scheme_t scheme
;
186 hash_algorithm_t algorithm
;
187 enumerator_t
*enumerator
;
191 chunk_t chunk
, hash
, content
;
195 while (this->inner
->enumerate(this->inner
, &info
))
197 /* clean up previous round */
198 DESTROY_IF(this->auth
);
201 scheme
= signature_scheme_from_oid(info
->digest_alg
);
202 if (scheme
== SIGN_UNKNOWN
)
204 DBG1(DBG_LIB
, "unsupported signature scheme");
207 if (!info
->attributes
)
209 DBG1(DBG_LIB
, "no authenticatedAttributes object found");
212 if (info
->enc_alg
!= OID_RSA_ENCRYPTION
)
214 DBG1(DBG_LIB
, "only RSA digest encryption supported");
218 enumerator
= lib
->credmgr
->create_trusted_enumerator(lib
->credmgr
,
219 KEY_RSA
, info
->serial
, FALSE
);
220 while (enumerator
->enumerate(enumerator
, &cert
, &auth
))
222 if (info
->issuer
->equals(info
->issuer
, cert
->get_issuer(cert
)))
224 key
= cert
->get_public_key(cert
);
227 chunk
= info
->attributes
->get_encoding(info
->attributes
);
228 if (key
->verify(key
, scheme
, chunk
, info
->encrypted_digest
))
230 this->auth
= auth
->clone(auth
);
238 enumerator
->destroy(enumerator
);
242 DBG1(DBG_LIB
, "unable to verify pkcs7 attributes signature");
246 chunk
= info
->attributes
->get_attribute(info
->attributes
,
247 OID_PKCS9_MESSAGE_DIGEST
);
250 DBG1(DBG_LIB
, "messageDigest attribute not found");
253 if (!this->this->content
->get_data(this->this->content
, &content
))
258 algorithm
= hasher_algorithm_from_oid(info
->digest_alg
);
259 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, algorithm
);
260 if (!hasher
|| !hasher
->allocate_hash(hasher
, content
, &hash
))
264 DBG1(DBG_LIB
, "hash algorithm %N not supported",
265 hash_algorithm_names
, algorithm
);
269 hasher
->destroy(hasher
);
270 DBG3(DBG_LIB
, "hash: %B", &hash
);
272 valid
= chunk_equals(chunk
, hash
);
276 DBG1(DBG_LIB
, "invalid messageDigest");
287 METHOD(enumerator_t
, enumerator_destroy
, void,
288 signature_enumerator_t
*this)
290 lib
->credmgr
->remove_local_set(lib
->credmgr
, &this->this->creds
->set
);
291 this->inner
->destroy(this->inner
);
292 DESTROY_IF(this->auth
);
296 METHOD(container_t
, create_signature_enumerator
, enumerator_t
*,
297 private_pkcs7_signed_data_t
*this)
299 signature_enumerator_t
*enumerator
;
303 .enumerate
= (void*)_enumerate
,
304 .destroy
= _enumerator_destroy
,
306 .inner
= this->signerinfos
->create_enumerator(this->signerinfos
),
310 lib
->credmgr
->add_local_set(lib
->credmgr
, &this->creds
->set
, FALSE
);
311 return &enumerator
->public;
314 METHOD(pkcs7_t
, get_attribute
, bool,
315 private_pkcs7_signed_data_t
*this, int oid
, enumerator_t
*enumerator
, chunk_t
*value
)
317 signature_enumerator_t
*e
;
320 e
= (signature_enumerator_t
*)enumerator
;
323 chunk
= e
->info
->attributes
->get_attribute(e
->info
->attributes
, oid
);
326 *value
= chunk_clone(chunk
);
333 METHOD(pkcs7_t
, create_cert_enumerator
, enumerator_t
*,
334 private_pkcs7_signed_data_t
*this)
336 return this->creds
->set
.create_cert_enumerator(&this->creds
->set
,
337 CERT_ANY
, KEY_ANY
, NULL
, FALSE
);
340 METHOD(container_t
, get_data
, bool,
341 private_pkcs7_signed_data_t
*this, chunk_t
*data
)
345 return this->content
->get_data(this->content
, data
);
350 METHOD(container_t
, get_encoding
, bool,
351 private_pkcs7_signed_data_t
*this, chunk_t
*data
)
353 *data
= chunk_clone(this->encoding
);
357 METHOD(container_t
, destroy
, void,
358 private_pkcs7_signed_data_t
*this)
360 this->creds
->destroy(this->creds
);
361 this->signerinfos
->destroy_function(this->signerinfos
,
362 (void*)signerinfo_destroy
);
363 DESTROY_IF(this->content
);
364 free(this->encoding
.ptr
);
369 * Create an empty PKCS#7 signed-data container.
371 static private_pkcs7_signed_data_t
* create_empty()
373 private_pkcs7_signed_data_t
*this;
378 .get_type
= _get_type
,
379 .create_signature_enumerator
= _create_signature_enumerator
,
380 .get_data
= _get_data
,
381 .get_encoding
= _get_encoding
,
384 .get_attribute
= _get_attribute
,
385 .create_cert_enumerator
= _create_cert_enumerator
,
387 .creds
= mem_cred_create(),
388 .signerinfos
= linked_list_create(),
395 * Parse PKCS#7 signed data
397 static bool parse(private_pkcs7_signed_data_t
*this, chunk_t content
)
399 asn1_parser_t
*parser
;
401 int objectID
, version
;
402 signerinfo_t
*info
= NULL
;
403 bool success
= FALSE
;
405 parser
= asn1_parser_create(signedDataObjects
, content
);
406 parser
->set_top_level(parser
, 0);
407 while (parser
->iterate(parser
, &objectID
, &object
))
409 u_int level
= parser
->get_level(parser
);
414 version
= object
.len ?
(int)*object
.ptr
: 0;
415 DBG2(DBG_LIB
, " v%d", version
);
417 case PKCS7_CONTENT_INFO
:
418 this->content
= lib
->creds
->create(lib
->creds
,
419 CRED_CONTAINER
, CONTAINER_PKCS7
,
420 BUILD_BLOB_ASN1_DER
, object
, BUILD_END
);
426 DBG2(DBG_LIB
, " parsing pkcs7-wrapped certificate");
427 cert
= lib
->creds
->create(lib
->creds
,
428 CRED_CERTIFICATE
, CERT_X509
,
429 BUILD_BLOB_ASN1_DER
, object
,
433 this->creds
->add_cert(this->creds
, FALSE
, cert
);
437 case PKCS7_SIGNER_INFO
:
439 .digest_alg
= OID_UNKNOWN
,
440 .enc_alg
= OID_UNKNOWN
,
442 this->signerinfos
->insert_last(this->signerinfos
, info
);
444 case PKCS7_SIGNER_INFO_VERSION
:
445 version
= object
.len ?
(int)*object
.ptr
: 0;
446 DBG2(DBG_LIB
, " v%d", version
);
449 info
->issuer
= identification_create_from_encoding(
450 ID_DER_ASN1_DN
, object
);
452 case PKCS7_SERIAL_NUMBER
:
453 info
->serial
= identification_create_from_encoding(
456 case PKCS7_AUTH_ATTRIBUTES
:
457 *object
.ptr
= ASN1_SET
;
458 info
->attributes
= pkcs9_create_from_chunk(object
, level
+1);
459 *object
.ptr
= ASN1_CONTEXT_C_0
;
461 case PKCS7_DIGEST_ALGORITHM
:
462 info
->digest_alg
= asn1_parse_algorithmIdentifier(object
,
465 case PKCS7_DIGEST_ENC_ALGORITHM
:
466 info
->enc_alg
= asn1_parse_algorithmIdentifier(object
,
469 case PKCS7_ENCRYPTED_DIGEST
:
470 info
->encrypted_digest
= chunk_clone(object
);
474 success
= parser
->success(parser
);
475 parser
->destroy(parser
);
483 pkcs7_t
*pkcs7_signed_data_load(chunk_t encoding
, chunk_t content
)
485 private_pkcs7_signed_data_t
*this = create_empty();
487 this->encoding
= chunk_clone(encoding
);
488 if (!parse(this, content
))
493 return &this->public;
497 * build a DER-encoded issuerAndSerialNumber object
499 static chunk_t
build_issuerAndSerialNumber(certificate_t
*cert
)
501 identification_t
*issuer
= cert
->get_issuer(cert
);
502 chunk_t serial
= chunk_empty
;
504 if (cert
->get_type(cert
) == CERT_X509
)
506 x509_t
*x509
= (x509_t
*)cert
;
507 serial
= x509
->get_serial(x509
);
510 return asn1_wrap(ASN1_SEQUENCE
, "cm",
511 issuer
->get_encoding(issuer
),
512 asn1_integer("c", serial
));
516 * Generate a new PKCS#7 signed-data container
518 static bool generate(private_pkcs7_signed_data_t
*this, private_key_t
*key
,
519 certificate_t
*cert
, hash_algorithm_t alg
, pkcs9_t
*pkcs9
)
521 chunk_t authenticatedAttributes
= chunk_empty
;
522 chunk_t encryptedDigest
= chunk_empty
;
523 chunk_t data
, signerInfo
, encoding
= chunk_empty
;
524 chunk_t messageDigest
, signingTime
, attributes
;
525 signature_scheme_t scheme
;
530 digest_oid
= hasher_algorithm_to_oid(alg
);
531 scheme
= signature_scheme_from_oid(digest_oid
);
533 if (!this->content
->get_data(this->content
, &data
))
538 hasher
= lib
->crypto
->create_hasher(lib
->crypto
, alg
);
539 if (!hasher
|| !hasher
->allocate_hash(hasher
, data
, &messageDigest
))
542 DBG1(DBG_LIB
, " hash algorithm %N not support",
543 hash_algorithm_names
, alg
);
547 hasher
->destroy(hasher
);
548 pkcs9
->add_attribute(pkcs9
,
549 OID_PKCS9_MESSAGE_DIGEST
,
550 asn1_wrap(ASN1_OCTET_STRING
, "m", messageDigest
));
552 /* take the current time as signingTime */
554 signingTime
= asn1_from_time(&now
, ASN1_UTCTIME
);
555 pkcs9
->add_attribute(pkcs9
, OID_PKCS9_SIGNING_TIME
, signingTime
);
556 pkcs9
->add_attribute(pkcs9
, OID_PKCS9_CONTENT_TYPE
,
557 asn1_build_known_oid(OID_PKCS7_DATA
));
559 attributes
= pkcs9
->get_encoding(pkcs9
);
561 if (!key
->sign(key
, scheme
, attributes
, &encryptedDigest
))
566 authenticatedAttributes
= chunk_clone(attributes
);
567 *authenticatedAttributes
.ptr
= ASN1_CONTEXT_C_0
;
570 if (encryptedDigest
.ptr
)
572 encryptedDigest
= asn1_wrap(ASN1_OCTET_STRING
, "m", encryptedDigest
);
574 signerInfo
= asn1_wrap(ASN1_SEQUENCE
, "cmmmmm",
576 build_issuerAndSerialNumber(cert
),
577 asn1_algorithmIdentifier(digest_oid
),
578 authenticatedAttributes
,
579 asn1_algorithmIdentifier(OID_RSA_ENCRYPTION
),
582 if (!cert
->get_encoding(cert
, CERT_ASN1_DER
, &encoding
))
584 free(signerInfo
.ptr
);
587 if (!this->content
->get_encoding(this->content
, &data
))
590 free(signerInfo
.ptr
);
594 this->encoding
= asn1_wrap(ASN1_SEQUENCE
, "mm",
595 asn1_build_known_oid(OID_PKCS7_SIGNED_DATA
),
596 asn1_wrap(ASN1_CONTEXT_C_0
, "m",
597 asn1_wrap(ASN1_SEQUENCE
, "cmmmm",
599 asn1_wrap(ASN1_SET
, "m", asn1_algorithmIdentifier(digest_oid
)),
601 asn1_wrap(ASN1_CONTEXT_C_0
, "m", encoding
),
602 asn1_wrap(ASN1_SET
, "m", signerInfo
))));
605 pkcs9
->destroy(pkcs9
);
606 /* TODO: create signerInfos entry */
613 pkcs7_t
*pkcs7_signed_data_gen(container_type_t type
, va_list args
)
615 private_pkcs7_signed_data_t
*this;
616 chunk_t blob
= chunk_empty
;
617 hash_algorithm_t alg
= HASH_SHA1
;
618 private_key_t
*key
= NULL
;
619 certificate_t
*cert
= NULL
;
624 pkcs9
= pkcs9_create();
628 switch (va_arg(args
, builder_part_t
))
630 case BUILD_SIGNING_KEY
:
631 key
= va_arg(args
, private_key_t
*);
633 case BUILD_SIGNING_CERT
:
634 cert
= va_arg(args
, certificate_t
*);
636 case BUILD_DIGEST_ALG
:
637 alg
= va_arg(args
, int);
640 blob
= va_arg(args
, chunk_t
);
642 case BUILD_PKCS7_ATTRIBUTE
:
643 oid
= va_arg(args
, int);
644 value
= va_arg(args
, chunk_t
);
645 pkcs9
->add_attribute(pkcs9
, oid
, chunk_clone(value
));
650 pkcs9
->destroy(pkcs9
);
655 if (blob
.len
&& key
&& cert
)
657 this = create_empty();
659 this->creds
->add_cert(this->creds
, FALSE
, cert
->get_ref(cert
));
660 this->content
= lib
->creds
->create(lib
->creds
,
661 CRED_CONTAINER
, CONTAINER_PKCS7_DATA
,
662 BUILD_BLOB
, blob
, BUILD_END
);
664 if (this->content
&& generate(this, key
, cert
, alg
, pkcs9
))
666 return &this->public;
668 pkcs9
->destroy(pkcs9
);
673 pkcs9
->destroy(pkcs9
);