2 PKCS#7 SignedData Verification Wrapper Implementation over OpenSSL.
4 Copyright (c) 2009 - 2011, 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>
23 Verification callback function to override any existing callbacks in OpenSSL
24 for intermediate certificate supports.
26 @param[in] Status Original status before calling this callback.
27 @param[in] Context X509 store context.
29 @retval 1 Current X509 certificate is verified successfully.
30 @retval 0 Verification failed.
33 STATIC
int X509VerifyCb (int Status
, X509_STORE_CTX
*Context
)
41 Error
= X509_STORE_CTX_get_error (Context
);
44 // X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT and X509_V_ERR_UNABLE_TO_GET_ISSUER_
45 // CERT_LOCALLY mean a X509 certificate is not self signed and its issuer
46 // can not be found in X509_verify_cert of X509_vfy.c.
47 // In order to support intermediate certificate node, we override the
48 // errors if the certification is obtained from X509 store, i.e. it is
49 // a trusted ceritifcate node that is enrolled by user.
50 // Besides,X509_V_ERR_CERT_UNTRUSTED and X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
51 // are also ignored to enable such feature.
53 if ((Error
== X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT
) ||
54 (Error
== X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
)) {
55 Obj
= (X509_OBJECT
*) OPENSSL_malloc (sizeof (X509_OBJECT
));
60 Obj
->type
= X509_LU_X509
;
61 Obj
->data
.x509
= Context
->current_cert
;
63 CRYPTO_w_lock (CRYPTO_LOCK_X509_STORE
);
65 if (X509_OBJECT_retrieve_match (Context
->ctx
->objs
, Obj
)) {
69 // If any certificate in the chain is enrolled as trusted certificate,
70 // pass the certificate verification.
72 if (Error
== X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
) {
73 Count
= sk_X509_num (Context
->chain
);
74 for (Index
= 0; Index
< Count
; Index
++) {
75 Obj
->data
.x509
= sk_X509_value (Context
->chain
, Index
);
76 if (X509_OBJECT_retrieve_match (Context
->ctx
->objs
, Obj
)) {
84 CRYPTO_w_unlock (CRYPTO_LOCK_X509_STORE
);
87 if ((Error
== X509_V_ERR_CERT_UNTRUSTED
) ||
88 (Error
== X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
)) {
100 Creates a PKCS#7 signedData as described in "PKCS #7: Cryptographic Message
101 Syntax Standard, version 1.5". This interface is only intended to be used for
102 application to perform PKCS#7 functionality validation.
104 @param[in] PrivateKey Pointer to the PEM-formatted private key data for
106 @param[in] PrivateKeySize Size of the PEM private key data in bytes.
107 @param[in] KeyPassword NULL-terminated passphrase used for encrypted PEM
109 @param[in] InData Pointer to the content to be signed.
110 @param[in] InDataSize Size of InData in bytes.
111 @param[in] SignCert Pointer to signer's DER-encoded certificate to sign with.
112 @param[in] OtherCerts Pointer to an optional additional set of certificates to
113 include in the PKCS#7 signedData (e.g. any intermediate
115 @param[out] SignedData Pointer to output PKCS#7 signedData.
116 @param[out] SignedDataSize Size of SignedData in bytes.
118 @retval TRUE PKCS#7 data signing succeeded.
119 @retval FALSE PKCS#7 data signing failed.
125 IN CONST UINT8
*PrivateKey
,
126 IN UINTN PrivateKeySize
,
127 IN CONST UINT8
*KeyPassword
,
131 IN UINT8
*OtherCerts OPTIONAL
,
132 OUT UINT8
**SignedData
,
133 OUT UINTN
*SignedDataSize
144 // Check input parameters.
146 if ((PrivateKey
== NULL
) || (KeyPassword
== NULL
) || (InData
== NULL
)) {
150 if ((SignCert
== NULL
) || (SignedData
== NULL
) || (SignedDataSize
== NULL
)) {
161 // Retrieve RSA private key from PEM data.
163 Status
= RsaGetPrivateKeyFromPem (
166 (CONST CHAR8
*) KeyPassword
,
167 (VOID
**) &RsaContext
174 // Register & Initialize necessary digest algorithms and PRNG for PKCS#7 Handling
176 EVP_add_digest (EVP_md5());
177 EVP_add_digest (EVP_sha1());
178 EVP_add_digest (EVP_sha256());
179 RandomSeed (NULL
, 0);
182 // Construct OpenSSL EVP_PKEY for private key.
184 Key
= EVP_PKEY_new ();
188 Key
->save_type
= EVP_PKEY_RSA
;
189 Key
->type
= EVP_PKEY_type (EVP_PKEY_RSA
);
190 Key
->pkey
.rsa
= (RSA
*) RsaContext
;
193 // Convert the data to be signed to BIO format.
195 DataBio
= BIO_new (BIO_s_mem ());
196 BIO_write (DataBio
, InData
, (int) InDataSize
);
199 // Create the PKCS#7 signedData structure.
204 (STACK_OF(X509
) *) OtherCerts
,
213 // Convert PKCS#7 signedData structure into DER-encoded buffer.
215 *SignedDataSize
= i2d_PKCS7 (Pkcs7
, NULL
);
216 if (*SignedDataSize
== 0) {
219 *SignedData
= OPENSSL_malloc (*SignedDataSize
);
220 P7Data
= *SignedData
;
221 *SignedDataSize
= i2d_PKCS7 (Pkcs7
, (unsigned char **) &P7Data
);
229 if (RsaContext
!= NULL
) {
230 RsaFree (RsaContext
);
232 Key
->pkey
.rsa
= NULL
;
240 if (DataBio
!= NULL
) {
252 Verifies the validility of a PKCS#7 signed data as described in "PKCS #7: Cryptographic
253 Message Syntax Standard".
255 If P7Data is NULL, then ASSERT().
257 @param[in] P7Data Pointer to the PKCS#7 message to verify.
258 @param[in] P7Length Length of the PKCS#7 message in bytes.
259 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which
260 is used for certificate chain verification.
261 @param[in] CertLength Length of the trusted certificate in bytes.
262 @param[in] InData Pointer to the content to be verified.
263 @param[in] DataLength Length of InData in bytes.
265 @retval TRUE The specified PKCS#7 signed data is valid.
266 @retval FALSE Invalid PKCS#7 signed data.
272 IN CONST UINT8
*P7Data
,
274 IN CONST UINT8
*TrustedCert
,
276 IN CONST UINT8
*InData
,
285 X509_STORE
*CertStore
;
288 // ASSERT if P7Data is NULL
290 ASSERT (P7Data
!= NULL
);
300 // Register & Initialize necessary digest algorithms for PKCS#7 Handling
302 EVP_add_digest (EVP_md5());
303 EVP_add_digest (EVP_sha1());
304 EVP_add_digest_alias (SN_sha1WithRSAEncryption
, SN_sha1WithRSA
);
305 EVP_add_digest (EVP_sha256());
308 // Retrieve PKCS#7 Data (DER encoding)
310 Pkcs7
= d2i_PKCS7 (NULL
, &P7Data
, (int)P7Length
);
316 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
318 if (!PKCS7_type_is_signed (Pkcs7
)) {
323 // Read DER-encoded root certificate and Construct X509 Certificate
325 CertBio
= BIO_new (BIO_s_mem ());
326 BIO_write (CertBio
, TrustedCert
, (int)CertLength
);
327 if (CertBio
== NULL
) {
330 Cert
= d2i_X509_bio (CertBio
, NULL
);
336 // Setup X509 Store for trusted certificate
338 CertStore
= X509_STORE_new ();
339 if (CertStore
== NULL
) {
342 if (!(X509_STORE_add_cert (CertStore
, Cert
))) {
347 // Register customized X509 verification callback function to support
348 // trusted intermediate certificate anchor.
350 CertStore
->verify_cb
= X509VerifyCb
;
353 // For generic PKCS#7 handling, InData may be NULL if the content is present
354 // in PKCS#7 structure. So ignore NULL checking here.
356 DataBio
= BIO_new (BIO_s_mem ());
357 BIO_write (DataBio
, InData
, (int)DataLength
);
360 // Verifies the PKCS#7 signedData structure
362 Status
= (BOOLEAN
) PKCS7_verify (Pkcs7
, NULL
, CertStore
, DataBio
, NULL
, PKCS7_BINARY
);
371 X509_STORE_free (CertStore
);