123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724 |
- /** @file
- X.509 Certificate Handler Wrapper Implementation over OpenSSL.
- Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #include "InternalCryptLib.h"
- #include <openssl/x509.h>
- #include <openssl/rsa.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;
- UINTN X509NameSize;
- //
- // 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;
- }
- X509NameSize = i2d_X509_NAME(X509Name, NULL);
- if (*SubjectSize < X509NameSize) {
- *SubjectSize = X509NameSize;
- goto _Exit;
- }
- *SubjectSize = X509NameSize;
- if (CertSubject != NULL) {
- i2d_X509_NAME(X509Name, &CertSubject);
- Status = TRUE;
- }
- _Exit:
- //
- // Release Resources.
- //
- if (X509Cert != NULL) {
- X509_free (X509Cert);
- }
- return Status;
- }
- /**
- Retrieve the common name (CN) string 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] CommonName Buffer to contain the retrieved certificate common
- name string (UTF8). At most CommonNameSize bytes will be
- written and the string will be null terminated. May be
- NULL in order to determine the size buffer needed.
- @param[in,out] CommonNameSize The size in bytes of the CommonName buffer on input,
- and the size of buffer returned CommonName on output.
- If CommonName is NULL then the amount of space needed
- in buffer (including the final null) is returned.
- @retval RETURN_SUCCESS The certificate CommonName retrieved successfully.
- @retval RETURN_INVALID_PARAMETER If Cert is NULL.
- If CommonNameSize is NULL.
- If CommonName is not NULL and *CommonNameSize is 0.
- If Certificate is invalid.
- @retval RETURN_NOT_FOUND If no CommonName entry exists.
- @retval RETURN_BUFFER_TOO_SMALL If the CommonName is NULL. The required buffer size
- (including the final null) is returned in the
- CommonNameSize parameter.
- @retval RETURN_UNSUPPORTED The operation is not supported.
- **/
- RETURN_STATUS
- EFIAPI
- X509GetCommonName (
- IN CONST UINT8 *Cert,
- IN UINTN CertSize,
- OUT CHAR8 *CommonName, OPTIONAL
- IN OUT UINTN *CommonNameSize
- )
- {
- RETURN_STATUS ReturnStatus;
- BOOLEAN Status;
- X509 *X509Cert;
- X509_NAME *X509Name;
- INT32 Index;
- INTN Length;
- X509_NAME_ENTRY *Entry;
- ASN1_STRING *EntryData;
- UINT8 *UTF8Name;
- ReturnStatus = RETURN_INVALID_PARAMETER;
- UTF8Name = NULL;
- //
- // Check input parameters.
- //
- if ((Cert == NULL) || (CertSize > INT_MAX) || (CommonNameSize == NULL)) {
- return ReturnStatus;
- }
- if ((CommonName != NULL) && (*CommonNameSize == 0)) {
- return ReturnStatus;
- }
- X509Cert = NULL;
- //
- // Read DER-encoded X509 Certificate and Construct X509 object.
- //
- Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);
- if ((X509Cert == NULL) || (!Status)) {
- //
- // Invalid X.509 Certificate
- //
- goto _Exit;
- }
- Status = FALSE;
- //
- // Retrieve subject name from certificate object.
- //
- X509Name = X509_get_subject_name (X509Cert);
- if (X509Name == NULL) {
- //
- // Fail to retrieve subject name content
- //
- goto _Exit;
- }
- //
- // Retrieve the CommonName information from X.509 Subject
- //
- Index = X509_NAME_get_index_by_NID (X509Name, NID_commonName, -1);
- if (Index < 0) {
- //
- // No CommonName entry exists in X509_NAME object
- //
- *CommonNameSize = 0;
- ReturnStatus = RETURN_NOT_FOUND;
- goto _Exit;
- }
- Entry = X509_NAME_get_entry (X509Name, Index);
- if (Entry == NULL) {
- //
- // Fail to retrieve name entry data
- //
- *CommonNameSize = 0;
- ReturnStatus = RETURN_NOT_FOUND;
- goto _Exit;
- }
- EntryData = X509_NAME_ENTRY_get_data (Entry);
- Length = ASN1_STRING_to_UTF8 (&UTF8Name, EntryData);
- if (Length < 0) {
- //
- // Fail to convert the commonName string
- //
- *CommonNameSize = 0;
- ReturnStatus = RETURN_INVALID_PARAMETER;
- goto _Exit;
- }
- if (CommonName == NULL) {
- *CommonNameSize = Length + 1;
- ReturnStatus = RETURN_BUFFER_TOO_SMALL;
- } else {
- *CommonNameSize = MIN ((UINTN)Length, *CommonNameSize - 1) + 1;
- CopyMem (CommonName, UTF8Name, *CommonNameSize - 1);
- CommonName[*CommonNameSize - 1] = '\0';
- ReturnStatus = RETURN_SUCCESS;
- }
- _Exit:
- //
- // Release Resources.
- //
- if (X509Cert != NULL) {
- X509_free (X509Cert);
- }
- if (UTF8Name != NULL) {
- OPENSSL_free (UTF8Name);
- }
- return ReturnStatus;
- }
- /**
- 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) || (EVP_PKEY_id (Pkey) != EVP_PKEY_RSA)) {
- goto _Exit;
- }
- //
- // Duplicate RSA Context from the retrieved EVP_PKEY.
- //
- if ((*RsaContext = RSAPublicKey_dup (EVP_PKEY_get0_RSA (Pkey))) != 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;
- CertCtx = 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;
- }
- //
- // Allow partial certificate chains, terminated by a non-self-signed but
- // still trusted intermediate certificate. Also disable time checks.
- //
- X509_STORE_set_flags (CertStore,
- X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_NO_CHECK_TIME);
- //
- // Set up X509_STORE_CTX for the subsequent verification operation.
- //
- CertCtx = X509_STORE_CTX_new ();
- if (CertCtx == NULL) {
- goto _Exit;
- }
- 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);
- }
- X509_STORE_CTX_free (CertCtx);
- 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;
- UINT32 Asn1Tag;
- UINT32 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;
- Length = 0;
- 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;
- }
|