2 PKCS#7 SignedData Verification Wrapper Implementation over OpenSSL.
4 Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 #include "InternalCryptLib.h"
17 #include <openssl/objects.h>
18 #include <openssl/x509.h>
19 #include <openssl/pkcs7.h>
21 UINT8 mOidValue
[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };
24 Verification callback function to override any existing callbacks in OpenSSL
25 for intermediate certificate supports.
27 @param[in] Status Original status before calling this callback.
28 @param[in] Context X509 store context.
30 @retval 1 Current X509 certificate is verified successfully.
31 @retval 0 Verification failed.
37 IN X509_STORE_CTX
*Context
46 Error
= (INTN
) X509_STORE_CTX_get_error (Context
);
49 // X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT and X509_V_ERR_UNABLE_TO_GET_ISSUER_
50 // CERT_LOCALLY mean a X509 certificate is not self signed and its issuer
51 // can not be found in X509_verify_cert of X509_vfy.c.
52 // In order to support intermediate certificate node, we override the
53 // errors if the certification is obtained from X509 store, i.e. it is
54 // a trusted ceritifcate node that is enrolled by user.
55 // Besides,X509_V_ERR_CERT_UNTRUSTED and X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
56 // are also ignored to enable such feature.
58 if ((Error
== X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT
) ||
59 (Error
== X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
)) {
60 Obj
= (X509_OBJECT
*) malloc (sizeof (X509_OBJECT
));
65 Obj
->type
= X509_LU_X509
;
66 Obj
->data
.x509
= Context
->current_cert
;
68 CRYPTO_w_lock (CRYPTO_LOCK_X509_STORE
);
70 if (X509_OBJECT_retrieve_match (Context
->ctx
->objs
, Obj
)) {
74 // If any certificate in the chain is enrolled as trusted certificate,
75 // pass the certificate verification.
77 if (Error
== X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
) {
78 Count
= (INTN
) sk_X509_num (Context
->chain
);
79 for (Index
= 0; Index
< Count
; Index
++) {
80 Obj
->data
.x509
= sk_X509_value (Context
->chain
, (int) Index
);
81 if (X509_OBJECT_retrieve_match (Context
->ctx
->objs
, Obj
)) {
89 CRYPTO_w_unlock (CRYPTO_LOCK_X509_STORE
);
92 if ((Error
== X509_V_ERR_CERT_UNTRUSTED
) ||
93 (Error
== X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
)) {
105 Creates a PKCS#7 signedData as described in "PKCS #7: Cryptographic Message
106 Syntax Standard, version 1.5". This interface is only intended to be used for
107 application to perform PKCS#7 functionality validation.
109 @param[in] PrivateKey Pointer to the PEM-formatted private key data for
111 @param[in] PrivateKeySize Size of the PEM private key data in bytes.
112 @param[in] KeyPassword NULL-terminated passphrase used for encrypted PEM
114 @param[in] InData Pointer to the content to be signed.
115 @param[in] InDataSize Size of InData in bytes.
116 @param[in] SignCert Pointer to signer's DER-encoded certificate to sign with.
117 @param[in] OtherCerts Pointer to an optional additional set of certificates to
118 include in the PKCS#7 signedData (e.g. any intermediate
120 @param[out] SignedData Pointer to output PKCS#7 signedData.
121 @param[out] SignedDataSize Size of SignedData in bytes.
123 @retval TRUE PKCS#7 data signing succeeded.
124 @retval FALSE PKCS#7 data signing failed.
130 IN CONST UINT8
*PrivateKey
,
131 IN UINTN PrivateKeySize
,
132 IN CONST UINT8
*KeyPassword
,
136 IN UINT8
*OtherCerts OPTIONAL
,
137 OUT UINT8
**SignedData
,
138 OUT UINTN
*SignedDataSize
151 // Check input parameters.
153 if (PrivateKey
== NULL
|| KeyPassword
== NULL
|| InData
== NULL
||
154 SignCert
== NULL
|| SignedData
== NULL
|| SignedDataSize
== NULL
|| InDataSize
> INT_MAX
) {
165 // Retrieve RSA private key from PEM data.
167 Status
= RsaGetPrivateKeyFromPem (
170 (CONST CHAR8
*) KeyPassword
,
171 (VOID
**) &RsaContext
178 // Register & Initialize necessary digest algorithms and PRNG for PKCS#7 Handling
180 EVP_add_digest (EVP_md5());
181 EVP_add_digest (EVP_sha1());
182 EVP_add_digest (EVP_sha256());
183 RandomSeed (NULL
, 0);
186 // Construct OpenSSL EVP_PKEY for private key.
188 Key
= EVP_PKEY_new ();
193 Key
->save_type
= EVP_PKEY_RSA
;
194 Key
->type
= EVP_PKEY_type (EVP_PKEY_RSA
);
195 Key
->pkey
.rsa
= (RSA
*) RsaContext
;
198 // Convert the data to be signed to BIO format.
200 DataBio
= BIO_new (BIO_s_mem ());
201 BIO_write (DataBio
, InData
, (int) InDataSize
);
204 // Create the PKCS#7 signedData structure.
209 (STACK_OF(X509
) *) OtherCerts
,
211 PKCS7_BINARY
| PKCS7_NOATTR
| PKCS7_DETACHED
219 // Convert PKCS#7 signedData structure into DER-encoded buffer.
221 P7DataSize
= i2d_PKCS7 (Pkcs7
, NULL
);
222 if (P7DataSize
<= 19) {
227 P7Data
= malloc (P7DataSize
);
228 if (P7Data
== NULL
) {
234 P7DataSize
= i2d_PKCS7 (Pkcs7
, (unsigned char **) &Tmp
);
237 // Strip ContentInfo to content only for signeddata. The data be trimmed off
238 // is totally 19 bytes.
240 *SignedDataSize
= P7DataSize
- 19;
241 *SignedData
= malloc (*SignedDataSize
);
242 if (*SignedData
== NULL
) {
244 OPENSSL_free (P7Data
);
248 CopyMem (*SignedData
, P7Data
+ 19, *SignedDataSize
);
250 OPENSSL_free (P7Data
);
258 if (RsaContext
!= NULL
) {
259 RsaFree (RsaContext
);
261 Key
->pkey
.rsa
= NULL
;
269 if (DataBio
!= NULL
) {
281 Check input P7Data is a wrapped ContentInfo structure or not. If not construct
282 a new structure to wrap P7Data.
284 @param[in] P7Data Pointer to the PKCS#7 message to verify.
285 @param[in] P7Length Length of the PKCS#7 message in bytes.
286 @param[out] WrapFlag If TRUE P7Data is a ContentInfo structure, otherwise
288 @param[out] WrapData If return status of this function is TRUE:
289 1) when WrapFlag is TRUE, pointer to P7Data.
290 2) when WrapFlag is FALSE, pointer to a new ContentInfo
291 structure. It's caller's responsibility to free this
293 @param[out] WrapDataSize Length of ContentInfo structure in bytes.
295 @retval TRUE The operation is finished successfully.
296 @retval FALSE The operation is failed due to lack of resources.
301 IN CONST UINT8
*P7Data
,
303 OUT BOOLEAN
*WrapFlag
,
304 OUT UINT8
**WrapData
,
305 OUT UINTN
*WrapDataSize
312 // Check whether input P7Data is a wrapped ContentInfo structure or not.
315 if ((P7Data
[4] == 0x06) && (P7Data
[5] == 0x09)) {
316 if (CompareMem (P7Data
+ 6, mOidValue
, sizeof (mOidValue
)) == 0) {
317 if ((P7Data
[15] == 0xA0) && (P7Data
[16] == 0x82)) {
324 *WrapData
= (UINT8
*) P7Data
;
325 *WrapDataSize
= P7Length
;
328 // Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes.
330 *WrapDataSize
= P7Length
+ 19;
331 *WrapData
= malloc (*WrapDataSize
);
332 if (*WrapData
== NULL
) {
337 SignedData
= *WrapData
;
340 // Part1: 0x30, 0x82.
342 SignedData
[0] = 0x30;
343 SignedData
[1] = 0x82;
346 // Part2: Length1 = P7Length + 19 - 4, in big endian.
348 SignedData
[2] = (UINT8
) (((UINT16
) (*WrapDataSize
- 4)) >> 8);
349 SignedData
[3] = (UINT8
) (((UINT16
) (*WrapDataSize
- 4)) & 0xff);
352 // Part3: 0x06, 0x09.
354 SignedData
[4] = 0x06;
355 SignedData
[5] = 0x09;
358 // Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02.
360 CopyMem (SignedData
+ 6, mOidValue
, sizeof (mOidValue
));
363 // Part5: 0xA0, 0x82.
365 SignedData
[15] = 0xA0;
366 SignedData
[16] = 0x82;
369 // Part6: Length2 = P7Length, in big endian.
371 SignedData
[17] = (UINT8
) (((UINT16
) P7Length
) >> 8);
372 SignedData
[18] = (UINT8
) (((UINT16
) P7Length
) & 0xff);
377 CopyMem (SignedData
+ 19, P7Data
, P7Length
);
385 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:
386 Cryptographic Message Syntax Standard". The input signed data could be wrapped
387 in a ContentInfo structure.
389 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then
390 return FALSE. If P7Length overflow, then return FAlSE.
392 @param[in] P7Data Pointer to the PKCS#7 message to verify.
393 @param[in] P7Length Length of the PKCS#7 message in bytes.
394 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.
395 It's caller's responsiblity to free the buffer.
396 @param[out] StackLength Length of signer's certificates in bytes.
397 @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.
398 It's caller's responsiblity to free the buffer.
399 @param[out] CertLength Length of the trusted certificate in bytes.
401 @retval TRUE The operation is finished successfully.
402 @retval FALSE Error occurs during the operation.
408 IN CONST UINT8
*P7Data
,
410 OUT UINT8
**CertStack
,
411 OUT UINTN
*StackLength
,
412 OUT UINT8
**TrustedCert
,
413 OUT UINTN
*CertLength
420 UINTN SignedDataSize
;
422 STACK_OF(X509
) *Stack
;
429 UINTN SingleCertSize
;
431 if ((P7Data
== NULL
) || (CertStack
== NULL
) || (StackLength
== NULL
) ||
432 (TrustedCert
== NULL
) || (CertLength
== NULL
) || (P7Length
> INT_MAX
)) {
436 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
449 // Retrieve PKCS#7 Data (DER encoding)
451 if (SignedDataSize
> INT_MAX
) {
456 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) SignedDataSize
);
462 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
464 if (!PKCS7_type_is_signed (Pkcs7
)) {
468 Stack
= PKCS7_get0_signers(Pkcs7
, NULL
, PKCS7_BINARY
);
474 // Convert CertStack to buffer in following format:
476 // UINT32 Cert1Length;
478 // UINT32 Cert2Length;
481 // UINT32 CertnLength;
484 BufferSize
= sizeof (UINT8
);
485 OldSize
= BufferSize
;
487 for (Index
= 0; ; Index
++) {
488 Status
= X509PopCertificate (Stack
, &SingleCert
, &SingleCertSize
);
493 OldSize
= BufferSize
;
495 BufferSize
= OldSize
+ SingleCertSize
+ sizeof (UINT32
);
496 CertBuf
= malloc (BufferSize
);
498 if (CertBuf
== NULL
) {
502 if (OldBuf
!= NULL
) {
503 CopyMem (CertBuf
, OldBuf
, OldSize
);
508 WriteUnaligned32 ((UINT32
*) (CertBuf
+ OldSize
), (UINT32
) SingleCertSize
);
509 CopyMem (CertBuf
+ OldSize
+ sizeof (UINT32
), SingleCert
, SingleCertSize
);
515 if (CertBuf
!= NULL
) {
517 // Update CertNumber.
521 *CertLength
= BufferSize
- OldSize
- sizeof (UINT32
);
522 *TrustedCert
= malloc (*CertLength
);
523 if (*TrustedCert
== NULL
) {
527 CopyMem (*TrustedCert
, CertBuf
+ OldSize
+ sizeof (UINT32
), *CertLength
);
528 *CertStack
= CertBuf
;
529 *StackLength
= BufferSize
;
546 sk_X509_pop_free(Stack
, X509_free
);
549 if (SingleCert
!= NULL
) {
553 if (!Status
&& (CertBuf
!= NULL
)) {
558 if (OldBuf
!= NULL
) {
566 Wrap function to use free() to free allocated memory for certificates.
568 @param[in] Certs Pointer to the certificates to be freed.
585 Verifies the validility of a PKCS#7 signed data as described in "PKCS #7:
586 Cryptographic Message Syntax Standard". The input signed data could be wrapped
587 in a ContentInfo structure.
589 If P7Data, TrustedCert or InData is NULL, then return FALSE.
590 If P7Length, CertLength or DataLength overflow, then return FAlSE.
592 @param[in] P7Data Pointer to the PKCS#7 message to verify.
593 @param[in] P7Length Length of the PKCS#7 message in bytes.
594 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which
595 is used for certificate chain verification.
596 @param[in] CertLength Length of the trusted certificate in bytes.
597 @param[in] InData Pointer to the content to be verified.
598 @param[in] DataLength Length of InData in bytes.
600 @retval TRUE The specified PKCS#7 signed data is valid.
601 @retval FALSE Invalid PKCS#7 signed data.
607 IN CONST UINT8
*P7Data
,
609 IN CONST UINT8
*TrustedCert
,
611 IN CONST UINT8
*InData
,
620 X509_STORE
*CertStore
;
623 UINTN SignedDataSize
;
627 // Check input parameters.
629 if (P7Data
== NULL
|| TrustedCert
== NULL
|| InData
== NULL
||
630 P7Length
> INT_MAX
|| CertLength
> INT_MAX
|| DataLength
> INT_MAX
) {
641 // Register & Initialize necessary digest algorithms for PKCS#7 Handling
643 EVP_add_digest (EVP_md5());
644 EVP_add_digest (EVP_sha1());
645 EVP_add_digest_alias (SN_sha1WithRSAEncryption
, SN_sha1WithRSA
);
646 EVP_add_digest (EVP_sha256());
648 Status
= WrapPkcs7Data (P7Data
, P7Length
, &Wrapped
, &SignedData
, &SignedDataSize
);
654 // Retrieve PKCS#7 Data (DER encoding)
656 if (SignedDataSize
> INT_MAX
) {
661 Pkcs7
= d2i_PKCS7 (NULL
, (const unsigned char **) &Temp
, (int) SignedDataSize
);
667 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
669 if (!PKCS7_type_is_signed (Pkcs7
)) {
674 // Read DER-encoded root certificate and Construct X509 Certificate
676 CertBio
= BIO_new (BIO_s_mem ());
677 BIO_write (CertBio
, TrustedCert
, (int)CertLength
);
678 if (CertBio
== NULL
) {
681 Cert
= d2i_X509_bio (CertBio
, NULL
);
687 // Setup X509 Store for trusted certificate
689 CertStore
= X509_STORE_new ();
690 if (CertStore
== NULL
) {
693 if (!(X509_STORE_add_cert (CertStore
, Cert
))) {
698 // Register customized X509 verification callback function to support
699 // trusted intermediate certificate anchor.
701 CertStore
->verify_cb
= X509VerifyCb
;
704 // For generic PKCS#7 handling, InData may be NULL if the content is present
705 // in PKCS#7 structure. So ignore NULL checking here.
707 DataBio
= BIO_new (BIO_s_mem ());
708 BIO_write (DataBio
, InData
, (int)DataLength
);
711 // Verifies the PKCS#7 signedData structure
713 Status
= (BOOLEAN
) PKCS7_verify (Pkcs7
, NULL
, CertStore
, DataBio
, NULL
, PKCS7_BINARY
);
722 X509_STORE_free (CertStore
);
726 OPENSSL_free (SignedData
);