/src/open62541_15/plugins/crypto/mbedtls/create_certificate.c
Line | Count | Source |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
4 | | * |
5 | | * Copyright (c) 2023 Fraunhofer IOSB (Author: Noel Graf) |
6 | | * Copyright 2026 (c) o6 Automation GmbH (Author: Andreas Ebner) |
7 | | * |
8 | | */ |
9 | | |
10 | | #include <open62541/plugin/create_certificate.h> |
11 | | |
12 | | #if defined(UA_ENABLE_ENCRYPTION_MBEDTLS) |
13 | | |
14 | | #include "securitypolicy_common.h" |
15 | | #include "../deps/musl_inet_pton.h" |
16 | | |
17 | | #include <time.h> |
18 | | |
19 | | #include <mbedtls/x509_crt.h> |
20 | | #include <mbedtls/oid.h> |
21 | | #include <mbedtls/asn1write.h> |
22 | | #include <mbedtls/entropy.h> |
23 | | #include <mbedtls/ctr_drbg.h> |
24 | | #include <mbedtls/platform.h> |
25 | | #include <mbedtls/version.h> |
26 | | |
27 | | #define SET_OID(x, oid) \ |
28 | 0 | do { x.len = MBEDTLS_OID_SIZE(oid); x.p = (unsigned char *) oid; } while (0) |
29 | | |
30 | | typedef struct mbedtls_write_san_node{ |
31 | | int type; |
32 | | char* host; |
33 | | size_t hostlen; |
34 | | } mbedtls_write_san_node; |
35 | | |
36 | | typedef struct mbedtls_write_san_list{ |
37 | | mbedtls_write_san_node node; |
38 | | struct mbedtls_write_san_list* next; |
39 | | } mbedtls_write_san_list; |
40 | | |
41 | | static size_t mbedtls_get_san_list_deep(const mbedtls_write_san_list* sanlist); |
42 | | |
43 | | int mbedtls_x509write_crt_set_subject_alt_name(mbedtls_x509write_cert *ctx, const mbedtls_write_san_list* sanlist); |
44 | | |
45 | | #if MBEDTLS_VERSION_NUMBER < 0x03030000 |
46 | | int mbedtls_x509write_crt_set_ext_key_usage(mbedtls_x509write_cert *ctx, |
47 | | const mbedtls_asn1_sequence *exts); |
48 | | #endif |
49 | | |
50 | | static int write_certificate(mbedtls_x509write_cert *crt, UA_CertificateFormat certFormat, |
51 | | UA_ByteString *outCertificate, int (*f_rng)(void *, unsigned char *, size_t), |
52 | | void *p_rng); |
53 | | |
54 | | static int write_private_key(mbedtls_pk_context *key, UA_CertificateFormat keyFormat, UA_ByteString *outPrivateKey); |
55 | | |
56 | | /* Case-insensitive comparison of a UA_String with a C string literal */ |
57 | | static UA_Boolean |
58 | 0 | uaStringEqualsCI_mbedtls(const UA_String *uaStr, const char *cStr) { |
59 | 0 | size_t cLen = strlen(cStr); |
60 | 0 | if(uaStr->length != cLen) |
61 | 0 | return false; |
62 | 0 | for(size_t i = 0; i < cLen; i++) { |
63 | 0 | char a = (char)uaStr->data[i]; |
64 | 0 | char b = cStr[i]; |
65 | 0 | if(a >= 'A' && a <= 'Z') a = (char)(a + 32); |
66 | 0 | if(b >= 'A' && b <= 'Z') b = (char)(b + 32); |
67 | 0 | if(a != b) return false; |
68 | 0 | } |
69 | 0 | return true; |
70 | 0 | } |
71 | | |
72 | | UA_StatusCode |
73 | | UA_CreateCertificate(const UA_Logger *logger, const UA_String *subject, |
74 | | size_t subjectSize, const UA_String *subjectAltName, |
75 | | size_t subjectAltNameSize, UA_CertificateFormat certFormat, |
76 | | UA_KeyValueMap *params, UA_ByteString *outPrivateKey, |
77 | 0 | UA_ByteString *outCertificate) { |
78 | 0 | if(!outPrivateKey || !outCertificate || !logger || !subjectAltName || !subject || |
79 | 0 | subjectAltNameSize == 0 || subjectSize == 0 || |
80 | 0 | (certFormat != UA_CERTIFICATEFORMAT_DER && certFormat != UA_CERTIFICATEFORMAT_PEM)) |
81 | 0 | return UA_STATUSCODE_BADINVALIDARGUMENT; |
82 | | |
83 | | /* Use the maximum size */ |
84 | 0 | UA_UInt16 keySizeBits = 4096; |
85 | | /* Default to 1 year */ |
86 | 0 | UA_UInt16 expiresInDays = 365; |
87 | | /* Key type: 0 = RSA (default), 1 = EC */ |
88 | 0 | int keyTypeEC = 0; |
89 | 0 | UA_String eccCurve = UA_STRING_STATIC("prime256v1"); |
90 | |
|
91 | 0 | if(params) { |
92 | 0 | const UA_UInt16 *keySizeBitsValue = (const UA_UInt16 *)UA_KeyValueMap_getScalar( |
93 | 0 | params, UA_QUALIFIEDNAME(0, "key-size-bits"), &UA_TYPES[UA_TYPES_UINT16]); |
94 | 0 | if(keySizeBitsValue) |
95 | 0 | keySizeBits = *keySizeBitsValue; |
96 | |
|
97 | 0 | const UA_UInt16 *expiresInDaysValue = (const UA_UInt16 *)UA_KeyValueMap_getScalar( |
98 | 0 | params, UA_QUALIFIEDNAME(0, "expires-in-days"), &UA_TYPES[UA_TYPES_UINT16]); |
99 | 0 | if(expiresInDaysValue) |
100 | 0 | expiresInDays = *expiresInDaysValue; |
101 | |
|
102 | 0 | const UA_String *keyTypeValue = (const UA_String *)UA_KeyValueMap_getScalar( |
103 | 0 | params, UA_QUALIFIEDNAME(0, "key-type"), &UA_TYPES[UA_TYPES_STRING]); |
104 | 0 | if(keyTypeValue && uaStringEqualsCI_mbedtls(keyTypeValue, "ec")) |
105 | 0 | keyTypeEC = 1; |
106 | |
|
107 | 0 | const UA_String *eccCurveValue = (const UA_String *)UA_KeyValueMap_getScalar( |
108 | 0 | params, UA_QUALIFIEDNAME(0, "ecc-curve"), &UA_TYPES[UA_TYPES_STRING]); |
109 | 0 | if(eccCurveValue && eccCurveValue->length > 0) |
110 | 0 | eccCurve = *eccCurveValue; |
111 | 0 | } |
112 | |
|
113 | 0 | UA_ByteString_init(outPrivateKey); |
114 | 0 | UA_ByteString_init(outCertificate); |
115 | |
|
116 | 0 | mbedtls_pk_context key; |
117 | 0 | mbedtls_ctr_drbg_context ctr_drbg; |
118 | 0 | mbedtls_entropy_context entropy; |
119 | 0 | const char *pers = "gen_key"; |
120 | 0 | mbedtls_x509write_cert crt; |
121 | |
|
122 | 0 | UA_StatusCode errRet = UA_STATUSCODE_GOOD; |
123 | | |
124 | | /* Set to sane values */ |
125 | 0 | mbedtls_pk_init(&key); |
126 | 0 | mbedtls_ctr_drbg_init(&ctr_drbg); |
127 | 0 | mbedtls_entropy_init(&entropy); |
128 | 0 | mbedtls_x509write_crt_init(&crt); |
129 | | |
130 | | /* Seed the random number generator */ |
131 | 0 | if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *)pers, strlen(pers)) != 0) { |
132 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
133 | 0 | "Failed to initialize the random number generator."); |
134 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
135 | 0 | goto cleanup; |
136 | 0 | } |
137 | | |
138 | | /* Generate a key pair */ |
139 | 0 | if(keyTypeEC) { |
140 | | #if MBEDTLS_VERSION_NUMBER >= 0x03000000 |
141 | | /* Map curve name to mbedTLS group ID */ |
142 | | mbedtls_ecp_group_id grp_id = MBEDTLS_ECP_DP_SECP256R1; /* default: P-256 */ |
143 | | if(uaStringEqualsCI_mbedtls(&eccCurve, "prime256v1") || |
144 | | uaStringEqualsCI_mbedtls(&eccCurve, "nistp256")) |
145 | | grp_id = MBEDTLS_ECP_DP_SECP256R1; |
146 | | else if(uaStringEqualsCI_mbedtls(&eccCurve, "secp384r1") || |
147 | | uaStringEqualsCI_mbedtls(&eccCurve, "nistp384")) |
148 | | grp_id = MBEDTLS_ECP_DP_SECP384R1; |
149 | | else if(uaStringEqualsCI_mbedtls(&eccCurve, "brainpoolp256r1")) |
150 | | grp_id = MBEDTLS_ECP_DP_BP256R1; |
151 | | else if(uaStringEqualsCI_mbedtls(&eccCurve, "brainpoolp384r1")) |
152 | | grp_id = MBEDTLS_ECP_DP_BP384R1; |
153 | | else if(uaStringEqualsCI_mbedtls(&eccCurve, "ed25519") || |
154 | | uaStringEqualsCI_mbedtls(&eccCurve, "ed448")) { |
155 | | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
156 | | "EdDSA (Curve25519/Curve448) certificate generation " |
157 | | "is not supported with mbedTLS. Use OpenSSL."); |
158 | | errRet = UA_STATUSCODE_BADNOTIMPLEMENTED; |
159 | | goto cleanup; |
160 | | } else { |
161 | | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
162 | | "Create Certificate: Unsupported ECC curve for mbedTLS."); |
163 | | errRet = UA_STATUSCODE_BADINVALIDARGUMENT; |
164 | | goto cleanup; |
165 | | } |
166 | | |
167 | | if(mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)) != 0 || |
168 | | mbedtls_ecp_gen_key(grp_id, mbedtls_pk_ec(key), |
169 | | mbedtls_ctr_drbg_random, &ctr_drbg) != 0) { |
170 | | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
171 | | "Failed to generate ECC key pair."); |
172 | | errRet = UA_STATUSCODE_BADINTERNALERROR; |
173 | | goto cleanup; |
174 | | } |
175 | | #else |
176 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
177 | 0 | "ECC certificate generation requires mbedTLS >= 3.0."); |
178 | 0 | errRet = UA_STATUSCODE_BADNOTIMPLEMENTED; |
179 | 0 | goto cleanup; |
180 | 0 | #endif |
181 | 0 | } else { |
182 | | /* Generate an RSA key pair */ |
183 | 0 | if(mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)) != 0 || |
184 | 0 | mbedtls_rsa_gen_key(mbedtls_pk_rsa(key), mbedtls_ctr_drbg_random, |
185 | 0 | &ctr_drbg, keySizeBits, 65537) != 0) { |
186 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
187 | 0 | "Failed to generate RSA key pair."); |
188 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
189 | 0 | goto cleanup; |
190 | 0 | } |
191 | 0 | } |
192 | | |
193 | | /* Setting certificate values */ |
194 | 0 | mbedtls_x509write_crt_set_version(&crt, MBEDTLS_X509_CRT_VERSION_3); |
195 | | /* P-384 / brainpoolP384r1 use SHA-384; everything else uses SHA-256 */ |
196 | 0 | if(keyTypeEC && |
197 | 0 | (uaStringEqualsCI_mbedtls(&eccCurve, "secp384r1") || |
198 | 0 | uaStringEqualsCI_mbedtls(&eccCurve, "nistp384") || |
199 | 0 | uaStringEqualsCI_mbedtls(&eccCurve, "brainpoolp384r1"))) |
200 | 0 | mbedtls_x509write_crt_set_md_alg(&crt, MBEDTLS_MD_SHA384); |
201 | 0 | else |
202 | 0 | mbedtls_x509write_crt_set_md_alg(&crt, MBEDTLS_MD_SHA256); |
203 | |
|
204 | 0 | size_t subject_char_len = 0; |
205 | 0 | for(size_t i = 0; i < subjectSize; i++) { |
206 | 0 | subject_char_len += subject[i].length; |
207 | 0 | } |
208 | 0 | char *subject_char = (char*)UA_malloc(subject_char_len + subjectSize); |
209 | 0 | if(!subject_char) { |
210 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
211 | 0 | "Cannot allocate memory for subject. Out of memory."); |
212 | 0 | errRet = UA_STATUSCODE_BADOUTOFMEMORY; |
213 | 0 | goto cleanup; |
214 | 0 | } |
215 | | |
216 | 0 | size_t pos = 0; |
217 | 0 | for(size_t i = 0; i < subjectSize; i++) { |
218 | 0 | subject_char_len += subject[i].length; |
219 | 0 | memcpy(subject_char + pos, subject[i].data, subject[i].length); |
220 | 0 | pos += subject[i].length; |
221 | 0 | if(i < subjectSize - 1) |
222 | 0 | subject_char[pos++] = ','; |
223 | 0 | else |
224 | 0 | subject_char[pos++] = '\0'; |
225 | 0 | } |
226 | |
|
227 | 0 | if((mbedtls_x509write_crt_set_subject_name(&crt, subject_char)) != 0) { |
228 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
229 | 0 | "Setting subject failed."); |
230 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
231 | 0 | UA_free(subject_char); |
232 | 0 | goto cleanup; |
233 | 0 | } |
234 | | |
235 | 0 | if((mbedtls_x509write_crt_set_issuer_name(&crt, subject_char)) != 0) { |
236 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
237 | 0 | "Setting issuer failed."); |
238 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
239 | 0 | UA_free(subject_char); |
240 | 0 | goto cleanup; |
241 | 0 | } |
242 | | |
243 | 0 | UA_free(subject_char); |
244 | |
|
245 | 0 | mbedtls_write_san_list *cur = NULL; |
246 | 0 | mbedtls_write_san_list *cur_tmp = NULL; |
247 | 0 | mbedtls_write_san_list *head = NULL; |
248 | 0 | for(size_t i = 0; i < subjectAltNameSize; i++) { |
249 | | /* Copy and null-terminate */ |
250 | 0 | char *subAlt = (char *)UA_malloc(subjectAltName[i].length + 1); |
251 | 0 | memcpy(subAlt, subjectAltName[i].data, subjectAltName[i].length); |
252 | 0 | subAlt[subjectAltName[i].length] = 0; |
253 | | |
254 | | /* split into SAN type and value */ |
255 | 0 | char *sanType = NULL; |
256 | 0 | for(char *pos = subAlt; *pos != 0; pos++) { |
257 | 0 | if(*pos == ':') { |
258 | 0 | *pos = '\0'; |
259 | 0 | sanType = subAlt; |
260 | 0 | break; |
261 | 0 | } |
262 | 0 | } |
263 | |
|
264 | 0 | if(!sanType) { |
265 | 0 | UA_LOG_WARNING(logger, UA_LOGCATEGORY_SECURECHANNEL, "Invalid Input format"); |
266 | 0 | UA_free(subAlt); |
267 | 0 | continue; |
268 | 0 | } |
269 | | |
270 | 0 | char *sanValue = (char *)subjectAltName[i].data + strlen(sanType) + 1; |
271 | 0 | size_t sanValueLength = subjectAltName[i].length - strlen(sanType) - 1; |
272 | |
|
273 | 0 | cur_tmp = (mbedtls_write_san_list*)mbedtls_calloc(1, sizeof(mbedtls_write_san_list)); |
274 | 0 | cur_tmp->next = NULL; |
275 | 0 | cur_tmp->node.host = sanValue; |
276 | 0 | cur_tmp->node.hostlen = sanValueLength; |
277 | |
|
278 | 0 | if(strcmp(sanType, "DNS") == 0) { |
279 | 0 | cur_tmp->node.type = MBEDTLS_X509_SAN_DNS_NAME; |
280 | 0 | } else if(strcmp(sanType, "URI") == 0) { |
281 | 0 | cur_tmp->node.type = MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER; |
282 | 0 | } else if(strcmp(sanType, "IP") == 0) { |
283 | 0 | uint8_t ip[4] = {0}; |
284 | 0 | if(musl_inet_pton(AF_INET, sanValue, ip) <= 0) { |
285 | 0 | UA_LOG_WARNING(logger, UA_LOGCATEGORY_SECURECHANNEL, "IP SAN preparation failed"); |
286 | 0 | mbedtls_free(cur_tmp); |
287 | 0 | UA_free(subAlt); |
288 | 0 | continue; |
289 | 0 | } |
290 | 0 | cur_tmp->node.type = MBEDTLS_X509_SAN_IP_ADDRESS; |
291 | 0 | cur_tmp->node.host = (char *)ip; |
292 | 0 | cur_tmp->node.hostlen = sizeof(ip); |
293 | 0 | } else if(strcmp(sanType, "RFC822") == 0) { |
294 | 0 | cur_tmp->node.type = MBEDTLS_X509_SAN_RFC822_NAME; |
295 | 0 | } else { |
296 | 0 | UA_LOG_WARNING(logger, UA_LOGCATEGORY_SECURECHANNEL, "Given an unsupported SAN"); |
297 | 0 | mbedtls_free(cur_tmp); |
298 | 0 | UA_free(subAlt); |
299 | 0 | continue; |
300 | 0 | } |
301 | | |
302 | 0 | if(!cur) { |
303 | 0 | cur = cur_tmp; |
304 | 0 | head = cur_tmp; |
305 | 0 | } else { |
306 | 0 | cur->next = cur_tmp; |
307 | 0 | cur = cur->next; |
308 | 0 | } |
309 | |
|
310 | 0 | UA_free(subAlt); |
311 | 0 | } |
312 | |
|
313 | 0 | if((mbedtls_x509write_crt_set_subject_alt_name(&crt, head)) != 0) { |
314 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
315 | 0 | "Setting subject alternative name failed."); |
316 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
317 | 0 | while(head != NULL) { |
318 | 0 | cur_tmp = head->next; |
319 | 0 | mbedtls_free(head); |
320 | 0 | head = cur_tmp; |
321 | 0 | } |
322 | 0 | goto cleanup; |
323 | 0 | } |
324 | | |
325 | 0 | while(head != NULL) { |
326 | 0 | cur_tmp = head->next; |
327 | 0 | mbedtls_free(head); |
328 | 0 | head = cur_tmp; |
329 | 0 | } |
330 | |
|
331 | | #if MBEDTLS_VERSION_NUMBER >= 0x03040000 |
332 | | unsigned char *serial = (unsigned char *)"1"; |
333 | | size_t serial_len = 1; |
334 | | mbedtls_x509write_crt_set_serial_raw(&crt, serial, serial_len); |
335 | | #else |
336 | 0 | mbedtls_mpi serial_mpi; |
337 | 0 | mbedtls_mpi_init(&serial_mpi); |
338 | 0 | mbedtls_mpi_lset(&serial_mpi, 1); |
339 | 0 | mbedtls_x509write_crt_set_serial(&crt, &serial_mpi); |
340 | 0 | mbedtls_mpi_free(&serial_mpi); |
341 | 0 | #endif |
342 | | |
343 | | /* Get the current time */ |
344 | 0 | time_t rawTime; |
345 | 0 | struct tm *timeInfo; |
346 | 0 | time(&rawTime); |
347 | 0 | timeInfo = gmtime(&rawTime); |
348 | | |
349 | | /* Format the current timestamp */ |
350 | 0 | char current_timestamp[15]; // YYYYMMDDhhmmss + '\0' |
351 | 0 | strftime(current_timestamp, sizeof(current_timestamp), "%Y%m%d%H%M%S", timeInfo); |
352 | | |
353 | | /* Calculate the future timestamp */ |
354 | 0 | timeInfo->tm_mday += expiresInDays; |
355 | 0 | time_t future_time = mktime(timeInfo); |
356 | | |
357 | | /* Format the future timestamp */ |
358 | 0 | char future_timestamp[15]; // YYYYMMDDhhmmss + '\0' |
359 | 0 | strftime(future_timestamp, sizeof(future_timestamp), "%Y%m%d%H%M%S", gmtime(&future_time)); |
360 | |
|
361 | 0 | if(mbedtls_x509write_crt_set_validity(&crt, current_timestamp, future_timestamp) != 0) { |
362 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
363 | 0 | "Setting 'not before' and 'not after' failed."); |
364 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
365 | 0 | goto cleanup; |
366 | 0 | } |
367 | | |
368 | 0 | if(mbedtls_x509write_crt_set_basic_constraints(&crt, 0, -1) != 0) { |
369 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
370 | 0 | "Setting basic constraints failed."); |
371 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
372 | 0 | goto cleanup; |
373 | 0 | } |
374 | | |
375 | | /* ECC certificates need keyAgreement for ECDH instead of keyEncipherment */ |
376 | 0 | unsigned int keyUsageFlags = keyTypeEC |
377 | 0 | ? (MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_NON_REPUDIATION |
378 | 0 | | MBEDTLS_X509_KU_KEY_AGREEMENT | MBEDTLS_X509_KU_KEY_CERT_SIGN) |
379 | 0 | : (MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_NON_REPUDIATION |
380 | 0 | | MBEDTLS_X509_KU_KEY_ENCIPHERMENT | MBEDTLS_X509_KU_DATA_ENCIPHERMENT |
381 | 0 | | MBEDTLS_X509_KU_KEY_CERT_SIGN | MBEDTLS_X509_KU_CRL_SIGN); |
382 | 0 | if(mbedtls_x509write_crt_set_key_usage(&crt, keyUsageFlags) != 0) { |
383 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
384 | 0 | "Setting key usage failed."); |
385 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
386 | 0 | goto cleanup; |
387 | 0 | } |
388 | | |
389 | 0 | mbedtls_asn1_sequence *ext_key_usage; |
390 | 0 | ext_key_usage = (mbedtls_asn1_sequence *)mbedtls_calloc(1, sizeof(mbedtls_asn1_sequence)); |
391 | 0 | ext_key_usage->buf.tag = MBEDTLS_ASN1_OID; |
392 | 0 | SET_OID(ext_key_usage->buf, MBEDTLS_OID_SERVER_AUTH); |
393 | 0 | ext_key_usage->next = (mbedtls_asn1_sequence *)mbedtls_calloc(1, sizeof(mbedtls_asn1_sequence)); |
394 | 0 | ext_key_usage->next->buf.tag = MBEDTLS_ASN1_OID; |
395 | 0 | SET_OID(ext_key_usage->next->buf, MBEDTLS_OID_CLIENT_AUTH); |
396 | |
|
397 | 0 | if(mbedtls_x509write_crt_set_ext_key_usage(&crt, ext_key_usage) != 0) { |
398 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
399 | 0 | "Setting extended key usage failed."); |
400 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
401 | 0 | mbedtls_free(ext_key_usage->next); |
402 | 0 | mbedtls_free(ext_key_usage); |
403 | 0 | goto cleanup; |
404 | 0 | } |
405 | | |
406 | 0 | mbedtls_free(ext_key_usage->next); |
407 | 0 | mbedtls_free(ext_key_usage); |
408 | |
|
409 | 0 | mbedtls_x509write_crt_set_subject_key(&crt, &key); |
410 | 0 | mbedtls_x509write_crt_set_issuer_key(&crt, &key); |
411 | | |
412 | | |
413 | | /* Write private key */ |
414 | 0 | if ((write_private_key(&key, certFormat, outPrivateKey)) != 0) { |
415 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
416 | 0 | "Create Certificate: Writing private key failed."); |
417 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
418 | 0 | goto cleanup; |
419 | 0 | } |
420 | | |
421 | | /* Write Certificate */ |
422 | 0 | if ((write_certificate(&crt, certFormat, outCertificate, |
423 | 0 | mbedtls_ctr_drbg_random, &ctr_drbg)) != 0) { |
424 | 0 | UA_LOG_ERROR(logger, UA_LOGCATEGORY_SECURECHANNEL, |
425 | 0 | "Create Certificate: Writing certificate failed."); |
426 | 0 | errRet = UA_STATUSCODE_BADINTERNALERROR; |
427 | 0 | goto cleanup; |
428 | 0 | } |
429 | | |
430 | 0 | mbedtls_ctr_drbg_free(&ctr_drbg); |
431 | 0 | mbedtls_entropy_free(&entropy); |
432 | 0 | mbedtls_x509write_crt_free(&crt); |
433 | 0 | mbedtls_pk_free(&key); |
434 | |
|
435 | 0 | cleanup: |
436 | 0 | mbedtls_ctr_drbg_free(&ctr_drbg); |
437 | 0 | mbedtls_entropy_free(&entropy); |
438 | 0 | mbedtls_x509write_crt_free(&crt); |
439 | 0 | mbedtls_pk_free(&key); |
440 | 0 | return errRet; |
441 | 0 | } |
442 | | |
443 | 0 | static int write_private_key(mbedtls_pk_context *key, UA_CertificateFormat keyFormat, UA_ByteString *outPrivateKey) { |
444 | 0 | int ret; |
445 | 0 | unsigned char output_buf[16000]; |
446 | 0 | unsigned char *c = output_buf; |
447 | 0 | size_t len = 0; |
448 | |
|
449 | 0 | memset(output_buf, 0, sizeof(output_buf)); |
450 | 0 | switch(keyFormat) { |
451 | 0 | case UA_CERTIFICATEFORMAT_DER: { |
452 | 0 | if((ret = mbedtls_pk_write_key_der(key, output_buf, sizeof(output_buf))) < 0) { |
453 | 0 | return ret; |
454 | 0 | } |
455 | | |
456 | 0 | len = (size_t)ret; |
457 | 0 | c = output_buf + sizeof(output_buf) - len; |
458 | 0 | break; |
459 | 0 | } |
460 | 0 | case UA_CERTIFICATEFORMAT_PEM: { |
461 | 0 | if((ret = mbedtls_pk_write_key_pem(key, output_buf, sizeof(output_buf))) != 0) { |
462 | 0 | return ret; |
463 | 0 | } |
464 | | |
465 | 0 | len = strlen((char *)output_buf); |
466 | 0 | break; |
467 | 0 | } |
468 | 0 | } |
469 | | |
470 | 0 | outPrivateKey->length = len; |
471 | 0 | UA_ByteString_allocBuffer(outPrivateKey, outPrivateKey->length); |
472 | 0 | memcpy(outPrivateKey->data, c, outPrivateKey->length); |
473 | |
|
474 | 0 | return 0; |
475 | 0 | } |
476 | | |
477 | | static int write_certificate(mbedtls_x509write_cert *crt, UA_CertificateFormat certFormat, |
478 | | UA_ByteString *outCertificate, int (*f_rng)(void *, unsigned char *, size_t), |
479 | 0 | void *p_rng) { |
480 | 0 | int ret; |
481 | 0 | unsigned char output_buf[4096]; |
482 | 0 | unsigned char *c = output_buf; |
483 | 0 | size_t len = 0; |
484 | |
|
485 | 0 | memset(output_buf, 0, sizeof(output_buf)); |
486 | 0 | switch(certFormat) { |
487 | 0 | case UA_CERTIFICATEFORMAT_DER: { |
488 | 0 | if((ret = mbedtls_x509write_crt_der(crt, output_buf, sizeof(output_buf), f_rng, p_rng)) < 0) { |
489 | 0 | return ret; |
490 | 0 | } |
491 | | |
492 | 0 | len = (size_t)ret; |
493 | 0 | c = output_buf + sizeof(output_buf) - len; |
494 | 0 | break; |
495 | 0 | } |
496 | 0 | case UA_CERTIFICATEFORMAT_PEM: { |
497 | 0 | if((ret = mbedtls_x509write_crt_pem(crt, output_buf, sizeof(output_buf), f_rng, p_rng)) < 0) { |
498 | 0 | return ret; |
499 | 0 | } |
500 | | |
501 | 0 | len = strlen((char *)output_buf); |
502 | 0 | break; |
503 | 0 | } |
504 | 0 | } |
505 | | |
506 | 0 | outCertificate->length = len; |
507 | 0 | UA_ByteString_allocBuffer(outCertificate, outCertificate->length); |
508 | 0 | memcpy(outCertificate->data, c, outCertificate->length); |
509 | |
|
510 | 0 | return 0; |
511 | 0 | } |
512 | | |
513 | | #if MBEDTLS_VERSION_NUMBER < 0x03030000 |
514 | | int mbedtls_x509write_crt_set_ext_key_usage(mbedtls_x509write_cert *ctx, |
515 | 0 | const mbedtls_asn1_sequence *exts) { |
516 | 0 | unsigned char buf[256]; |
517 | 0 | unsigned char *c = buf + sizeof(buf); |
518 | 0 | int ret; |
519 | 0 | size_t len = 0; |
520 | 0 | const mbedtls_asn1_sequence *last_ext = NULL; |
521 | 0 | const mbedtls_asn1_sequence *ext; |
522 | |
|
523 | 0 | memset(buf, 0, sizeof(buf)); |
524 | | |
525 | | /* We need at least one extension: SEQUENCE SIZE (1..MAX) OF KeyPurposeId */ |
526 | 0 | if(!exts) { |
527 | 0 | return MBEDTLS_ERR_X509_BAD_INPUT_DATA; |
528 | 0 | } |
529 | | |
530 | | /* Iterate over exts backwards, so we write them out in the requested order */ |
531 | 0 | while(last_ext != exts) { |
532 | 0 | for(ext = exts; ext->next != last_ext; ext = ext->next) { |
533 | 0 | } |
534 | 0 | if(ext->buf.tag != MBEDTLS_ASN1_OID) { |
535 | 0 | return MBEDTLS_ERR_X509_BAD_INPUT_DATA; |
536 | 0 | } |
537 | 0 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(&c, buf, ext->buf.p, ext->buf.len)); |
538 | 0 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, ext->buf.len)); |
539 | 0 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, buf, MBEDTLS_ASN1_OID)); |
540 | 0 | last_ext = ext; |
541 | 0 | } |
542 | | |
543 | 0 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); |
544 | 0 | MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, buf, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); |
545 | | |
546 | 0 | return mbedtls_x509write_crt_set_extension(ctx, MBEDTLS_OID_EXTENDED_KEY_USAGE, |
547 | 0 | MBEDTLS_OID_SIZE(MBEDTLS_OID_EXTENDED_KEY_USAGE), 1, c, len); |
548 | 0 | } |
549 | | |
550 | | #endif |
551 | | |
552 | 0 | static size_t mbedtls_get_san_list_deep(const mbedtls_write_san_list* sanlist) { |
553 | 0 | size_t ret = 0; |
554 | 0 | const mbedtls_write_san_list* cur = sanlist; |
555 | 0 | while (cur) { |
556 | 0 | ++ret; |
557 | 0 | cur = cur->next; |
558 | 0 | } |
559 | |
|
560 | 0 | return ret; |
561 | 0 | } |
562 | | |
563 | 0 | int mbedtls_x509write_crt_set_subject_alt_name(mbedtls_x509write_cert *ctx, const mbedtls_write_san_list* sanlist) { |
564 | 0 | int ret = 0; |
565 | 0 | size_t sandeep = 0; |
566 | 0 | const mbedtls_write_san_list* cur = sanlist; |
567 | 0 | unsigned char* buf; |
568 | 0 | unsigned char* pc; |
569 | 0 | size_t len; |
570 | 0 | size_t buflen = 0; |
571 | | |
572 | | /* How many alt names to be written */ |
573 | 0 | sandeep = mbedtls_get_san_list_deep(sanlist); |
574 | 0 | if (sandeep == 0) |
575 | 0 | return ret; |
576 | | |
577 | 0 | buflen = MBEDTLS_SAN_MAX_LEN * sandeep + sandeep; |
578 | 0 | buf = (unsigned char *)mbedtls_calloc(1, buflen); |
579 | 0 | if(!buf) |
580 | 0 | return MBEDTLS_ERR_ASN1_ALLOC_FAILED; |
581 | | |
582 | 0 | memset(buf, 0, buflen); |
583 | 0 | pc = buf + buflen; |
584 | |
|
585 | 0 | len = 0; |
586 | 0 | while(cur) { |
587 | 0 | switch (cur->node.type) { |
588 | 0 | case MBEDTLS_X509_SAN_DNS_NAME: |
589 | 0 | case MBEDTLS_X509_SAN_RFC822_NAME: |
590 | 0 | case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: |
591 | 0 | case MBEDTLS_X509_SAN_IP_ADDRESS: |
592 | 0 | MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, |
593 | 0 | mbedtls_asn1_write_raw_buffer(&pc, buf, (const unsigned char *)cur->node.host, |
594 | 0 | cur->node.hostlen)); |
595 | 0 | MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len(&pc, buf, cur->node.hostlen)); |
596 | 0 | MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_tag(&pc, buf, |
597 | 0 | MBEDTLS_ASN1_CONTEXT_SPECIFIC | cur->node.type)); |
598 | 0 | break; |
599 | 0 | default: |
600 | | /* Error out on an unsupported SAN */ |
601 | 0 | ret = MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE; |
602 | 0 | goto cleanup; |
603 | 0 | } |
604 | | |
605 | 0 | cur = cur->next; |
606 | 0 | } |
607 | | |
608 | 0 | MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len(&pc, buf, len)); |
609 | 0 | MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_tag(&pc, buf, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); |
610 | | |
611 | 0 | ret = mbedtls_x509write_crt_set_extension(ctx, MBEDTLS_OID_SUBJECT_ALT_NAME, |
612 | 0 | MBEDTLS_OID_SIZE(MBEDTLS_OID_SUBJECT_ALT_NAME), 0, buf + buflen - len, len); |
613 | |
|
614 | 0 | mbedtls_free(buf); |
615 | 0 | return ret; |
616 | | |
617 | 0 | cleanup: |
618 | | mbedtls_free(buf); |
619 | 0 | return ret; |
620 | 0 | } |
621 | | |
622 | | #endif |