1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
40 * $Id: p7create.c,v 1.9 2008/02/03 06:08:48 nelson%bolyard.com Exp $
56 sec_pkcs7_init_content_info (SEC_PKCS7ContentInfo
*cinfo
, PRArenaPool
*poolp
,
57 SECOidTag kind
, PRBool detached
)
64 PORT_Assert (cinfo
!= NULL
&& poolp
!= NULL
);
65 if (cinfo
== NULL
|| poolp
== NULL
)
68 cinfo
->contentTypeTag
= SECOID_FindOIDByTag (kind
);
69 PORT_Assert (cinfo
->contentTypeTag
70 && cinfo
->contentTypeTag
->offset
== kind
);
72 rv
= SECITEM_CopyItem (poolp
, &(cinfo
->contentType
),
73 &(cinfo
->contentTypeTag
->oid
));
82 case SEC_OID_PKCS7_DATA
:
83 thing
= PORT_ArenaZAlloc (poolp
, sizeof(SECItem
));
84 cinfo
->content
.data
= (SECItem
*)thing
;
88 case SEC_OID_PKCS7_DIGESTED_DATA
:
89 thing
= PORT_ArenaZAlloc (poolp
, sizeof(SEC_PKCS7DigestedData
));
90 cinfo
->content
.digestedData
= (SEC_PKCS7DigestedData
*)thing
;
91 versionp
= &(cinfo
->content
.digestedData
->version
);
92 version
= SEC_PKCS7_DIGESTED_DATA_VERSION
;
94 case SEC_OID_PKCS7_ENCRYPTED_DATA
:
95 thing
= PORT_ArenaZAlloc (poolp
, sizeof(SEC_PKCS7EncryptedData
));
96 cinfo
->content
.encryptedData
= (SEC_PKCS7EncryptedData
*)thing
;
97 versionp
= &(cinfo
->content
.encryptedData
->version
);
98 version
= SEC_PKCS7_ENCRYPTED_DATA_VERSION
;
100 case SEC_OID_PKCS7_ENVELOPED_DATA
:
101 thing
= PORT_ArenaZAlloc (poolp
, sizeof(SEC_PKCS7EnvelopedData
));
102 cinfo
->content
.envelopedData
=
103 (SEC_PKCS7EnvelopedData
*)thing
;
104 versionp
= &(cinfo
->content
.envelopedData
->version
);
105 version
= SEC_PKCS7_ENVELOPED_DATA_VERSION
;
107 case SEC_OID_PKCS7_SIGNED_DATA
:
108 thing
= PORT_ArenaZAlloc (poolp
, sizeof(SEC_PKCS7SignedData
));
109 cinfo
->content
.signedData
=
110 (SEC_PKCS7SignedData
*)thing
;
111 versionp
= &(cinfo
->content
.signedData
->version
);
112 version
= SEC_PKCS7_SIGNED_DATA_VERSION
;
114 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA
:
115 thing
= PORT_ArenaZAlloc(poolp
,sizeof(SEC_PKCS7SignedAndEnvelopedData
));
116 cinfo
->content
.signedAndEnvelopedData
=
117 (SEC_PKCS7SignedAndEnvelopedData
*)thing
;
118 versionp
= &(cinfo
->content
.signedAndEnvelopedData
->version
);
119 version
= SEC_PKCS7_SIGNED_AND_ENVELOPED_DATA_VERSION
;
126 if (versionp
!= NULL
) {
129 PORT_Assert (version
>= 0);
130 dummy
= SEC_ASN1EncodeInteger (poolp
, versionp
, version
);
133 PORT_Assert (dummy
== versionp
);
140 static SEC_PKCS7ContentInfo
*
141 sec_pkcs7_create_content_info (SECOidTag kind
, PRBool detached
,
142 SECKEYGetPasswordKey pwfn
, void *pwfn_arg
)
144 SEC_PKCS7ContentInfo
*cinfo
;
148 poolp
= PORT_NewArena (1024); /* XXX what is right value? */
152 cinfo
= (SEC_PKCS7ContentInfo
*)PORT_ArenaZAlloc (poolp
, sizeof(*cinfo
));
154 PORT_FreeArena (poolp
, PR_FALSE
);
158 cinfo
->poolp
= poolp
;
160 cinfo
->pwfn_arg
= pwfn_arg
;
161 cinfo
->created
= PR_TRUE
;
164 rv
= sec_pkcs7_init_content_info (cinfo
, poolp
, kind
, detached
);
165 if (rv
!= SECSuccess
) {
166 PORT_FreeArena (poolp
, PR_FALSE
);
175 * Add a signer to a PKCS7 thing, verifying the signature cert first.
176 * Any error returns SECFailure.
178 * XXX Right now this only adds the *first* signer. It fails if you try
179 * to add a second one -- this needs to be fixed.
182 sec_pkcs7_add_signer (SEC_PKCS7ContentInfo
*cinfo
,
183 CERTCertificate
* cert
,
184 SECCertUsage certusage
,
185 CERTCertDBHandle
* certdb
,
186 SECOidTag digestalgtag
,
187 SECItem
* digestdata
)
189 SEC_PKCS7SignerInfo
*signerinfo
, **signerinfos
, ***signerinfosp
;
190 SECAlgorithmID
*digestalg
, **digestalgs
, ***digestalgsp
;
191 SECItem
*digest
, **digests
, ***digestsp
;
197 kind
= SEC_PKCS7ContentType (cinfo
);
199 case SEC_OID_PKCS7_SIGNED_DATA
:
201 SEC_PKCS7SignedData
*sdp
;
203 sdp
= cinfo
->content
.signedData
;
204 digestalgsp
= &(sdp
->digestAlgorithms
);
205 digestsp
= &(sdp
->digests
);
206 signerinfosp
= &(sdp
->signerInfos
);
209 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA
:
211 SEC_PKCS7SignedAndEnvelopedData
*saedp
;
213 saedp
= cinfo
->content
.signedAndEnvelopedData
;
214 digestalgsp
= &(saedp
->digestAlgorithms
);
215 digestsp
= &(saedp
->digests
);
216 signerinfosp
= &(saedp
->signerInfos
);
220 return SECFailure
; /* XXX set an error? */
224 * XXX I think that CERT_VerifyCert should do this if *it* is passed
227 if (certdb
== NULL
) {
228 certdb
= CERT_GetDefaultCertDB();
230 return SECFailure
; /* XXX set an error? */
233 if (CERT_VerifyCert (certdb
, cert
, PR_TRUE
, certusage
, PR_Now(),
234 cinfo
->pwfn_arg
, NULL
) != SECSuccess
)
236 /* XXX Did CERT_VerifyCert set an error? */
241 * XXX This is the check that we do not already have a signer.
242 * This is not what we really want -- we want to allow this
243 * and *add* the new signer.
245 PORT_Assert (*signerinfosp
== NULL
246 && *digestalgsp
== NULL
&& *digestsp
== NULL
);
247 if (*signerinfosp
!= NULL
|| *digestalgsp
!= NULL
|| *digestsp
!= NULL
)
250 mark
= PORT_ArenaMark (cinfo
->poolp
);
252 signerinfo
= (SEC_PKCS7SignerInfo
*)PORT_ArenaZAlloc (cinfo
->poolp
,
253 sizeof(SEC_PKCS7SignerInfo
));
254 if (signerinfo
== NULL
) {
255 PORT_ArenaRelease (cinfo
->poolp
, mark
);
259 dummy
= SEC_ASN1EncodeInteger (cinfo
->poolp
, &signerinfo
->version
,
260 SEC_PKCS7_SIGNER_INFO_VERSION
);
262 PORT_ArenaRelease (cinfo
->poolp
, mark
);
265 PORT_Assert (dummy
== &signerinfo
->version
);
267 signerinfo
->cert
= CERT_DupCertificate (cert
);
268 if (signerinfo
->cert
== NULL
) {
269 PORT_ArenaRelease (cinfo
->poolp
, mark
);
273 signerinfo
->issuerAndSN
= CERT_GetCertIssuerAndSN (cinfo
->poolp
, cert
);
274 if (signerinfo
->issuerAndSN
== NULL
) {
275 PORT_ArenaRelease (cinfo
->poolp
, mark
);
279 rv
= SECOID_SetAlgorithmID (cinfo
->poolp
, &signerinfo
->digestAlg
,
281 if (rv
!= SECSuccess
) {
282 PORT_ArenaRelease (cinfo
->poolp
, mark
);
287 * Okay, now signerinfo is all set. We just need to put it and its
288 * companions (another copy of the digest algorithm, and the digest
289 * itself if given) into the main structure.
291 * XXX If we are handling more than one signer, the following code
292 * needs to look through the digest algorithms already specified
293 * and see if the same one is there already. If it is, it does not
294 * need to be added again. Also, if it is there *and* the digest
295 * is not null, then the digest given should match the digest already
296 * specified -- if not, that is an error. Finally, the new signerinfo
297 * should be *added* to the set already found.
300 signerinfos
= (SEC_PKCS7SignerInfo
**)PORT_ArenaAlloc (cinfo
->poolp
,
301 2 * sizeof(SEC_PKCS7SignerInfo
*));
302 if (signerinfos
== NULL
) {
303 PORT_ArenaRelease (cinfo
->poolp
, mark
);
306 signerinfos
[0] = signerinfo
;
307 signerinfos
[1] = NULL
;
309 digestalg
= PORT_ArenaZAlloc (cinfo
->poolp
, sizeof(SECAlgorithmID
));
310 digestalgs
= PORT_ArenaAlloc (cinfo
->poolp
, 2 * sizeof(SECAlgorithmID
*));
311 if (digestalg
== NULL
|| digestalgs
== NULL
) {
312 PORT_ArenaRelease (cinfo
->poolp
, mark
);
315 rv
= SECOID_SetAlgorithmID (cinfo
->poolp
, digestalg
, digestalgtag
, NULL
);
316 if (rv
!= SECSuccess
) {
317 PORT_ArenaRelease (cinfo
->poolp
, mark
);
320 digestalgs
[0] = digestalg
;
321 digestalgs
[1] = NULL
;
323 if (digestdata
!= NULL
) {
324 digest
= (SECItem
*)PORT_ArenaAlloc (cinfo
->poolp
, sizeof(SECItem
));
325 digests
= (SECItem
**)PORT_ArenaAlloc (cinfo
->poolp
,
326 2 * sizeof(SECItem
*));
327 if (digest
== NULL
|| digests
== NULL
) {
328 PORT_ArenaRelease (cinfo
->poolp
, mark
);
331 rv
= SECITEM_CopyItem (cinfo
->poolp
, digest
, digestdata
);
332 if (rv
!= SECSuccess
) {
333 PORT_ArenaRelease (cinfo
->poolp
, mark
);
342 *signerinfosp
= signerinfos
;
343 *digestalgsp
= digestalgs
;
346 PORT_ArenaUnmark(cinfo
->poolp
, mark
);
352 * Helper function for creating an empty signedData.
354 static SEC_PKCS7ContentInfo
*
355 sec_pkcs7_create_signed_data (SECKEYGetPasswordKey pwfn
, void *pwfn_arg
)
357 SEC_PKCS7ContentInfo
*cinfo
;
358 SEC_PKCS7SignedData
*sigd
;
361 cinfo
= sec_pkcs7_create_content_info (SEC_OID_PKCS7_SIGNED_DATA
, PR_FALSE
,
366 sigd
= cinfo
->content
.signedData
;
367 PORT_Assert (sigd
!= NULL
);
370 * XXX Might we want to allow content types other than data?
371 * If so, via what interface?
373 rv
= sec_pkcs7_init_content_info (&(sigd
->contentInfo
), cinfo
->poolp
,
374 SEC_OID_PKCS7_DATA
, PR_TRUE
);
375 if (rv
!= SECSuccess
) {
376 SEC_PKCS7DestroyContentInfo (cinfo
);
385 * Start a PKCS7 signing context.
387 * "cert" is the cert that will be used to sign the data. It will be
388 * checked for validity.
390 * "certusage" describes the signing usage (e.g. certUsageEmailSigner)
391 * XXX Maybe SECCertUsage should be split so that our caller just says
392 * "email" and *we* add the "signing" part -- otherwise our caller
393 * could be lying about the usage; we do not want to allow encryption
394 * certs for signing or vice versa.
396 * "certdb" is the cert database to use for verifying the cert.
397 * It can be NULL if a default database is available (like in the client).
399 * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1).
401 * "digest" is the actual digest of the data. It must be provided in
402 * the case of detached data or NULL if the content will be included.
404 * The return value can be passed to functions which add things to
405 * it like attributes, then eventually to SEC_PKCS7Encode() or to
406 * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
407 * SEC_PKCS7DestroyContentInfo().
409 * An error results in a return value of NULL and an error set.
410 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
412 SEC_PKCS7ContentInfo
*
413 SEC_PKCS7CreateSignedData (CERTCertificate
*cert
,
414 SECCertUsage certusage
,
415 CERTCertDBHandle
*certdb
,
418 SECKEYGetPasswordKey pwfn
, void *pwfn_arg
)
420 SEC_PKCS7ContentInfo
*cinfo
;
423 cinfo
= sec_pkcs7_create_signed_data (pwfn
, pwfn_arg
);
427 rv
= sec_pkcs7_add_signer (cinfo
, cert
, certusage
, certdb
,
429 if (rv
!= SECSuccess
) {
430 SEC_PKCS7DestroyContentInfo (cinfo
);
438 static SEC_PKCS7Attribute
*
439 sec_pkcs7_create_attribute (PRArenaPool
*poolp
, SECOidTag oidtag
,
440 SECItem
*value
, PRBool encoded
)
442 SEC_PKCS7Attribute
*attr
;
446 PORT_Assert (poolp
!= NULL
);
447 mark
= PORT_ArenaMark (poolp
);
449 attr
= (SEC_PKCS7Attribute
*)PORT_ArenaAlloc (poolp
,
450 sizeof(SEC_PKCS7Attribute
));
454 attr
->typeTag
= SECOID_FindOIDByTag (oidtag
);
455 if (attr
->typeTag
== NULL
)
458 if (SECITEM_CopyItem (poolp
, &(attr
->type
),
459 &(attr
->typeTag
->oid
)) != SECSuccess
)
462 values
= (SECItem
**)PORT_ArenaAlloc (poolp
, 2 * sizeof(SECItem
*));
469 copy
= (SECItem
*)PORT_ArenaAlloc (poolp
, sizeof(SECItem
));
473 if (SECITEM_CopyItem (poolp
, copy
, value
) != SECSuccess
)
481 attr
->values
= values
;
482 attr
->encoded
= encoded
;
484 PORT_ArenaUnmark (poolp
, mark
);
488 PORT_Assert (mark
!= NULL
);
489 PORT_ArenaRelease (poolp
, mark
);
495 sec_pkcs7_add_attribute (SEC_PKCS7ContentInfo
*cinfo
,
496 SEC_PKCS7Attribute
***attrsp
,
497 SEC_PKCS7Attribute
*attr
)
499 SEC_PKCS7Attribute
**attrs
;
503 PORT_Assert (SEC_PKCS7ContentType (cinfo
) == SEC_OID_PKCS7_SIGNED_DATA
);
504 if (SEC_PKCS7ContentType (cinfo
) != SEC_OID_PKCS7_SIGNED_DATA
)
512 * We already have some attributes, and just need to add this
517 * We should already have the *required* attributes, which were
518 * created/added at the same time the first attribute was added.
520 PORT_Assert (sec_PKCS7FindAttribute (attrs
,
521 SEC_OID_PKCS9_CONTENT_TYPE
,
523 PORT_Assert (sec_PKCS7FindAttribute (attrs
,
524 SEC_OID_PKCS9_MESSAGE_DIGEST
,
527 for (count
= 0; attrs
[count
] != NULL
; count
++)
529 attrs
= (SEC_PKCS7Attribute
**)PORT_ArenaGrow (cinfo
->poolp
, attrs
,
530 (count
+ 1) * sizeof(SEC_PKCS7Attribute
*),
531 (count
+ 2) * sizeof(SEC_PKCS7Attribute
*));
536 attrs
[count
+1] = NULL
;
543 * This is the first time an attribute is going in.
544 * We need to create and add the required attributes, and then
545 * we will also add in the one our caller gave us.
549 * There are 2 required attributes, plus the one our caller wants
550 * to add, plus we always end with a NULL one. Thus, four slots.
552 attrs
= (SEC_PKCS7Attribute
**)PORT_ArenaAlloc (cinfo
->poolp
,
553 4 * sizeof(SEC_PKCS7Attribute
*));
557 mark
= PORT_ArenaMark (cinfo
->poolp
);
560 * First required attribute is the content type of the data
563 ct_value
= &(cinfo
->content
.signedData
->contentInfo
.contentType
);
564 attrs
[0] = sec_pkcs7_create_attribute (cinfo
->poolp
,
565 SEC_OID_PKCS9_CONTENT_TYPE
,
568 * Second required attribute is the message digest of the data
569 * being signed; we leave the value NULL for now (just create
570 * the place for it to go), and the encoder will fill it in later.
572 attrs
[1] = sec_pkcs7_create_attribute (cinfo
->poolp
,
573 SEC_OID_PKCS9_MESSAGE_DIGEST
,
575 if (attrs
[0] == NULL
|| attrs
[1] == NULL
) {
576 PORT_ArenaRelease (cinfo
->poolp
, mark
);
584 PORT_ArenaUnmark (cinfo
->poolp
, mark
);
590 * Add the signing time to the authenticated (i.e. signed) attributes
591 * of "cinfo". This is expected to be included in outgoing signed
592 * messages for email (S/MIME) but is likely useful in other situations.
594 * This should only be added once; a second call will either do
595 * nothing or replace an old signing time with a newer one.
597 * XXX This will probably just shove the current time into "cinfo"
598 * but it will not actually get signed until the entire item is
599 * processed for encoding. Is this (expected to be small) delay okay?
601 * "cinfo" should be of type signedData (the only kind of pkcs7 data
602 * that is allowed authenticated attributes); SECFailure will be returned
606 SEC_PKCS7AddSigningTime (SEC_PKCS7ContentInfo
*cinfo
)
608 SEC_PKCS7SignerInfo
**signerinfos
;
609 SEC_PKCS7Attribute
*attr
;
614 PORT_Assert (SEC_PKCS7ContentType (cinfo
) == SEC_OID_PKCS7_SIGNED_DATA
);
615 if (SEC_PKCS7ContentType (cinfo
) != SEC_OID_PKCS7_SIGNED_DATA
)
618 signerinfos
= cinfo
->content
.signedData
->signerInfos
;
620 /* There has to be a signer, or it makes no sense. */
621 if (signerinfos
== NULL
|| signerinfos
[0] == NULL
)
624 rv
= DER_EncodeTimeChoice(NULL
, &stime
, PR_Now());
625 if (rv
!= SECSuccess
)
628 attr
= sec_pkcs7_create_attribute (cinfo
->poolp
,
629 SEC_OID_PKCS9_SIGNING_TIME
,
631 SECITEM_FreeItem (&stime
, PR_FALSE
);
637 for (si
= 0; signerinfos
[si
] != NULL
; si
++) {
638 SEC_PKCS7Attribute
*oattr
;
640 oattr
= sec_PKCS7FindAttribute (signerinfos
[si
]->authAttr
,
641 SEC_OID_PKCS9_SIGNING_TIME
, PR_FALSE
);
642 PORT_Assert (oattr
== NULL
);
644 continue; /* XXX or would it be better to replace it? */
646 rv
= sec_pkcs7_add_attribute (cinfo
, &(signerinfos
[si
]->authAttr
),
648 if (rv
!= SECSuccess
)
649 break; /* could try to continue, but may as well give up now */
657 * Add the specified attribute to the authenticated (i.e. signed) attributes
658 * of "cinfo" -- "oidtag" describes the attribute and "value" is the
659 * value to be associated with it. NOTE! "value" must already be encoded;
660 * no interpretation of "oidtag" is done. Also, it is assumed that this
661 * signedData has only one signer -- if we ever need to add attributes
662 * when there is more than one signature, we need a way to specify *which*
663 * signature should get the attribute.
665 * XXX Technically, a signed attribute can have multiple values; if/when
666 * we ever need to support an attribute which takes multiple values, we
667 * either need to change this interface or create an AddSignedAttributeValue
668 * which can be called subsequently, and would then append a value.
670 * "cinfo" should be of type signedData (the only kind of pkcs7 data
671 * that is allowed authenticated attributes); SECFailure will be returned
675 SEC_PKCS7AddSignedAttribute (SEC_PKCS7ContentInfo
*cinfo
,
679 SEC_PKCS7SignerInfo
**signerinfos
;
680 SEC_PKCS7Attribute
*attr
;
682 PORT_Assert (SEC_PKCS7ContentType (cinfo
) == SEC_OID_PKCS7_SIGNED_DATA
);
683 if (SEC_PKCS7ContentType (cinfo
) != SEC_OID_PKCS7_SIGNED_DATA
)
686 signerinfos
= cinfo
->content
.signedData
->signerInfos
;
689 * No signature or more than one means no deal.
691 if (signerinfos
== NULL
|| signerinfos
[0] == NULL
|| signerinfos
[1] != NULL
)
694 attr
= sec_pkcs7_create_attribute (cinfo
->poolp
, oidtag
, value
, PR_TRUE
);
698 return sec_pkcs7_add_attribute (cinfo
, &(signerinfos
[0]->authAttr
), attr
);
703 * Mark that the signer certificates and their issuing chain should
704 * be included in the encoded data. This is expected to be used
705 * in outgoing signed messages for email (S/MIME).
707 * "certdb" is the cert database to use for finding the chain.
708 * It can be NULL, meaning use the default database.
710 * "cinfo" should be of type signedData or signedAndEnvelopedData;
711 * SECFailure will be returned if it is not.
714 SEC_PKCS7IncludeCertChain (SEC_PKCS7ContentInfo
*cinfo
,
715 CERTCertDBHandle
*certdb
)
718 SEC_PKCS7SignerInfo
*signerinfo
, **signerinfos
;
720 kind
= SEC_PKCS7ContentType (cinfo
);
722 case SEC_OID_PKCS7_SIGNED_DATA
:
723 signerinfos
= cinfo
->content
.signedData
->signerInfos
;
725 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA
:
726 signerinfos
= cinfo
->content
.signedAndEnvelopedData
->signerInfos
;
729 return SECFailure
; /* XXX set an error? */
732 if (signerinfos
== NULL
) /* no signer, no certs? */
733 return SECFailure
; /* XXX set an error? */
735 if (certdb
== NULL
) {
736 certdb
= CERT_GetDefaultCertDB();
737 if (certdb
== NULL
) {
738 PORT_SetError (SEC_ERROR_BAD_DATABASE
);
743 /* XXX Should it be an error if we find no signerinfo or no certs? */
744 while ((signerinfo
= *signerinfos
++) != NULL
) {
745 if (signerinfo
->cert
!= NULL
)
746 /* get the cert chain. don't send the root to avoid contamination
747 * of old clients with a new root that they don't trust
749 signerinfo
->certList
= CERT_CertChainFromCert (signerinfo
->cert
,
750 certUsageEmailSigner
,
759 * Helper function to add a certificate chain for inclusion in the
760 * bag of certificates in a signedData.
763 sec_pkcs7_add_cert_chain (SEC_PKCS7ContentInfo
*cinfo
,
764 CERTCertificate
*cert
,
765 CERTCertDBHandle
*certdb
)
768 CERTCertificateList
*certlist
, **certlists
, ***certlistsp
;
771 kind
= SEC_PKCS7ContentType (cinfo
);
773 case SEC_OID_PKCS7_SIGNED_DATA
:
775 SEC_PKCS7SignedData
*sdp
;
777 sdp
= cinfo
->content
.signedData
;
778 certlistsp
= &(sdp
->certLists
);
781 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA
:
783 SEC_PKCS7SignedAndEnvelopedData
*saedp
;
785 saedp
= cinfo
->content
.signedAndEnvelopedData
;
786 certlistsp
= &(saedp
->certLists
);
790 return SECFailure
; /* XXX set an error? */
793 if (certdb
== NULL
) {
794 certdb
= CERT_GetDefaultCertDB();
795 if (certdb
== NULL
) {
796 PORT_SetError (SEC_ERROR_BAD_DATABASE
);
801 certlist
= CERT_CertChainFromCert (cert
, certUsageEmailSigner
, PR_FALSE
);
802 if (certlist
== NULL
)
805 certlists
= *certlistsp
;
806 if (certlists
== NULL
) {
808 certlists
= (CERTCertificateList
**)PORT_ArenaAlloc (cinfo
->poolp
,
809 2 * sizeof(CERTCertificateList
*));
811 for (count
= 0; certlists
[count
] != NULL
; count
++)
813 PORT_Assert (count
); /* should be at least one already */
814 certlists
= (CERTCertificateList
**)PORT_ArenaGrow (cinfo
->poolp
,
816 (count
+ 1) * sizeof(CERTCertificateList
*),
817 (count
+ 2) * sizeof(CERTCertificateList
*));
820 if (certlists
== NULL
) {
821 CERT_DestroyCertificateList (certlist
);
825 certlists
[count
] = certlist
;
826 certlists
[count
+ 1] = NULL
;
828 *certlistsp
= certlists
;
835 * Helper function to add a certificate for inclusion in the bag of
836 * certificates in a signedData.
839 sec_pkcs7_add_certificate (SEC_PKCS7ContentInfo
*cinfo
,
840 CERTCertificate
*cert
)
843 CERTCertificate
**certs
, ***certsp
;
846 kind
= SEC_PKCS7ContentType (cinfo
);
848 case SEC_OID_PKCS7_SIGNED_DATA
:
850 SEC_PKCS7SignedData
*sdp
;
852 sdp
= cinfo
->content
.signedData
;
853 certsp
= &(sdp
->certs
);
856 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA
:
858 SEC_PKCS7SignedAndEnvelopedData
*saedp
;
860 saedp
= cinfo
->content
.signedAndEnvelopedData
;
861 certsp
= &(saedp
->certs
);
865 return SECFailure
; /* XXX set an error? */
868 cert
= CERT_DupCertificate (cert
);
875 certs
= (CERTCertificate
**)PORT_ArenaAlloc (cinfo
->poolp
,
876 2 * sizeof(CERTCertificate
*));
878 for (count
= 0; certs
[count
] != NULL
; count
++)
880 PORT_Assert (count
); /* should be at least one already */
881 certs
= (CERTCertificate
**)PORT_ArenaGrow (cinfo
->poolp
, certs
,
882 (count
+ 1) * sizeof(CERTCertificate
*),
883 (count
+ 2) * sizeof(CERTCertificate
*));
887 CERT_DestroyCertificate (cert
);
892 certs
[count
+ 1] = NULL
;
901 * Create a PKCS7 certs-only container.
903 * "cert" is the (first) cert that will be included.
905 * "include_chain" specifies whether the entire chain for "cert" should
908 * "certdb" is the cert database to use for finding the chain.
909 * It can be NULL in when "include_chain" is false, or when meaning
910 * use the default database.
912 * More certs and chains can be added via AddCertificate and AddCertChain.
914 * An error results in a return value of NULL and an error set.
915 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
917 SEC_PKCS7ContentInfo
*
918 SEC_PKCS7CreateCertsOnly (CERTCertificate
*cert
,
919 PRBool include_chain
,
920 CERTCertDBHandle
*certdb
)
922 SEC_PKCS7ContentInfo
*cinfo
;
925 cinfo
= sec_pkcs7_create_signed_data (NULL
, NULL
);
930 rv
= sec_pkcs7_add_cert_chain (cinfo
, cert
, certdb
);
932 rv
= sec_pkcs7_add_certificate (cinfo
, cert
);
934 if (rv
!= SECSuccess
) {
935 SEC_PKCS7DestroyContentInfo (cinfo
);
944 * Add "cert" and its entire chain to the set of certs included in "cinfo".
946 * "certdb" is the cert database to use for finding the chain.
947 * It can be NULL, meaning use the default database.
949 * "cinfo" should be of type signedData or signedAndEnvelopedData;
950 * SECFailure will be returned if it is not.
953 SEC_PKCS7AddCertChain (SEC_PKCS7ContentInfo
*cinfo
,
954 CERTCertificate
*cert
,
955 CERTCertDBHandle
*certdb
)
959 kind
= SEC_PKCS7ContentType (cinfo
);
960 if (kind
!= SEC_OID_PKCS7_SIGNED_DATA
961 && kind
!= SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA
)
962 return SECFailure
; /* XXX set an error? */
964 return sec_pkcs7_add_cert_chain (cinfo
, cert
, certdb
);
969 * Add "cert" to the set of certs included in "cinfo".
971 * "cinfo" should be of type signedData or signedAndEnvelopedData;
972 * SECFailure will be returned if it is not.
975 SEC_PKCS7AddCertificate (SEC_PKCS7ContentInfo
*cinfo
, CERTCertificate
*cert
)
979 kind
= SEC_PKCS7ContentType (cinfo
);
980 if (kind
!= SEC_OID_PKCS7_SIGNED_DATA
981 && kind
!= SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA
)
982 return SECFailure
; /* XXX set an error? */
984 return sec_pkcs7_add_certificate (cinfo
, cert
);
989 sec_pkcs7_init_encrypted_content_info (SEC_PKCS7EncryptedContentInfo
*enccinfo
,
991 SECOidTag kind
, PRBool detached
,
992 SECOidTag encalg
, int keysize
)
996 PORT_Assert (enccinfo
!= NULL
&& poolp
!= NULL
);
997 if (enccinfo
== NULL
|| poolp
== NULL
)
1001 * XXX Some day we may want to allow for other kinds. That needs
1002 * more work and modifications to the creation interface, etc.
1003 * For now, allow but notice callers who pass in other kinds.
1004 * They are responsible for creating the inner type and encoding,
1005 * if it is other than DATA.
1007 PORT_Assert (kind
== SEC_OID_PKCS7_DATA
);
1009 enccinfo
->contentTypeTag
= SECOID_FindOIDByTag (kind
);
1010 PORT_Assert (enccinfo
->contentTypeTag
1011 && enccinfo
->contentTypeTag
->offset
== kind
);
1013 rv
= SECITEM_CopyItem (poolp
, &(enccinfo
->contentType
),
1014 &(enccinfo
->contentTypeTag
->oid
));
1015 if (rv
!= SECSuccess
)
1018 /* Save keysize and algorithm for later. */
1019 enccinfo
->keysize
= keysize
;
1020 enccinfo
->encalg
= encalg
;
1027 * Add a recipient to a PKCS7 thing, verifying their cert first.
1028 * Any error returns SECFailure.
1031 sec_pkcs7_add_recipient (SEC_PKCS7ContentInfo
*cinfo
,
1032 CERTCertificate
*cert
,
1033 SECCertUsage certusage
,
1034 CERTCertDBHandle
*certdb
)
1037 SEC_PKCS7RecipientInfo
*recipientinfo
, **recipientinfos
, ***recipientinfosp
;
1042 kind
= SEC_PKCS7ContentType (cinfo
);
1044 case SEC_OID_PKCS7_ENVELOPED_DATA
:
1046 SEC_PKCS7EnvelopedData
*edp
;
1048 edp
= cinfo
->content
.envelopedData
;
1049 recipientinfosp
= &(edp
->recipientInfos
);
1052 case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA
:
1054 SEC_PKCS7SignedAndEnvelopedData
*saedp
;
1056 saedp
= cinfo
->content
.signedAndEnvelopedData
;
1057 recipientinfosp
= &(saedp
->recipientInfos
);
1061 return SECFailure
; /* XXX set an error? */
1065 * XXX I think that CERT_VerifyCert should do this if *it* is passed
1068 if (certdb
== NULL
) {
1069 certdb
= CERT_GetDefaultCertDB();
1071 return SECFailure
; /* XXX set an error? */
1074 if (CERT_VerifyCert (certdb
, cert
, PR_TRUE
, certusage
, PR_Now(),
1075 cinfo
->pwfn_arg
, NULL
) != SECSuccess
)
1077 /* XXX Did CERT_VerifyCert set an error? */
1081 mark
= PORT_ArenaMark (cinfo
->poolp
);
1083 recipientinfo
= (SEC_PKCS7RecipientInfo
*)PORT_ArenaZAlloc (cinfo
->poolp
,
1084 sizeof(SEC_PKCS7RecipientInfo
));
1085 if (recipientinfo
== NULL
) {
1086 PORT_ArenaRelease (cinfo
->poolp
, mark
);
1090 dummy
= SEC_ASN1EncodeInteger (cinfo
->poolp
, &recipientinfo
->version
,
1091 SEC_PKCS7_RECIPIENT_INFO_VERSION
);
1092 if (dummy
== NULL
) {
1093 PORT_ArenaRelease (cinfo
->poolp
, mark
);
1096 PORT_Assert (dummy
== &recipientinfo
->version
);
1098 recipientinfo
->cert
= CERT_DupCertificate (cert
);
1099 if (recipientinfo
->cert
== NULL
) {
1100 PORT_ArenaRelease (cinfo
->poolp
, mark
);
1104 recipientinfo
->issuerAndSN
= CERT_GetCertIssuerAndSN (cinfo
->poolp
, cert
);
1105 if (recipientinfo
->issuerAndSN
== NULL
) {
1106 PORT_ArenaRelease (cinfo
->poolp
, mark
);
1111 * Okay, now recipientinfo is all set. We just need to put it into
1112 * the main structure.
1114 * If this is the first recipient, allocate a new recipientinfos array;
1115 * otherwise, reallocate the array, making room for the new entry.
1117 recipientinfos
= *recipientinfosp
;
1118 if (recipientinfos
== NULL
) {
1120 recipientinfos
= (SEC_PKCS7RecipientInfo
**)PORT_ArenaAlloc (
1122 2 * sizeof(SEC_PKCS7RecipientInfo
*));
1124 for (count
= 0; recipientinfos
[count
] != NULL
; count
++)
1126 PORT_Assert (count
); /* should be at least one already */
1127 recipientinfos
= (SEC_PKCS7RecipientInfo
**)PORT_ArenaGrow (
1128 cinfo
->poolp
, recipientinfos
,
1129 (count
+ 1) * sizeof(SEC_PKCS7RecipientInfo
*),
1130 (count
+ 2) * sizeof(SEC_PKCS7RecipientInfo
*));
1133 if (recipientinfos
== NULL
) {
1134 PORT_ArenaRelease (cinfo
->poolp
, mark
);
1138 recipientinfos
[count
] = recipientinfo
;
1139 recipientinfos
[count
+ 1] = NULL
;
1141 *recipientinfosp
= recipientinfos
;
1143 PORT_ArenaUnmark (cinfo
->poolp
, mark
);
1149 * Start a PKCS7 enveloping context.
1151 * "cert" is the cert for the recipient. It will be checked for validity.
1153 * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
1154 * XXX Maybe SECCertUsage should be split so that our caller just says
1155 * "email" and *we* add the "recipient" part -- otherwise our caller
1156 * could be lying about the usage; we do not want to allow encryption
1157 * certs for signing or vice versa.
1159 * "certdb" is the cert database to use for verifying the cert.
1160 * It can be NULL if a default database is available (like in the client).
1162 * "encalg" specifies the bulk encryption algorithm to use (e.g. SEC_OID_RC2).
1164 * "keysize" specifies the bulk encryption key size, in bits.
1166 * The return value can be passed to functions which add things to
1167 * it like more recipients, then eventually to SEC_PKCS7Encode() or to
1168 * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
1169 * SEC_PKCS7DestroyContentInfo().
1171 * An error results in a return value of NULL and an error set.
1172 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
1174 extern SEC_PKCS7ContentInfo
*
1175 SEC_PKCS7CreateEnvelopedData (CERTCertificate
*cert
,
1176 SECCertUsage certusage
,
1177 CERTCertDBHandle
*certdb
,
1180 SECKEYGetPasswordKey pwfn
, void *pwfn_arg
)
1182 SEC_PKCS7ContentInfo
*cinfo
;
1183 SEC_PKCS7EnvelopedData
*envd
;
1186 cinfo
= sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENVELOPED_DATA
,
1187 PR_FALSE
, pwfn
, pwfn_arg
);
1191 rv
= sec_pkcs7_add_recipient (cinfo
, cert
, certusage
, certdb
);
1192 if (rv
!= SECSuccess
) {
1193 SEC_PKCS7DestroyContentInfo (cinfo
);
1197 envd
= cinfo
->content
.envelopedData
;
1198 PORT_Assert (envd
!= NULL
);
1201 * XXX Might we want to allow content types other than data?
1202 * If so, via what interface?
1204 rv
= sec_pkcs7_init_encrypted_content_info (&(envd
->encContentInfo
),
1206 SEC_OID_PKCS7_DATA
, PR_FALSE
,
1208 if (rv
!= SECSuccess
) {
1209 SEC_PKCS7DestroyContentInfo (cinfo
);
1213 /* XXX Anything more to do here? */
1220 * Add another recipient to an encrypted message.
1222 * "cinfo" should be of type envelopedData or signedAndEnvelopedData;
1223 * SECFailure will be returned if it is not.
1225 * "cert" is the cert for the recipient. It will be checked for validity.
1227 * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
1228 * XXX Maybe SECCertUsage should be split so that our caller just says
1229 * "email" and *we* add the "recipient" part -- otherwise our caller
1230 * could be lying about the usage; we do not want to allow encryption
1231 * certs for signing or vice versa.
1233 * "certdb" is the cert database to use for verifying the cert.
1234 * It can be NULL if a default database is available (like in the client).
1237 SEC_PKCS7AddRecipient (SEC_PKCS7ContentInfo
*cinfo
,
1238 CERTCertificate
*cert
,
1239 SECCertUsage certusage
,
1240 CERTCertDBHandle
*certdb
)
1242 return sec_pkcs7_add_recipient (cinfo
, cert
, certusage
, certdb
);
1247 * Create an empty PKCS7 data content info.
1249 * An error results in a return value of NULL and an error set.
1250 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
1252 SEC_PKCS7ContentInfo
*
1253 SEC_PKCS7CreateData (void)
1255 return sec_pkcs7_create_content_info (SEC_OID_PKCS7_DATA
, PR_FALSE
,
1261 * Create an empty PKCS7 encrypted content info.
1263 * "algorithm" specifies the bulk encryption algorithm to use.
1265 * An error results in a return value of NULL and an error set.
1266 * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
1268 SEC_PKCS7ContentInfo
*
1269 SEC_PKCS7CreateEncryptedData (SECOidTag algorithm
, int keysize
,
1270 SECKEYGetPasswordKey pwfn
, void *pwfn_arg
)
1272 SEC_PKCS7ContentInfo
*cinfo
;
1273 SECAlgorithmID
*algid
;
1274 SEC_PKCS7EncryptedData
*enc_data
;
1277 cinfo
= sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENCRYPTED_DATA
,
1278 PR_FALSE
, pwfn
, pwfn_arg
);
1282 enc_data
= cinfo
->content
.encryptedData
;
1283 algid
= &(enc_data
->encContentInfo
.contentEncAlg
);
1285 if (!SEC_PKCS5IsAlgorithmPBEAlgTag(algorithm
)) {
1286 rv
= SECOID_SetAlgorithmID (cinfo
->poolp
, algid
, algorithm
, NULL
);
1288 /* Assume password-based-encryption.
1289 * Note: we can't generate pkcs5v2 from this interface.
1290 * PK11_CreateBPEAlgorithmID generates pkcs5v2 by accepting
1291 * non-PBE oids and assuming that they are pkcs5v2 oids, but
1292 * NSS_CMSEncryptedData_Create accepts non-PBE oids as regular
1293 * CMS encrypted data, so we can't tell SEC_PKCS7CreateEncryptedtedData
1294 * to create pkcs5v2 PBEs */
1295 SECAlgorithmID
*pbe_algid
;
1296 pbe_algid
= PK11_CreatePBEAlgorithmID (algorithm
, 1, NULL
);
1297 if (pbe_algid
== NULL
) {
1300 rv
= SECOID_CopyAlgorithmID (cinfo
->poolp
, algid
, pbe_algid
);
1301 SECOID_DestroyAlgorithmID (pbe_algid
, PR_TRUE
);
1305 if (rv
!= SECSuccess
) {
1306 SEC_PKCS7DestroyContentInfo (cinfo
);
1310 rv
= sec_pkcs7_init_encrypted_content_info (&(enc_data
->encContentInfo
),
1312 SEC_OID_PKCS7_DATA
, PR_FALSE
,
1313 algorithm
, keysize
);
1314 if (rv
!= SECSuccess
) {
1315 SEC_PKCS7DestroyContentInfo (cinfo
);