/** @file | |
X.509 Certificate Handler Wrapper Implementation over OpenSSL. | |
Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include "InternalCryptLib.h" | |
#include <openssl/x509.h> | |
/** | |
Construct a X509 object from DER-encoded certificate data. | |
If Cert is NULL, then return FALSE. | |
If SingleX509Cert is NULL, then return FALSE. | |
@param[in] Cert Pointer to the DER-encoded certificate data. | |
@param[in] CertSize The size of certificate data in bytes. | |
@param[out] SingleX509Cert The generated X509 object. | |
@retval TRUE The X509 object generation succeeded. | |
@retval FALSE The operation failed. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509ConstructCertificate ( | |
IN CONST UINT8 *Cert, | |
IN UINTN CertSize, | |
OUT UINT8 **SingleX509Cert | |
) | |
{ | |
X509 *X509Cert; | |
CONST UINT8 *Temp; | |
// | |
// Check input parameters. | |
// | |
if (Cert == NULL || SingleX509Cert == NULL || CertSize > INT_MAX) { | |
return FALSE; | |
} | |
// | |
// Read DER-encoded X509 Certificate and Construct X509 object. | |
// | |
Temp = Cert; | |
X509Cert = d2i_X509 (NULL, &Temp, (long) CertSize); | |
if (X509Cert == NULL) { | |
return FALSE; | |
} | |
*SingleX509Cert = (UINT8 *) X509Cert; | |
return TRUE; | |
} | |
/** | |
Construct a X509 stack object from a list of DER-encoded certificate data. | |
If X509Stack is NULL, then return FALSE. | |
@param[in, out] X509Stack On input, pointer to an existing or NULL X509 stack object. | |
On output, pointer to the X509 stack object with new | |
inserted X509 certificate. | |
@param ... A list of DER-encoded single certificate data followed | |
by certificate size. A NULL terminates the list. The | |
pairs are the arguments to X509ConstructCertificate(). | |
@retval TRUE The X509 stack construction succeeded. | |
@retval FALSE The construction operation failed. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509ConstructCertificateStack ( | |
IN OUT UINT8 **X509Stack, | |
... | |
) | |
{ | |
UINT8 *Cert; | |
UINTN CertSize; | |
X509 *X509Cert; | |
STACK_OF(X509) *CertStack; | |
BOOLEAN Status; | |
VA_LIST Args; | |
UINTN Index; | |
// | |
// Check input parameters. | |
// | |
if (X509Stack == NULL) { | |
return FALSE; | |
} | |
Status = FALSE; | |
// | |
// Initialize X509 stack object. | |
// | |
CertStack = (STACK_OF(X509) *) (*X509Stack); | |
if (CertStack == NULL) { | |
CertStack = sk_X509_new_null (); | |
if (CertStack == NULL) { | |
return Status; | |
} | |
} | |
VA_START (Args, X509Stack); | |
for (Index = 0; ; Index++) { | |
// | |
// If Cert is NULL, then it is the end of the list. | |
// | |
Cert = VA_ARG (Args, UINT8 *); | |
if (Cert == NULL) { | |
break; | |
} | |
CertSize = VA_ARG (Args, UINTN); | |
if (CertSize == 0) { | |
break; | |
} | |
// | |
// Construct X509 Object from the given DER-encoded certificate data. | |
// | |
X509Cert = NULL; | |
Status = X509ConstructCertificate ( | |
(CONST UINT8 *) Cert, | |
CertSize, | |
(UINT8 **) &X509Cert | |
); | |
if (!Status) { | |
if (X509Cert != NULL) { | |
X509_free (X509Cert); | |
} | |
break; | |
} | |
// | |
// Insert the new X509 object into X509 stack object. | |
// | |
sk_X509_push (CertStack, X509Cert); | |
} | |
VA_END (Args); | |
if (!Status) { | |
sk_X509_pop_free (CertStack, X509_free); | |
} else { | |
*X509Stack = (UINT8 *) CertStack; | |
} | |
return Status; | |
} | |
/** | |
Release the specified X509 object. | |
If X509Cert is NULL, then return FALSE. | |
@param[in] X509Cert Pointer to the X509 object to be released. | |
**/ | |
VOID | |
EFIAPI | |
X509Free ( | |
IN VOID *X509Cert | |
) | |
{ | |
// | |
// Check input parameters. | |
// | |
if (X509Cert == NULL) { | |
return; | |
} | |
// | |
// Free OpenSSL X509 object. | |
// | |
X509_free ((X509 *) X509Cert); | |
} | |
/** | |
Release the specified X509 stack object. | |
If X509Stack is NULL, then return FALSE. | |
@param[in] X509Stack Pointer to the X509 stack object to be released. | |
**/ | |
VOID | |
EFIAPI | |
X509StackFree ( | |
IN VOID *X509Stack | |
) | |
{ | |
// | |
// Check input parameters. | |
// | |
if (X509Stack == NULL) { | |
return; | |
} | |
// | |
// Free OpenSSL X509 stack object. | |
// | |
sk_X509_pop_free ((STACK_OF(X509) *) X509Stack, X509_free); | |
} | |
/** | |
Retrieve the subject bytes from one X.509 certificate. | |
@param[in] Cert Pointer to the DER-encoded X509 certificate. | |
@param[in] CertSize Size of the X509 certificate in bytes. | |
@param[out] CertSubject Pointer to the retrieved certificate subject bytes. | |
@param[in, out] SubjectSize The size in bytes of the CertSubject buffer on input, | |
and the size of buffer returned CertSubject on output. | |
If Cert is NULL, then return FALSE. | |
If SubjectSize is NULL, then return FALSE. | |
@retval TRUE The certificate subject retrieved successfully. | |
@retval FALSE Invalid certificate, or the SubjectSize is too small for the result. | |
The SubjectSize will be updated with the required size. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509GetSubjectName ( | |
IN CONST UINT8 *Cert, | |
IN UINTN CertSize, | |
OUT UINT8 *CertSubject, | |
IN OUT UINTN *SubjectSize | |
) | |
{ | |
BOOLEAN Status; | |
X509 *X509Cert; | |
X509_NAME *X509Name; | |
// | |
// Check input parameters. | |
// | |
if (Cert == NULL || SubjectSize == NULL) { | |
return FALSE; | |
} | |
X509Cert = NULL; | |
// | |
// Read DER-encoded X509 Certificate and Construct X509 object. | |
// | |
Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert); | |
if ((X509Cert == NULL) || (!Status)) { | |
Status = FALSE; | |
goto _Exit; | |
} | |
Status = FALSE; | |
// | |
// Retrieve subject name from certificate object. | |
// | |
X509Name = X509_get_subject_name (X509Cert); | |
if (X509Name == NULL) { | |
goto _Exit; | |
} | |
if (*SubjectSize < (UINTN) X509Name->bytes->length) { | |
*SubjectSize = (UINTN) X509Name->bytes->length; | |
goto _Exit; | |
} | |
*SubjectSize = (UINTN) X509Name->bytes->length; | |
if (CertSubject != NULL) { | |
CopyMem (CertSubject, (UINT8 *) X509Name->bytes->data, *SubjectSize); | |
Status = TRUE; | |
} | |
_Exit: | |
// | |
// Release Resources. | |
// | |
if (X509Cert != NULL) { | |
X509_free (X509Cert); | |
} | |
return Status; | |
} | |
/** | |
Retrieve the RSA Public Key from one DER-encoded X509 certificate. | |
@param[in] Cert Pointer to the DER-encoded X509 certificate. | |
@param[in] CertSize Size of the X509 certificate in bytes. | |
@param[out] RsaContext Pointer to new-generated RSA context which contain the retrieved | |
RSA public key component. Use RsaFree() function to free the | |
resource. | |
If Cert is NULL, then return FALSE. | |
If RsaContext is NULL, then return FALSE. | |
@retval TRUE RSA Public Key was retrieved successfully. | |
@retval FALSE Fail to retrieve RSA public key from X509 certificate. | |
**/ | |
BOOLEAN | |
EFIAPI | |
RsaGetPublicKeyFromX509 ( | |
IN CONST UINT8 *Cert, | |
IN UINTN CertSize, | |
OUT VOID **RsaContext | |
) | |
{ | |
BOOLEAN Status; | |
EVP_PKEY *Pkey; | |
X509 *X509Cert; | |
// | |
// Check input parameters. | |
// | |
if (Cert == NULL || RsaContext == NULL) { | |
return FALSE; | |
} | |
Pkey = NULL; | |
X509Cert = NULL; | |
// | |
// Read DER-encoded X509 Certificate and Construct X509 object. | |
// | |
Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert); | |
if ((X509Cert == NULL) || (!Status)) { | |
Status = FALSE; | |
goto _Exit; | |
} | |
Status = FALSE; | |
// | |
// Retrieve and check EVP_PKEY data from X509 Certificate. | |
// | |
Pkey = X509_get_pubkey (X509Cert); | |
if ((Pkey == NULL) || (Pkey->type != EVP_PKEY_RSA)) { | |
goto _Exit; | |
} | |
// | |
// Duplicate RSA Context from the retrieved EVP_PKEY. | |
// | |
if ((*RsaContext = RSAPublicKey_dup (Pkey->pkey.rsa)) != NULL) { | |
Status = TRUE; | |
} | |
_Exit: | |
// | |
// Release Resources. | |
// | |
if (X509Cert != NULL) { | |
X509_free (X509Cert); | |
} | |
if (Pkey != NULL) { | |
EVP_PKEY_free (Pkey); | |
} | |
return Status; | |
} | |
/** | |
Verify one X509 certificate was issued by the trusted CA. | |
@param[in] Cert Pointer to the DER-encoded X509 certificate to be verified. | |
@param[in] CertSize Size of the X509 certificate in bytes. | |
@param[in] CACert Pointer to the DER-encoded trusted CA certificate. | |
@param[in] CACertSize Size of the CA Certificate in bytes. | |
If Cert is NULL, then return FALSE. | |
If CACert is NULL, then return FALSE. | |
@retval TRUE The certificate was issued by the trusted CA. | |
@retval FALSE Invalid certificate or the certificate was not issued by the given | |
trusted CA. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509VerifyCert ( | |
IN CONST UINT8 *Cert, | |
IN UINTN CertSize, | |
IN CONST UINT8 *CACert, | |
IN UINTN CACertSize | |
) | |
{ | |
BOOLEAN Status; | |
X509 *X509Cert; | |
X509 *X509CACert; | |
X509_STORE *CertStore; | |
X509_STORE_CTX CertCtx; | |
// | |
// Check input parameters. | |
// | |
if (Cert == NULL || CACert == NULL) { | |
return FALSE; | |
} | |
Status = FALSE; | |
X509Cert = NULL; | |
X509CACert = NULL; | |
CertStore = NULL; | |
// | |
// Register & Initialize necessary digest algorithms for certificate verification. | |
// | |
if (EVP_add_digest (EVP_md5 ()) == 0) { | |
goto _Exit; | |
} | |
if (EVP_add_digest (EVP_sha1 ()) == 0) { | |
goto _Exit; | |
} | |
if (EVP_add_digest (EVP_sha256 ()) == 0) { | |
goto _Exit; | |
} | |
// | |
// Read DER-encoded certificate to be verified and Construct X509 object. | |
// | |
Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert); | |
if ((X509Cert == NULL) || (!Status)) { | |
Status = FALSE; | |
goto _Exit; | |
} | |
// | |
// Read DER-encoded root certificate and Construct X509 object. | |
// | |
Status = X509ConstructCertificate (CACert, CACertSize, (UINT8 **) &X509CACert); | |
if ((X509CACert == NULL) || (!Status)) { | |
Status = FALSE; | |
goto _Exit; | |
} | |
Status = FALSE; | |
// | |
// Set up X509 Store for trusted certificate. | |
// | |
CertStore = X509_STORE_new (); | |
if (CertStore == NULL) { | |
goto _Exit; | |
} | |
if (!(X509_STORE_add_cert (CertStore, X509CACert))) { | |
goto _Exit; | |
} | |
// | |
// Set up X509_STORE_CTX for the subsequent verification operation. | |
// | |
if (!X509_STORE_CTX_init (&CertCtx, CertStore, X509Cert, NULL)) { | |
goto _Exit; | |
} | |
// | |
// X509 Certificate Verification. | |
// | |
Status = (BOOLEAN) X509_verify_cert (&CertCtx); | |
X509_STORE_CTX_cleanup (&CertCtx); | |
_Exit: | |
// | |
// Release Resources. | |
// | |
if (X509Cert != NULL) { | |
X509_free (X509Cert); | |
} | |
if (X509CACert != NULL) { | |
X509_free (X509CACert); | |
} | |
if (CertStore != NULL) { | |
X509_STORE_free (CertStore); | |
} | |
return Status; | |
} | |
/** | |
Retrieve the TBSCertificate from one given X.509 certificate. | |
@param[in] Cert Pointer to the given DER-encoded X509 certificate. | |
@param[in] CertSize Size of the X509 certificate in bytes. | |
@param[out] TBSCert DER-Encoded To-Be-Signed certificate. | |
@param[out] TBSCertSize Size of the TBS certificate in bytes. | |
If Cert is NULL, then return FALSE. | |
If TBSCert is NULL, then return FALSE. | |
If TBSCertSize is NULL, then return FALSE. | |
@retval TRUE The TBSCertificate was retrieved successfully. | |
@retval FALSE Invalid X.509 certificate. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509GetTBSCert ( | |
IN CONST UINT8 *Cert, | |
IN UINTN CertSize, | |
OUT UINT8 **TBSCert, | |
OUT UINTN *TBSCertSize | |
) | |
{ | |
CONST UINT8 *Temp; | |
INTN Asn1Tag; | |
INTN ObjClass; | |
UINTN Length; | |
// | |
// Check input parameters. | |
// | |
if ((Cert == NULL) || (TBSCert == NULL) || | |
(TBSCertSize == NULL) || (CertSize > INT_MAX)) { | |
return FALSE; | |
} | |
// | |
// An X.509 Certificate is: (defined in RFC3280) | |
// Certificate ::= SEQUENCE { | |
// tbsCertificate TBSCertificate, | |
// signatureAlgorithm AlgorithmIdentifier, | |
// signature BIT STRING } | |
// | |
// and | |
// | |
// TBSCertificate ::= SEQUENCE { | |
// version [0] Version DEFAULT v1, | |
// ... | |
// } | |
// | |
// So we can just ASN1-parse the x.509 DER-encoded data. If we strip | |
// the first SEQUENCE, the second SEQUENCE is the TBSCertificate. | |
// | |
Temp = Cert; | |
ASN1_get_object (&Temp, (long *)&Length, (int *)&Asn1Tag, (int *)&ObjClass, (long)CertSize); | |
if (Asn1Tag != V_ASN1_SEQUENCE) { | |
return FALSE; | |
} | |
*TBSCert = (UINT8 *)Temp; | |
ASN1_get_object (&Temp, (long *)&Length, (int *)&Asn1Tag, (int *)&ObjClass, (long)Length); | |
// | |
// Verify the parsed TBSCertificate is one correct SEQUENCE data. | |
// | |
if (Asn1Tag != V_ASN1_SEQUENCE) { | |
return FALSE; | |
} | |
*TBSCertSize = Length + (Temp - *TBSCert); | |
return TRUE; | |
} |