CryptX509.c revision da9e7418daad22aee7b48790c1d1db5d2ede7e58
1/** @file
2  X.509 Certificate Handler Wrapper Implementation over OpenSSL.
3
4Copyright (c) 2010 - 2011, 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 ASSERT().
23  If SingleX509Cert is NULL, then ASSERT().
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  BIO      *CertBio;
42  X509     *X509Cert;
43  BOOLEAN  Status;
44
45  //
46  // ASSERT if Cert is NULL or SingleX509Cert is NULL.
47  //
48  ASSERT (Cert != NULL);
49  ASSERT (SingleX509Cert != NULL);
50
51  if (CertSize > INT_MAX) {
52    return FALSE;
53  }
54
55  Status = FALSE;
56
57  //
58  // Read DER-encoded X509 Certificate and Construct X509 object.
59  //
60  CertBio = BIO_new (BIO_s_mem ());
61  BIO_write (CertBio, Cert, (int) CertSize);
62  if (CertBio == NULL) {
63    goto _Exit;
64  }
65  X509Cert = d2i_X509_bio (CertBio, NULL);
66  if (X509Cert == NULL) {
67    goto _Exit;
68  }
69
70  *SingleX509Cert = (UINT8 *) X509Cert;
71  Status = TRUE;
72
73_Exit:
74  //
75  // Release Resources.
76  //
77  BIO_free (CertBio);
78
79  return Status;
80}
81
82/**
83  Construct a X509 stack object from a list of DER-encoded certificate data.
84
85  If X509Stack is NULL, then ASSERT().
86
87  @param[in, out]  X509Stack  On input, pointer to an existing X509 stack object.
88                              On output, pointer to the X509 stack object with new
89                              inserted X509 certificate.
90  @param           ...        A list of DER-encoded single certificate data followed
91                              by certificate size. A NULL terminates the list. The
92                              pairs are the arguments to X509ConstructCertificate().
93
94  @retval     TRUE            The X509 stack construction succeeded.
95  @retval     FALSE           The construction operation failed.
96
97**/
98BOOLEAN
99EFIAPI
100X509ConstructCertificateStack (
101  IN OUT  UINT8  **X509Stack,
102  ...
103  )
104{
105  UINT8           *Cert;
106  UINTN           CertSize;
107  X509            *X509Cert;
108  STACK_OF(X509)  *CertStack;
109  BOOLEAN         Status;
110  VA_LIST         Args;
111  UINTN           Index;
112
113  //
114  // ASSERT if input X509Stack is NULL.
115  //
116  ASSERT (X509Stack != NULL);
117
118  Status = FALSE;
119
120  //
121  // Initialize X509 stack object.
122  //
123  CertStack = (STACK_OF(X509) *) (*X509Stack);
124  if (CertStack == NULL) {
125    CertStack = sk_X509_new_null ();
126    if (CertStack == NULL) {
127      return Status;
128    }
129  }
130
131  VA_START (Args, X509Stack);
132
133  for (Index = 0; ; Index++) {
134    //
135    // If Cert is NULL, then it is the end of the list.
136    //
137    Cert = VA_ARG (Args, UINT8 *);
138    if (Cert == NULL) {
139      break;
140    }
141
142    CertSize = VA_ARG (Args, UINTN);
143
144    //
145    // Construct X509 Object from the given DER-encoded certificate data.
146    //
147    Status = X509ConstructCertificate (
148               (CONST UINT8 *) Cert,
149               CertSize,
150               (UINT8 **) &X509Cert
151               );
152    if (!Status) {
153      X509_free (X509Cert);
154      break;
155    }
156
157    //
158    // Insert the new X509 object into X509 stack object.
159    //
160    sk_X509_push (CertStack, X509Cert);
161  }
162
163  VA_END (Args);
164
165  if (!Status) {
166    sk_X509_pop_free (CertStack, X509_free);
167  } else {
168    *X509Stack = (UINT8 *) CertStack;
169  }
170
171  return Status;
172}
173
174/**
175  Release the specified X509 object.
176
177  If X509Cert is NULL, then ASSERT().
178
179  @param[in]  X509Cert  Pointer to the X509 object to be released.
180
181**/
182VOID
183EFIAPI
184X509Free (
185  IN  VOID  *X509Cert
186  )
187{
188  ASSERT (X509Cert != NULL);
189
190  //
191  // Free OpenSSL X509 object.
192  //
193  X509_free ((X509 *) X509Cert);
194}
195
196/**
197  Release the specified X509 stack object.
198
199  If X509Stack is NULL, then ASSERT().
200
201  @param[in]  X509Stack  Pointer to the X509 stack object to be released.
202
203**/
204VOID
205EFIAPI
206X509StackFree (
207  IN  VOID  *X509Stack
208  )
209{
210  ASSERT (X509Stack != NULL);
211
212  //
213  // Free OpenSSL X509 stack object.
214  //
215  sk_X509_pop_free ((STACK_OF(X509) *) X509Stack, X509_free);
216}
217
218/**
219  Retrieve the subject bytes from one X.509 certificate.
220
221  @param[in]      Cert         Pointer to the DER-encoded X509 certificate.
222  @param[in]      CertSize     Size of the X509 certificate in bytes.
223  @param[out]     CertSubject  Pointer to the retrieved certificate subject bytes.
224  @param[in, out] SubjectSize  The size in bytes of the CertSubject buffer on input,
225                               and the size of buffer returned CertSubject on output.
226
227  If Cert is NULL, then ASSERT().
228  If SubjectSize is NULL, then ASSERT().
229
230  @retval  TRUE   The certificate subject retrieved successfully.
231  @retval  FALSE  Invalid certificate, or the SubjectSize is too small for the result.
232                  The SubjectSize will be updated with the required size.
233
234**/
235BOOLEAN
236EFIAPI
237X509GetSubjectName (
238  IN      CONST UINT8  *Cert,
239  IN      UINTN        CertSize,
240  OUT     UINT8        *CertSubject,
241  IN OUT  UINTN        *SubjectSize
242  )
243{
244  BOOLEAN    Status;
245  X509       *X509Cert;
246  X509_NAME  *X509Name;
247
248  //
249  // ASSERT if Cert is NULL or SubjectSize is NULL.
250  //
251  ASSERT (Cert        != NULL);
252  ASSERT (SubjectSize != NULL);
253
254  Status   = FALSE;
255  X509Cert = NULL;
256
257  //
258  // Read DER-encoded X509 Certificate and Construct X509 object.
259  //
260  Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);
261  if ((X509Cert == NULL) || (!Status)) {
262    goto _Exit;
263  }
264
265  //
266  // Retrieve subject name from certificate object.
267  //
268  X509Name = X509_get_subject_name (X509Cert);
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  X509_free (X509Cert);
284
285  return Status;
286}
287
288/**
289  Retrieve the RSA Public Key from one DER-encoded X509 certificate.
290
291  @param[in]  Cert         Pointer to the DER-encoded X509 certificate.
292  @param[in]  CertSize     Size of the X509 certificate in bytes.
293  @param[out] RsaContext   Pointer to new-generated RSA context which contain the retrieved
294                           RSA public key component. Use RsaFree() function to free the
295                           resource.
296
297  If Cert is NULL, then ASSERT().
298  If RsaContext is NULL, then ASSERT().
299
300  @retval  TRUE   RSA Public Key was retrieved successfully.
301  @retval  FALSE  Fail to retrieve RSA public key from X509 certificate.
302
303**/
304BOOLEAN
305EFIAPI
306RsaGetPublicKeyFromX509 (
307  IN   CONST UINT8  *Cert,
308  IN   UINTN        CertSize,
309  OUT  VOID         **RsaContext
310  )
311{
312  BOOLEAN   Status;
313  EVP_PKEY  *Pkey;
314  X509      *X509Cert;
315
316  //
317  // ASSERT if Cert is NULL or RsaContext is NULL.
318  //
319  ASSERT (Cert       != NULL);
320  ASSERT (RsaContext != NULL);
321
322  Status   = FALSE;
323  Pkey     = NULL;
324  X509Cert = NULL;
325
326  //
327  // Read DER-encoded X509 Certificate and Construct X509 object.
328  //
329  Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);
330  if ((X509Cert == NULL) || (!Status)) {
331    goto _Exit;
332  }
333
334  //
335  // Retrieve and check EVP_PKEY data from X509 Certificate.
336  //
337  Pkey = X509_get_pubkey (X509Cert);
338  if ((Pkey == NULL) || (Pkey->type != EVP_PKEY_RSA)) {
339    goto _Exit;
340  }
341
342  //
343  // Duplicate RSA Context from the retrieved EVP_PKEY.
344  //
345  if ((*RsaContext = RSAPublicKey_dup (Pkey->pkey.rsa)) != NULL) {
346    Status = TRUE;
347  }
348
349_Exit:
350  //
351  // Release Resources.
352  //
353  X509_free (X509Cert);
354  EVP_PKEY_free (Pkey);
355
356  return Status;
357}
358
359/**
360  Verify one X509 certificate was issued by the trusted CA.
361
362  @param[in]      Cert         Pointer to the DER-encoded X509 certificate to be verified.
363  @param[in]      CertSize     Size of the X509 certificate in bytes.
364  @param[in]      CACert       Pointer to the DER-encoded trusted CA certificate.
365  @param[in]      CACertSize   Size of the CA Certificate in bytes.
366
367  If Cert is NULL, then ASSERT().
368  If CACert is NULL, then ASSERT().
369
370  @retval  TRUE   The certificate was issued by the trusted CA.
371  @retval  FALSE  Invalid certificate or the certificate was not issued by the given
372                  trusted CA.
373
374**/
375BOOLEAN
376EFIAPI
377X509VerifyCert (
378  IN  CONST UINT8  *Cert,
379  IN  UINTN        CertSize,
380  IN  CONST UINT8  *CACert,
381  IN  UINTN        CACertSize
382  )
383{
384  BOOLEAN         Status;
385  X509            *X509Cert;
386  X509            *X509CACert;
387  X509_STORE      *CertStore;
388  X509_STORE_CTX  CertCtx;
389
390  //
391  // ASSERT if Cert is NULL or CACert is NULL.
392  //
393  ASSERT (Cert   != NULL);
394  ASSERT (CACert != NULL);
395
396  Status     = FALSE;
397  X509Cert   = NULL;
398  X509CACert = NULL;
399  CertStore  = NULL;
400
401  //
402  // Register & Initialize necessary digest algorithms for certificate verification.
403  //
404  EVP_add_digest (EVP_md5());
405  EVP_add_digest (EVP_sha1());
406  EVP_add_digest (EVP_sha256());
407
408  //
409  // Read DER-encoded certificate to be verified and Construct X509 object.
410  //
411  Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);
412  if ((X509Cert == NULL) || (!Status)) {
413    goto _Exit;
414  }
415
416  //
417  // Read DER-encoded root certificate and Construct X509 object.
418  //
419  Status = X509ConstructCertificate (CACert, CACertSize, (UINT8 **) &X509CACert);
420  if ((X509CACert == NULL) || (!Status)) {
421    goto _Exit;
422  }
423
424  //
425  // Set up X509 Store for trusted certificate.
426  //
427  CertStore = X509_STORE_new ();
428  if (CertStore == NULL) {
429    goto _Exit;
430  }
431  if (!(X509_STORE_add_cert (CertStore, X509CACert))) {
432    goto _Exit;
433  }
434
435  //
436  // Set up X509_STORE_CTX for the subsequent verification operation.
437  //
438  if (!X509_STORE_CTX_init (&CertCtx, CertStore, X509Cert, NULL)) {
439    goto _Exit;
440  }
441
442  //
443  // X509 Certificate Verification.
444  //
445  Status = (BOOLEAN) X509_verify_cert (&CertCtx);
446  X509_STORE_CTX_cleanup (&CertCtx);
447
448_Exit:
449  //
450  // Release Resources.
451  //
452  X509_free (X509Cert);
453  X509_free (X509CACert);
454  X509_STORE_free (CertStore);
455
456  return Status;
457}
458