CryptX509.c revision 12d95665cb0e088afe2cd395f0acc7fdb2604acc
1/** @file
2  X.509 Certificate Handler Wrapper Implementation over OpenSSL.
3
4Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials
6are licensed and made available under the terms and conditions of the BSD License
7which accompanies this distribution.  The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13**/
14
15#include "InternalCryptLib.h"
16#include <openssl/x509.h>
17
18
19/**
20  Construct a X509 object from DER-encoded certificate data.
21
22  If Cert is NULL, then return FALSE.
23  If SingleX509Cert is NULL, then return FALSE.
24
25  @param[in]  Cert            Pointer to the DER-encoded certificate data.
26  @param[in]  CertSize        The size of certificate data in bytes.
27  @param[out] SingleX509Cert  The generated X509 object.
28
29  @retval     TRUE            The X509 object generation succeeded.
30  @retval     FALSE           The operation failed.
31
32**/
33BOOLEAN
34EFIAPI
35X509ConstructCertificate (
36  IN   CONST UINT8  *Cert,
37  IN   UINTN        CertSize,
38  OUT  UINT8        **SingleX509Cert
39  )
40{
41  X509     *X509Cert;
42
43  //
44  // Check input parameters.
45  //
46  if (Cert == NULL || SingleX509Cert == NULL || CertSize > INT_MAX) {
47    return FALSE;
48  }
49
50  //
51  // Read DER-encoded X509 Certificate and Construct X509 object.
52  //
53  X509Cert = d2i_X509 (NULL, &Cert, (long) CertSize);
54  if (X509Cert == NULL) {
55    return FALSE;
56  }
57
58  *SingleX509Cert = (UINT8 *) X509Cert;
59
60  return TRUE;
61}
62
63/**
64  Construct a X509 stack object from a list of DER-encoded certificate data.
65
66  If X509Stack is NULL, then return FALSE.
67
68  @param[in, out]  X509Stack  On input, pointer to an existing X509 stack object.
69                              On output, pointer to the X509 stack object with new
70                              inserted X509 certificate.
71  @param           ...        A list of DER-encoded single certificate data followed
72                              by certificate size. A NULL terminates the list. The
73                              pairs are the arguments to X509ConstructCertificate().
74
75  @retval     TRUE            The X509 stack construction succeeded.
76  @retval     FALSE           The construction operation failed.
77
78**/
79BOOLEAN
80EFIAPI
81X509ConstructCertificateStack (
82  IN OUT  UINT8  **X509Stack,
83  ...
84  )
85{
86  UINT8           *Cert;
87  UINTN           CertSize;
88  X509            *X509Cert;
89  STACK_OF(X509)  *CertStack;
90  BOOLEAN         Status;
91  VA_LIST         Args;
92  UINTN           Index;
93
94  //
95  // Check input parameters.
96  //
97  if (X509Stack == NULL) {
98    return FALSE;
99  }
100
101  Status = FALSE;
102
103  //
104  // Initialize X509 stack object.
105  //
106  CertStack = (STACK_OF(X509) *) (*X509Stack);
107  if (CertStack == NULL) {
108    CertStack = sk_X509_new_null ();
109    if (CertStack == NULL) {
110      return Status;
111    }
112  }
113
114  VA_START (Args, X509Stack);
115
116  for (Index = 0; ; Index++) {
117    //
118    // If Cert is NULL, then it is the end of the list.
119    //
120    Cert = VA_ARG (Args, UINT8 *);
121    if (Cert == NULL) {
122      break;
123    }
124
125    CertSize = VA_ARG (Args, UINTN);
126
127    //
128    // Construct X509 Object from the given DER-encoded certificate data.
129    //
130    Status = X509ConstructCertificate (
131               (CONST UINT8 *) Cert,
132               CertSize,
133               (UINT8 **) &X509Cert
134               );
135    if (!Status) {
136      X509_free (X509Cert);
137      break;
138    }
139
140    //
141    // Insert the new X509 object into X509 stack object.
142    //
143    sk_X509_push (CertStack, X509Cert);
144  }
145
146  VA_END (Args);
147
148  if (!Status) {
149    sk_X509_pop_free (CertStack, X509_free);
150  } else {
151    *X509Stack = (UINT8 *) CertStack;
152  }
153
154  return Status;
155}
156
157/**
158  Release the specified X509 object.
159
160  If X509Cert is NULL, then return FALSE.
161
162  @param[in]  X509Cert  Pointer to the X509 object to be released.
163
164**/
165VOID
166EFIAPI
167X509Free (
168  IN  VOID  *X509Cert
169  )
170{
171  //
172  // Check input parameters.
173  //
174  if (X509Cert == NULL) {
175    return;
176  }
177
178  //
179  // Free OpenSSL X509 object.
180  //
181  X509_free ((X509 *) X509Cert);
182}
183
184/**
185  Release the specified X509 stack object.
186
187  If X509Stack is NULL, then return FALSE.
188
189  @param[in]  X509Stack  Pointer to the X509 stack object to be released.
190
191**/
192VOID
193EFIAPI
194X509StackFree (
195  IN  VOID  *X509Stack
196  )
197{
198  //
199  // Check input parameters.
200  //
201  if (X509Stack == NULL) {
202    return;
203  }
204
205  //
206  // Free OpenSSL X509 stack object.
207  //
208  sk_X509_pop_free ((STACK_OF(X509) *) X509Stack, X509_free);
209}
210
211/**
212  Retrieve the subject bytes from one X.509 certificate.
213
214  @param[in]      Cert         Pointer to the DER-encoded X509 certificate.
215  @param[in]      CertSize     Size of the X509 certificate in bytes.
216  @param[out]     CertSubject  Pointer to the retrieved certificate subject bytes.
217  @param[in, out] SubjectSize  The size in bytes of the CertSubject buffer on input,
218                               and the size of buffer returned CertSubject on output.
219
220  If Cert is NULL, then return FALSE.
221  If SubjectSize is NULL, then return FALSE.
222
223  @retval  TRUE   The certificate subject retrieved successfully.
224  @retval  FALSE  Invalid certificate, or the SubjectSize is too small for the result.
225                  The SubjectSize will be updated with the required size.
226
227**/
228BOOLEAN
229EFIAPI
230X509GetSubjectName (
231  IN      CONST UINT8  *Cert,
232  IN      UINTN        CertSize,
233  OUT     UINT8        *CertSubject,
234  IN OUT  UINTN        *SubjectSize
235  )
236{
237  BOOLEAN    Status;
238  X509       *X509Cert;
239  X509_NAME  *X509Name;
240
241  //
242  // Check input parameters.
243  //
244  if (Cert == NULL || SubjectSize == NULL) {
245    return FALSE;
246  }
247
248  X509Cert = NULL;
249
250  //
251  // Read DER-encoded X509 Certificate and Construct X509 object.
252  //
253  Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);
254  if ((X509Cert == NULL) || (!Status)) {
255    Status = FALSE;
256    goto _Exit;
257  }
258
259  Status = FALSE;
260
261  //
262  // Retrieve subject name from certificate object.
263  //
264  X509Name = X509_get_subject_name (X509Cert);
265  if (X509Name == NULL) {
266    goto _Exit;
267  }
268
269  if (*SubjectSize < (UINTN) X509Name->bytes->length) {
270    *SubjectSize = (UINTN) X509Name->bytes->length;
271    goto _Exit;
272  }
273  *SubjectSize = (UINTN) X509Name->bytes->length;
274  if (CertSubject != NULL) {
275    CopyMem (CertSubject, (UINT8 *) X509Name->bytes->data, *SubjectSize);
276    Status = TRUE;
277  }
278
279_Exit:
280  //
281  // Release Resources.
282  //
283  if (X509Cert != NULL) {
284    X509_free (X509Cert);
285  }
286
287  return Status;
288}
289
290/**
291  Retrieve the RSA Public Key from one DER-encoded X509 certificate.
292
293  @param[in]  Cert         Pointer to the DER-encoded X509 certificate.
294  @param[in]  CertSize     Size of the X509 certificate in bytes.
295  @param[out] RsaContext   Pointer to new-generated RSA context which contain the retrieved
296                           RSA public key component. Use RsaFree() function to free the
297                           resource.
298
299  If Cert is NULL, then return FALSE.
300  If RsaContext is NULL, then return FALSE.
301
302  @retval  TRUE   RSA Public Key was retrieved successfully.
303  @retval  FALSE  Fail to retrieve RSA public key from X509 certificate.
304
305**/
306BOOLEAN
307EFIAPI
308RsaGetPublicKeyFromX509 (
309  IN   CONST UINT8  *Cert,
310  IN   UINTN        CertSize,
311  OUT  VOID         **RsaContext
312  )
313{
314  BOOLEAN   Status;
315  EVP_PKEY  *Pkey;
316  X509      *X509Cert;
317
318  //
319  // Check input parameters.
320  //
321  if (Cert == NULL || RsaContext == NULL) {
322    return FALSE;
323  }
324
325  Pkey     = NULL;
326  X509Cert = NULL;
327
328  //
329  // Read DER-encoded X509 Certificate and Construct X509 object.
330  //
331  Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);
332  if ((X509Cert == NULL) || (!Status)) {
333    Status = FALSE;
334    goto _Exit;
335  }
336
337  Status = FALSE;
338
339  //
340  // Retrieve and check EVP_PKEY data from X509 Certificate.
341  //
342  Pkey = X509_get_pubkey (X509Cert);
343  if ((Pkey == NULL) || (Pkey->type != EVP_PKEY_RSA)) {
344    goto _Exit;
345  }
346
347  //
348  // Duplicate RSA Context from the retrieved EVP_PKEY.
349  //
350  if ((*RsaContext = RSAPublicKey_dup (Pkey->pkey.rsa)) != NULL) {
351    Status = TRUE;
352  }
353
354_Exit:
355  //
356  // Release Resources.
357  //
358  if (X509Cert != NULL) {
359    X509_free (X509Cert);
360  }
361
362  if (Pkey != NULL) {
363    EVP_PKEY_free (Pkey);
364  }
365
366  return Status;
367}
368
369/**
370  Verify one X509 certificate was issued by the trusted CA.
371
372  @param[in]      Cert         Pointer to the DER-encoded X509 certificate to be verified.
373  @param[in]      CertSize     Size of the X509 certificate in bytes.
374  @param[in]      CACert       Pointer to the DER-encoded trusted CA certificate.
375  @param[in]      CACertSize   Size of the CA Certificate in bytes.
376
377  If Cert is NULL, then return FALSE.
378  If CACert is NULL, then return FALSE.
379
380  @retval  TRUE   The certificate was issued by the trusted CA.
381  @retval  FALSE  Invalid certificate or the certificate was not issued by the given
382                  trusted CA.
383
384**/
385BOOLEAN
386EFIAPI
387X509VerifyCert (
388  IN  CONST UINT8  *Cert,
389  IN  UINTN        CertSize,
390  IN  CONST UINT8  *CACert,
391  IN  UINTN        CACertSize
392  )
393{
394  BOOLEAN         Status;
395  X509            *X509Cert;
396  X509            *X509CACert;
397  X509_STORE      *CertStore;
398  X509_STORE_CTX  CertCtx;
399
400  //
401  // Check input parameters.
402  //
403  if (Cert == NULL || CACert == NULL) {
404    return FALSE;
405  }
406
407  Status     = FALSE;
408  X509Cert   = NULL;
409  X509CACert = NULL;
410  CertStore  = NULL;
411
412  //
413  // Register & Initialize necessary digest algorithms for certificate verification.
414  //
415  if (EVP_add_digest (EVP_md5 ()) == 0) {
416    goto _Exit;
417  }
418  if (EVP_add_digest (EVP_sha1 ()) == 0) {
419    goto _Exit;
420  }
421  if (EVP_add_digest (EVP_sha256 ()) == 0) {
422    goto _Exit;
423  }
424
425  //
426  // Read DER-encoded certificate to be verified and Construct X509 object.
427  //
428  Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);
429  if ((X509Cert == NULL) || (!Status)) {
430    Status = FALSE;
431    goto _Exit;
432  }
433
434  //
435  // Read DER-encoded root certificate and Construct X509 object.
436  //
437  Status = X509ConstructCertificate (CACert, CACertSize, (UINT8 **) &X509CACert);
438  if ((X509CACert == NULL) || (!Status)) {
439    Status = FALSE;
440    goto _Exit;
441  }
442
443  Status = FALSE;
444
445  //
446  // Set up X509 Store for trusted certificate.
447  //
448  CertStore = X509_STORE_new ();
449  if (CertStore == NULL) {
450    goto _Exit;
451  }
452  if (!(X509_STORE_add_cert (CertStore, X509CACert))) {
453    goto _Exit;
454  }
455
456  //
457  // Set up X509_STORE_CTX for the subsequent verification operation.
458  //
459  if (!X509_STORE_CTX_init (&CertCtx, CertStore, X509Cert, NULL)) {
460    goto _Exit;
461  }
462
463  //
464  // X509 Certificate Verification.
465  //
466  Status = (BOOLEAN) X509_verify_cert (&CertCtx);
467  X509_STORE_CTX_cleanup (&CertCtx);
468
469_Exit:
470  //
471  // Release Resources.
472  //
473  if (X509Cert != NULL) {
474    X509_free (X509Cert);
475  }
476
477  if (X509CACert != NULL) {
478    X509_free (X509CACert);
479  }
480
481  if (CertStore != NULL) {
482    X509_STORE_free (CertStore);
483  }
484
485  return Status;
486}
487
488/**
489  Retrieve the TBSCertificate from one given X.509 certificate.
490
491  @param[in]      Cert         Pointer to the given DER-encoded X509 certificate.
492  @param[in]      CertSize     Size of the X509 certificate in bytes.
493  @param[out]     TBSCert      DER-Encoded To-Be-Signed certificate.
494  @param[out]     TBSCertSize  Size of the TBS certificate in bytes.
495
496  If Cert is NULL, then return FALSE.
497  If TBSCert is NULL, then return FALSE.
498  If TBSCertSize is NULL, then return FALSE.
499
500  @retval  TRUE   The TBSCertificate was retrieved successfully.
501  @retval  FALSE  Invalid X.509 certificate.
502
503**/
504BOOLEAN
505EFIAPI
506X509GetTBSCert (
507  IN  CONST UINT8  *Cert,
508  IN  UINTN        CertSize,
509  OUT UINT8        **TBSCert,
510  OUT UINTN        *TBSCertSize
511  )
512{
513  CONST UINT8  *Temp;
514  INTN         Asn1Tag;
515  INTN         ObjClass;
516  UINTN        Length;
517
518  //
519  // Check input parameters.
520  //
521  if ((Cert == NULL) || (TBSCert == NULL) || (TBSCertSize == NULL)) {
522    return FALSE;
523  }
524
525  //
526  // An X.509 Certificate is: (defined in RFC3280)
527  //   Certificate  ::=  SEQUENCE  {
528  //     tbsCertificate       TBSCertificate,
529  //     signatureAlgorithm   AlgorithmIdentifier,
530  //     signature            BIT STRING }
531  //
532  // and
533  //
534  //  TBSCertificate  ::=  SEQUENCE  {
535  //    version         [0]  Version DEFAULT v1,
536  //    ...
537  //    }
538  //
539  // So we can just ASN1-parse the x.509 DER-encoded data. If we strip
540  // the first SEQUENCE, the second SEQUENCE is the TBSCertificate.
541  //
542  Temp = Cert;
543  ASN1_get_object (&Temp, (long *)&Length, (int *)&Asn1Tag, (int *)&ObjClass, (long)CertSize);
544
545  if (Asn1Tag != V_ASN1_SEQUENCE) {
546    return FALSE;
547  }
548
549  *TBSCert = (UINT8 *)Temp;
550
551  ASN1_get_object (&Temp, (long *)&Length, (int *)&Asn1Tag, (int *)&ObjClass, (long)Length);
552  //
553  // Verify the parsed TBSCertificate is one correct SEQUENCE data.
554  //
555  if (Asn1Tag != V_ASN1_SEQUENCE) {
556    return FALSE;
557  }
558
559  *TBSCertSize = Length + (Temp - *TBSCert);
560
561  return TRUE;
562}
563