• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *    http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "x509_csr_openssl.h"
17 
18 #include "cf_log.h"
19 #include "cf_blob.h"
20 #include <openssl/x509.h>
21 #include <openssl/pem.h>
22 #include <openssl/evp.h>
23 #include <openssl/decoder.h>
24 #include "certificate_openssl_common.h"
25 #include "x509_distinguished_name.h"
26 #include "x509_distinguished_name_openssl.h"
27 
FreeResources(X509_REQ * req,EVP_PKEY * pkey,BIO * out)28 static void FreeResources(X509_REQ *req, EVP_PKEY *pkey, BIO *out)
29 {
30     if (req != NULL) {
31         X509_REQ_free(req);
32     }
33     if (pkey != NULL) {
34         EVP_PKEY_free(pkey);
35     }
36     if (out != NULL) {
37         BIO_free(out);
38     }
39 }
40 
InitializeRequest(X509_REQ ** req,const HcfGenCsrConf * conf)41 static CfResult InitializeRequest(X509_REQ **req, const HcfGenCsrConf *conf)
42 {
43     *req = X509_REQ_new();
44     if (*req == NULL) {
45         CfPrintOpensslError();
46         LOGE("X509_REQ_new failed");
47         return CF_ERR_MALLOC;
48     }
49 
50     if (X509_REQ_set_version(*req, 0L) != 1) {
51         LOGE("X509_REQ_set_version failed");
52         CfPrintOpensslError();
53         return CF_ERR_CRYPTO_OPERATION;
54     }
55 
56     HcfX509DistinguishedNameImpl *realName = (HcfX509DistinguishedNameImpl *)(conf->subject);
57     if (realName == NULL) {
58         LOGE("realName is NULL!");
59         return CF_ERR_MALLOC;
60     }
61     HcfX509DistinguishedNameOpensslImpl *opensslName = (HcfX509DistinguishedNameOpensslImpl *)(realName->spiObj);
62     if (opensslName == NULL) {
63         LOGE("opensslName is NULL!");
64         return CF_ERR_MALLOC;
65     }
66     X509_NAME *name = opensslName->name;
67     if (name == NULL) {
68         LOGE("name is NULL!");
69         return CF_ERR_MALLOC;
70     }
71 
72     if (!X509_REQ_set_subject_name(*req, name)) {
73         LOGE("X509_REQ_set_subject_name failed");
74         CfPrintOpensslError();
75         return CF_ERR_CRYPTO_OPERATION;
76     }
77 
78     if (conf->attribute.array != NULL && conf->attribute.attributeSize > 0) {
79         for (uint32_t i = 0; i < conf->attribute.attributeSize; i++) {
80             if (X509_REQ_add1_attr_by_txt(*req,
81                 conf->attribute.array[i].attributeName,
82                 MBSTRING_FLAG,
83                 (const unsigned char *)conf->attribute.array[i].attributeValue,
84                 -1) != 1) {
85                 LOGE("Failed to add attribute to request");
86                 CfPrintOpensslError();
87                 return CF_ERR_CRYPTO_OPERATION;
88             }
89         }
90     }
91     return CF_SUCCESS;
92 }
93 
LoadPrivateKey(EVP_PKEY ** pkey,PrivateKeyInfo * privateKeyInfo)94 static CfResult LoadPrivateKey(EVP_PKEY **pkey, PrivateKeyInfo *privateKeyInfo)
95 {
96     const char *keytype = "RSA";
97     const char *inputType = (privateKeyInfo->privateKey->encodingFormat == CF_FORMAT_PEM) ? "PEM" : "DER";
98     OSSL_DECODER_CTX *ctx = OSSL_DECODER_CTX_new_for_pkey(pkey, inputType, NULL, keytype,
99         OSSL_KEYMGMT_SELECT_PRIVATE_KEY, NULL, NULL);
100     if (ctx == NULL) {
101         LOGE("OSSL_DECODER_CTX_new_for_pkey fail.");
102         CfPrintOpensslError();
103         return CF_ERR_CRYPTO_OPERATION;
104     }
105     if (privateKeyInfo->privateKeyPassword != NULL) {
106         const unsigned char *passWd = (const unsigned char *)privateKeyInfo->privateKeyPassword;
107         if (OSSL_DECODER_CTX_set_passphrase(ctx, passWd,
108             strlen(privateKeyInfo->privateKeyPassword)) != CF_OPENSSL_SUCCESS) {
109             LOGE("OSSL_DECODER_CTX_set_passphrase failed");
110             CfPrintOpensslError();
111             OSSL_DECODER_CTX_free(ctx);
112             return CF_ERR_CRYPTO_OPERATION;
113         }
114     }
115     size_t pdataLen = privateKeyInfo->privateKey->len;
116     const unsigned char *pdata = (const unsigned char *)privateKeyInfo->privateKey->data;
117     int ret = OSSL_DECODER_from_data(ctx, &pdata, &pdataLen);
118     OSSL_DECODER_CTX_free(ctx);
119     if (ret != CF_OPENSSL_SUCCESS) {
120         LOGE("OSSL_DECODER_from_data failed.");
121         CfPrintOpensslError();
122         EVP_PKEY_free(*pkey);
123         *pkey = NULL;
124         return CF_ERR_CRYPTO_OPERATION;
125     }
126     return CF_SUCCESS;
127 }
128 
WriteCsrToString(BIO * out,bool isPem,X509_REQ * req,CfBlob * csrBlob)129 static CfResult WriteCsrToString(BIO *out, bool isPem, X509_REQ *req, CfBlob *csrBlob)
130 {
131     int ret = isPem ? PEM_write_bio_X509_REQ(out, req) : i2d_X509_REQ_bio(out, req);
132     if (ret != 1) {
133         LOGE("PEM_write_bio_X509_REQ or i2d_X509_REQ_bio failed");
134         return CF_ERR_CRYPTO_OPERATION;
135     }
136 
137     int csrLen = BIO_pending(out);
138     if (csrLen <= 0) {
139         LOGE("BIO_pending failed");
140         return CF_INVALID_PARAMS;
141     }
142     csrBlob->data = (uint8_t *)OPENSSL_malloc(csrLen);
143     if (csrBlob->data == NULL) {
144         LOGE("OPENSSL_malloc failed");
145         return CF_ERR_MALLOC;
146     }
147 
148     if (BIO_read(out, csrBlob->data, csrLen) != csrLen) {
149         OPENSSL_free(csrBlob->data);
150         csrBlob->data = NULL;
151         LOGE("BIO_read failed");
152         return CF_ERR_CRYPTO_OPERATION;
153     }
154     csrBlob->size = (uint32_t)csrLen;
155     return CF_SUCCESS;
156 }
157 
SetupCsrPubKeyAndSign(X509_REQ * req,EVP_PKEY * pkey,const HcfGenCsrConf * conf)158 static CfResult SetupCsrPubKeyAndSign(X509_REQ *req, EVP_PKEY *pkey, const HcfGenCsrConf *conf)
159 {
160     if (X509_REQ_set_pubkey(req, pkey) != 1) {
161         LOGE("X509_REQ_set_pubkey failed");
162         CfPrintOpensslError();
163         return CF_ERR_CRYPTO_OPERATION;
164     }
165     const EVP_MD *md = EVP_get_digestbyname(conf->mdName);
166     if (md == NULL) {
167         LOGE("Unsupported digest algorithm: %{public}s", conf->mdName);
168         CfPrintOpensslError();
169         return CF_ERR_CRYPTO_OPERATION;
170     }
171         if (!X509_REQ_sign(req, pkey, md)) {
172         LOGE("X509_REQ_sign failed");
173         CfPrintOpensslError();
174         return CF_ERR_CRYPTO_OPERATION;
175     }
176     return CF_SUCCESS;
177 }
178 
GenerateX509Csr(PrivateKeyInfo * privateKeyInfo,const HcfGenCsrConf * conf,CfBlob * csrBlob)179 CfResult GenerateX509Csr(PrivateKeyInfo *privateKeyInfo, const HcfGenCsrConf *conf, CfBlob *csrBlob)
180 {
181     if (privateKeyInfo == NULL || conf == NULL || csrBlob == NULL) {
182         return CF_INVALID_PARAMS;
183     }
184 
185     X509_REQ *req = NULL;
186     EVP_PKEY *pkey = NULL;
187     BIO *out = NULL;
188     CfResult result = CF_SUCCESS;
189 
190     do {
191         result = InitializeRequest(&req, conf);
192         if (result != CF_SUCCESS) {
193             break;
194         }
195         result = LoadPrivateKey(&pkey, privateKeyInfo);
196         if (result != CF_SUCCESS) {
197             LOGE("load prikey failed");
198             break;
199         }
200 
201         result = SetupCsrPubKeyAndSign(req, pkey, conf);
202         if (result != CF_SUCCESS) {
203             break;
204         }
205 
206         out = BIO_new(BIO_s_mem());
207         if (out == NULL) {
208             CfPrintOpensslError();
209             LOGE("BIO_new failed");
210             result = CF_ERR_MALLOC;
211             break;
212         }
213 
214         result = WriteCsrToString(out, conf->isPem, req, csrBlob);
215     } while (0);
216     FreeResources(req, pkey, out);
217     if (result != CF_SUCCESS) {
218         LOGE("Write csr toString failed.");
219         OPENSSL_free(csrBlob->data);
220         csrBlob->data = NULL;
221     }
222     return result;
223 }
224