1. Add new API supports for PEM & X509 key retrieving & verification;
[mirror_edk2.git] / CryptoPkg / Library / BaseCryptLib / Pk / CryptX509.c
1 /** @file\r
2   X.509 Certificate Handler Wrapper Implementation over OpenSSL.\r
3 \r
4 Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>\r
5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution.  The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9 \r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12 \r
13 **/\r
14 \r
15 #include "InternalCryptLib.h"\r
16 #include <openssl/x509.h>\r
17 \r
18 /**\r
19   Retrieve the subject bytes from one X.509 certificate.\r
20 \r
21   @param[in]      Cert         Pointer to the DER-encoded X509 certificate.\r
22   @param[in]      CertSize     Size of the X509 certificate in bytes.\r
23   @param[out]     CertSubject  Pointer to the retrieved certificate subject bytes.\r
24   @param[in, out] SubjectSize  The size in bytes of the CertSubject buffer on input,\r
25                                and the size of buffer returned CertSubject on output.\r
26 \r
27   If Cert is NULL, then ASSERT().\r
28   If SubjectSize is NULL, then ASSERT().\r
29 \r
30   @retval  TRUE   The certificate subject retrieved successfully.\r
31   @retval  FALSE  Invalid certificate, or the SubjectSize is too small for the result.\r
32                   The SubjectSize will be updated with the required size.\r
33 \r
34 **/\r
35 BOOLEAN\r
36 EFIAPI\r
37 X509GetSubjectName (\r
38   IN      CONST UINT8  *Cert,\r
39   IN      UINTN        CertSize,\r
40   OUT     UINT8        *CertSubject,\r
41   IN OUT  UINTN        *SubjectSize\r
42   )\r
43 {\r
44   BOOLEAN    Status;\r
45   BIO        *CertBio;\r
46   X509       *X509Cert;\r
47   X509_NAME  *X509Name;\r
48 \r
49   //\r
50   // ASSERT if Cert is NULL or SubjectSize is NULL.\r
51   //\r
52   ASSERT (Cert        != NULL);\r
53   ASSERT (SubjectSize != NULL);\r
54 \r
55   Status   = FALSE;\r
56   X509Cert = NULL;\r
57 \r
58   //\r
59   // Read DER-encoded X509 Certificate and Construct X509 object.\r
60   //\r
61   CertBio = BIO_new (BIO_s_mem ());\r
62   BIO_write (CertBio, Cert, (int)CertSize);\r
63   if (CertBio == NULL) {\r
64     goto _Exit;\r
65   }\r
66   X509Cert = d2i_X509_bio (CertBio, NULL);\r
67   if (Cert == NULL) {\r
68     goto _Exit;\r
69   }\r
70 \r
71   //\r
72   // Retrieve subject name from certificate object.\r
73   //\r
74   X509Name = X509_get_subject_name (X509Cert);\r
75   if (*SubjectSize < (UINTN) X509Name->bytes->length) {\r
76     *SubjectSize = (UINTN) X509Name->bytes->length;\r
77     goto _Exit;\r
78   }\r
79   *SubjectSize = (UINTN) X509Name->bytes->length;\r
80   if (CertSubject != NULL) {\r
81     CopyMem (CertSubject, (UINT8 *)X509Name->bytes->data, *SubjectSize);\r
82     Status = TRUE;\r
83   }\r
84 \r
85 _Exit:\r
86   //\r
87   // Release Resources.\r
88   //\r
89   BIO_free (CertBio);\r
90   X509_free (X509Cert);\r
91 \r
92   return Status;\r
93 }\r
94 \r
95 /**\r
96   Retrieve the RSA Public Key from one DER-encoded X509 certificate.\r
97 \r
98   @param[in]  Cert         Pointer to the DER-encoded X509 certificate.\r
99   @param[in]  CertSize     Size of the X509 certificate in bytes.\r
100   @param[out] RsaContext   Pointer to new-generated RSA context which contain the retrieved\r
101                            RSA public key component. Use RsaFree() function to free the\r
102                            resource.\r
103 \r
104   If Cert is NULL, then ASSERT().\r
105   If RsaContext is NULL, then ASSERT().\r
106 \r
107   @retval  TRUE   RSA Public Key was retrieved successfully.\r
108   @retval  FALSE  Fail to retrieve RSA public key from X509 certificate.\r
109 \r
110 **/\r
111 BOOLEAN\r
112 EFIAPI\r
113 RsaGetPublicKeyFromX509 (\r
114   IN   CONST UINT8  *Cert,\r
115   IN   UINTN        CertSize,\r
116   OUT  VOID         **RsaContext\r
117   )\r
118 {\r
119   BOOLEAN   Status;\r
120   EVP_PKEY  *Pkey;\r
121   BIO       *CertBio;\r
122   X509      *X509Cert;\r
123 \r
124   //\r
125   // ASSERT if Cert is NULL or RsaContext is NULL.\r
126   //\r
127   ASSERT (Cert       != NULL);\r
128   ASSERT (RsaContext != NULL);\r
129 \r
130   Status   = FALSE;\r
131   Pkey     = NULL;\r
132   CertBio  = NULL;\r
133   X509Cert = NULL;\r
134 \r
135   //\r
136   // Read DER-encoded X509 Certificate and Construct X509 object.\r
137   //\r
138   CertBio = BIO_new (BIO_s_mem ());\r
139   BIO_write (CertBio, Cert, (int)CertSize);\r
140   if (CertBio == NULL) {\r
141     goto _Exit;\r
142   }\r
143   X509Cert = d2i_X509_bio (CertBio, NULL);\r
144   if (X509Cert == NULL) {\r
145     goto _Exit;\r
146   }\r
147 \r
148   //\r
149   // Retrieve and check EVP_PKEY data from X509 Certificate.\r
150   //\r
151   Pkey = X509_get_pubkey (X509Cert);\r
152   if ((Pkey == NULL) || (Pkey->type != EVP_PKEY_RSA)) {\r
153     goto _Exit;\r
154   }\r
155 \r
156   //\r
157   // Duplicate RSA Context from the retrieved EVP_PKEY.\r
158   //\r
159   if ((*RsaContext = RSAPublicKey_dup (Pkey->pkey.rsa)) != NULL) {\r
160     Status = TRUE;\r
161   }\r
162 \r
163 _Exit:\r
164   //\r
165   // Release Resources.\r
166   //\r
167   BIO_free (CertBio);\r
168   X509_free (X509Cert);\r
169   EVP_PKEY_free (Pkey);\r
170 \r
171   return Status;\r
172 }\r
173 \r
174 /**\r
175   Verify one X509 certificate was issued by the trusted CA.\r
176 \r
177   @param[in]      Cert         Pointer to the DER-encoded X509 certificate to be verified.\r
178   @param[in]      CertSize     Size of the X509 certificate in bytes.\r
179   @param[in]      CACert       Pointer to the DER-encoded trusted CA certificate.\r
180   @param[in]      CACertSize   Size of the CA Certificate in bytes.\r
181 \r
182   If Cert is NULL, then ASSERT().\r
183   If CACert is NULL, then ASSERT().\r
184 \r
185   @retval  TRUE   The certificate was issued by the trusted CA.\r
186   @retval  FALSE  Invalid certificate or the certificate was not issued by the given\r
187                   trusted CA.\r
188 \r
189 **/\r
190 BOOLEAN\r
191 EFIAPI\r
192 X509VerifyCert (\r
193   IN  CONST UINT8  *Cert,\r
194   IN  UINTN        CertSize,\r
195   IN  CONST UINT8  *CACert,\r
196   IN  UINTN        CACertSize\r
197   )\r
198 {\r
199   BOOLEAN         Status;\r
200   BIO             *BioCert;\r
201   BIO             *BioCACert;\r
202   X509            *X509Cert;\r
203   X509            *X509CACert;\r
204   X509_STORE      *CertStore;\r
205   X509_STORE_CTX  CertCtx;\r
206 \r
207   //\r
208   // ASSERT if Cert is NULL or CACert is NULL.\r
209   //\r
210   ASSERT (Cert   != NULL);\r
211   ASSERT (CACert != NULL);\r
212 \r
213   Status     = FALSE;\r
214   BioCert    = NULL;\r
215   BioCACert  = NULL;\r
216   X509Cert   = NULL;\r
217   X509CACert = NULL;\r
218   CertStore  = NULL;\r
219 \r
220   //\r
221   // Register & Initialize necessary digest algorithms for certificate verification.\r
222   //\r
223   EVP_add_digest (EVP_md5());\r
224   EVP_add_digest (EVP_sha1());\r
225   EVP_add_digest (EVP_sha256());\r
226 \r
227   //\r
228   // Read DER-encoded certificate to be verified and Construct X509 object.\r
229   //\r
230   BioCert = BIO_new (BIO_s_mem ());\r
231   BIO_write (BioCert, Cert, (int)CertSize);\r
232   if (BioCert == NULL) {\r
233     goto _Exit;\r
234   }\r
235   X509Cert = d2i_X509_bio (BioCert, NULL);\r
236   if (X509Cert == NULL) {\r
237     goto _Exit;\r
238   }\r
239 \r
240   //\r
241   // Read DER-encoded root certificate and Construct X509 object.\r
242   //\r
243   BioCACert = BIO_new (BIO_s_mem());\r
244   BIO_write (BioCACert, CACert, (int)CACertSize);\r
245   if (BioCert == NULL) {\r
246     goto _Exit;\r
247   }\r
248   X509CACert = d2i_X509_bio (BioCACert, NULL);\r
249   if (CACert == NULL) {\r
250     goto _Exit;\r
251   }\r
252 \r
253   //\r
254   // Set up X509 Store for trusted certificate.\r
255   //\r
256   CertStore = X509_STORE_new ();\r
257   if (CertStore == NULL) {\r
258     goto _Exit;\r
259   }\r
260   if (!(X509_STORE_add_cert (CertStore, X509CACert))) {\r
261     goto _Exit;\r
262   }\r
263 \r
264   //\r
265   // Set up X509_STORE_CTX for the subsequent verification operation.\r
266   //\r
267   if (!X509_STORE_CTX_init (&CertCtx, CertStore, X509Cert, NULL)) {\r
268     goto _Exit;\r
269   }\r
270 \r
271   //\r
272   // X509 Certificate Verification.\r
273   //\r
274   Status = (BOOLEAN) X509_verify_cert (&CertCtx);\r
275 \r
276 _Exit:\r
277   //\r
278   // Release Resources.\r
279   //\r
280   BIO_free (BioCert);\r
281   BIO_free (BioCACert);\r
282   X509_free (X509Cert);\r
283   X509_free (X509CACert);\r
284   X509_STORE_free (CertStore);\r
285   X509_STORE_CTX_cleanup (&CertCtx);\r
286 \r
287   return Status;\r
288 }\r