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