From 93a9e32b7ef2270e4663edca558ac62cfbd578e5 Mon Sep 17 00:00:00 2001 From: Kevin Kane Date: Wed, 8 Feb 2017 18:03:43 -0800 Subject: [PATCH] Helpers to create key pairs, identity, and role certificates Changed the SSL adapter to use the MBEDTLS_OID_SIZE macro. Corrected a couple signed/unsigned issues in provisioningclient to fix warnings. Change-Id: I45e9723e57513a5281073e76b960622dae0cc15b Signed-off-by: Kevin Kane Reviewed-on: https://gerrit.iotivity.org/gerrit/17303 Reviewed-by: Alex Kelley Tested-by: jenkins-iotivity --- extlibs/mbedtls/config-iotivity.h | 15 + .../src/adapter_util/ca_adapter_net_ssl.c | 2 +- .../csdk/security/provisioning/sample/SConscript | 1 + .../security/provisioning/sample/certgenerator.cpp | 537 ++++++++++++++++++ .../provisioning/sample/provisioningclient.c | 4 +- resource/csdk/security/src/certhelpers.c | 13 +- resource/csdk/stack/SConscript | 3 +- resource/csdk/stack/include/occertutility.h | 179 ++++++ resource/csdk/stack/octbstack_product_secured.def | 5 + resource/csdk/stack/src/occertutility.c | 612 +++++++++++++++++++++ 10 files changed, 1363 insertions(+), 8 deletions(-) create mode 100644 resource/csdk/security/provisioning/sample/certgenerator.cpp create mode 100644 resource/csdk/stack/include/occertutility.h create mode 100644 resource/csdk/stack/src/occertutility.c diff --git a/extlibs/mbedtls/config-iotivity.h b/extlibs/mbedtls/config-iotivity.h index 8f2082c..38ebdc5 100644 --- a/extlibs/mbedtls/config-iotivity.h +++ b/extlibs/mbedtls/config-iotivity.h @@ -1382,6 +1382,21 @@ #define MBEDTLS_X509_RSASSA_PSS_SUPPORT /** + * \def MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT + * + * Enable parsing of all supported subtypes of the Subject Alternative Name + * extension. When enabled, the subject_alt_names field of mbedtls_x509_crt + * is defined as an mbedtls_x509_subject_alt_name_sequence, each element of + * which can describe a different subtype of the GeneralName choice as defined + * by the standard. + * + * Comment this macro to only support dNSName subtypes, and to define the + * subject_alt_names field as an mbedtls_x509_sequence. Any other subtypes will + * be ignored. This was the behavior in earlier versions. + */ +#define MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT + +/** * \def MBEDTLS_ZLIB_SUPPORT * * If set, the SSL/TLS module uses ZLIB to support compression and diff --git a/resource/csdk/connectivity/src/adapter_util/ca_adapter_net_ssl.c b/resource/csdk/connectivity/src/adapter_util/ca_adapter_net_ssl.c index 7fee2f1..5d84865 100644 --- a/resource/csdk/connectivity/src/adapter_util/ca_adapter_net_ssl.c +++ b/resource/csdk/connectivity/src/adapter_util/ca_adapter_net_ssl.c @@ -1874,7 +1874,7 @@ CAResult_t CAdecryptSsl(const CASecureEndpoint_t *sep, uint8_t *data, uint32_t d continue; } - if ((name->oid.len < (sizeof(MBEDTLS_OID_AT_CN) - 1) || + if ((name->oid.len < MBEDTLS_OID_SIZE(MBEDTLS_OID_AT_CN) || (0 != memcmp(MBEDTLS_OID_AT_CN, name->oid.p, name->oid.len)))) { continue; diff --git a/resource/csdk/security/provisioning/sample/SConscript b/resource/csdk/security/provisioning/sample/SConscript index b1d1bb4..edbc1aa 100644 --- a/resource/csdk/security/provisioning/sample/SConscript +++ b/resource/csdk/security/provisioning/sample/SConscript @@ -84,6 +84,7 @@ sampleserver_justworks = provisioning_sample_env.Program('sampleserver_justworks sampleserver_randompin = provisioning_sample_env.Program('sampleserver_randompin', 'sampleserver_randompin.cpp') sampleserver_mfg = provisioning_sample_env.Program('sampleserver_mfg', 'sampleserver_mfg.cpp') sampleserver_mvjustworks = provisioning_sample_env.Program('sampleserver_mvjustworks', 'sampleserver_mvjustworks.cpp') +certgenerator = provisioning_sample_env.Program('certgenerator', 'certgenerator.cpp') if provisioning_sample_env.get('MULTIPLE_OWNER') == '1': subownerclient = provisioning_sample_env.Program('subownerclient', 'subownerclient.c') diff --git a/resource/csdk/security/provisioning/sample/certgenerator.cpp b/resource/csdk/security/provisioning/sample/certgenerator.cpp new file mode 100644 index 0000000..7b3aa26 --- /dev/null +++ b/resource/csdk/security/provisioning/sample/certgenerator.cpp @@ -0,0 +1,537 @@ +//****************************************************************** +// +// Copyright 2017 Microsoft +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//****************************************************************** + +#include +#include +#include +#include +#include + +#include "ocstack.h" +#include "oic_malloc.h" +#include "ocrandom.h" +#include "occertutility.h" + +#ifdef HAVE_WINDOWS_H +#include +/** @todo stop-gap for naming issue. Windows.h does not like us to use ERROR */ +#ifdef ERROR +#undef ERROR +#endif //ERROR +#endif //HAVE_WINDOWS_H + +static long GetFileSize(FILE *f) +{ + long offset, size; + + offset = ftell(f); + + if (offset < 0) + { + return offset; + } + + if (fseek(f, 0, SEEK_END) < 0) + { + return -1; + } + + size = ftell(f); + + if (fseek(f, 0, SEEK_SET) < 0) + { + return -1; + } + + return size; +} + +static char *ReadLine(char *buf, size_t len) +{ + char *p; + int lenAsInt; + + if (len > 0xFFFFFFFF) + { + return NULL; + } + else + { + lenAsInt = (int)len; + } + + if (NULL == fgets(buf, lenAsInt, stdin)) + { + return NULL; + } + + /* Trim newline that fgets stores in the buffer. */ + for (p = buf; ((p - buf) < lenAsInt) && (*p != '\r') && (*p != '\n'); p++); + + *p = '\0'; + + return buf; +} + +typedef enum { + CA_CERT, + IDENTITY_CERT, + ROLE_CERT +} CertType; + +static void DoGenCertificate(CertType certType) +{ + OCStackResult res; + char subjKeyPairName[50]; + char issKeyPairName[50]; + char filename[70]; + char subject[100]; + char authority[100]; + OicUuid_t subjectUuid; + char notValidBefore[50]; + char notValidAfter[50]; + char *serial = NULL; + size_t serialLen = 0; + long fileLen = 0; + size_t bytesProcessed = 0; + std::vector publicKey; + std::vector privateKey; + std::vector issuerCert; + FILE *f = NULL; + time_t nowTimeT; + struct tm *now; + char *certificate = NULL; + size_t certificateLen = 0; + + printf("Subject key pair name (do not include .pub or .prv): "); + if (NULL == ReadLine(subjKeyPairName, sizeof(subjKeyPairName))) + { + printf("Failed to get key pair name!"); + goto exit; + } + + /* This sample only supports self-signed CAs. Intermediates can be created with + * the helper functions, though, by providing a different issuer private key and + * certificate. + */ + if (CA_CERT != certType) + { + printf("Issuer cert/key pair name (do not include .crt, .pub, or .prv): "); + if (NULL == ReadLine(issKeyPairName, sizeof(issKeyPairName))) + { + printf("Failed to get key pair name!"); + goto exit; + } + } + else + { + strcpy(issKeyPairName, subjKeyPairName); + } + + // -- Load public key -- + + sprintf(filename, "%s.pub", subjKeyPairName); + f = fopen(filename, "rb"); + if (NULL == f) + { + printf("Could not open public key!\n"); + goto exit; + } + + fileLen = GetFileSize(f); + if (fileLen < 0) + { + printf("Could not get size of public key!\n"); + goto exit; + } + + publicKey.resize(fileLen); + bytesProcessed = fread(publicKey.data(), 1, publicKey.size(), f); + if (bytesProcessed < publicKey.size()) + { + printf("Could not read public key! Got %zu, expected %zu\n", bytesProcessed, publicKey.size()); + goto exit; + } + + if (0 != fclose(f)) + { + printf("Warning: could not fclose public key\n"); + } + + // -- Load private key -- + sprintf(filename, "%s.prv", issKeyPairName); + f = fopen(filename, "rb"); + if (NULL == f) + { + printf("Could not open private key!\n"); + goto exit; + } + + fileLen = GetFileSize(f); + if (fileLen < 0) + { + printf("Could not get size of private key!\n"); + goto exit; + } + + privateKey.resize(fileLen); + bytesProcessed = fread(privateKey.data(), 1, privateKey.size(), f); + if (bytesProcessed < privateKey.size()) + { + printf("Could not read private key! Got %zu, expected %zu\n", bytesProcessed, privateKey.size()); + goto exit; + } + + if (0 != fclose(f)) + { + printf("Warning: could not fclose private key\n"); + } + + f = NULL; + + // -- Load issuer cert if applicable -- + if (CA_CERT != certType) + { + sprintf(filename, "%s.crt", issKeyPairName); + f = fopen(filename, "rb"); + if (NULL == f) + { + printf("Could not open issuer certificate file!\n"); + goto exit; + } + + fileLen = GetFileSize(f); + if (fileLen < 0) + { + printf("Could not get size of issuer certificate!\n"); + goto exit; + } + + issuerCert.resize(fileLen); + bytesProcessed = fread(issuerCert.data(), 1, issuerCert.size(), f); + if (bytesProcessed < issuerCert.size()) + { + printf("Could not read issuer certificate! Got %zu, expected %zu\n", bytesProcessed, issuerCert.size()); + goto exit; + } + + if (0 != fclose(f)) + { + printf("Warning: could not fclose issuer certificate\n"); + } + + f = NULL; + } + + // -- Prompt user for subject name -- + + if (CA_CERT == certType) + { + printf("Subject name as comma-separated list of RDN types and values\n"); + printf("e.g.: C=US, O=Open Connectivity Foundation, CN=Main CA : "); + if (NULL == ReadLine(subject, sizeof(subject))) + { + printf("Failed to get subject!\n"); + goto exit; + } + } + else + { + printf("Subject UUID in the RFC 4122 form (XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX): "); + if (NULL == ReadLine(subject, sizeof(subject))) + { + printf("Failed to get subject UUID!"); + goto exit; + } + + if (!OCConvertStringToUuid(subject, subjectUuid.id)) + { + printf("Failed to parse input UUID!\n"); + goto exit; + } + } + + // -- Compute validity to be ten years from current time -- + nowTimeT = time(NULL); + now = gmtime(&nowTimeT); + + if (0 == strftime(notValidBefore, sizeof(notValidBefore), "%Y%m%d%H%M%S", now)) + { + printf("Failed to format notValidBefore time!"); + goto exit; + } + + now->tm_year += 10; + + if (0 == strftime(notValidAfter, sizeof(notValidAfter), "%Y%m%d%H%M%S", now)) + { + printf("Failed to format notValidAfter time!"); + goto exit; + } + + // -- Generate random serial number. A real CA should record serial numbers as they are used, and -- + // -- make sure none are ever repeated! -- + + res = OCGenerateRandomSerialNumber(&serial, &serialLen); + if (OC_STACK_OK != res) + { + printf("Failed to generate a serial number!\n"); + goto exit; + } + + switch (certType) + { + case CA_CERT: + res = OCGenerateCACertificate( + subject, + publicKey.data(), + NULL, + privateKey.data(), + serial, + notValidBefore, + notValidAfter, + &certificate, + &certificateLen); + break; + + case IDENTITY_CERT: + res = OCGenerateIdentityCertificate( + &subjectUuid, + publicKey.data(), + issuerCert.data(), + privateKey.data(), + serial, + notValidBefore, + notValidAfter, + &certificate, + &certificateLen); + break; + + case ROLE_CERT: + printf("Role name: "); + /* We don't need subject's contents now, so reuse the buffer.*/ + if (NULL == ReadLine(subject, sizeof(subject))) + { + printf("Failed to read role name!\n"); + goto exit; + } + printf("Authority name (press ENTER for none): "); + if (NULL == ReadLine(authority, sizeof(authority))) + { + printf("Failed to read authority name!\n"); + goto exit; + } + res = OCGenerateRoleCertificate( + &subjectUuid, + publicKey.data(), + issuerCert.data(), + privateKey.data(), + serial, + notValidBefore, + notValidAfter, + subject, + ('\0' != *authority)?authority:NULL, + &certificate, + &certificateLen); + break; + } + + if (OC_STACK_OK != res) + { + printf("Failed to generate certificate!\n"); + goto exit; + } + + sprintf(filename, "%s.crt", subjKeyPairName); + f = fopen(filename, "wb"); + if (NULL == f) + { + printf("Failed to open certificate file for writing!\n"); + goto exit; + } + + bytesProcessed = fwrite(certificate, 1, certificateLen, f); + if (bytesProcessed < certificateLen) + { + printf("Failed to write certificate! Had %zu bytes, wrote %zu\n", certificateLen, bytesProcessed); + goto exit; + } + + if (0 != fclose(f)) + { + printf("Warning: failed to close certificate file\n"); + goto exit; + } + + f = NULL; + + printf("Wrote certificate file.\n"); + +exit: + + OICFree(serial); + OICFree(certificate); + if (NULL != f) + { + if (0 != fclose(f)) + { + printf("Warning: failed to fclose\n"); + } + } +} + +static void DoGenKeyPair() +{ + OCStackResult res = OC_STACK_OK; + char nameBuf[100]; + char *publicKey = NULL; + char *privateKey = NULL; + size_t publicKeyLen = 0; + size_t privateKeyLen = 0; + FILE *f = NULL; + + printf("Name the key pair: "); + if (NULL == ReadLine(nameBuf, sizeof(nameBuf))) + { + printf("Failed to read name!"); + return; + } + + res = OCGenerateKeyPair(&publicKey, &publicKeyLen, &privateKey, &privateKeyLen); + if (OC_STACK_OK == res) + { + char filename[120]; + size_t written; + + sprintf(filename, "%s.pub", nameBuf); + f = fopen(filename, "wb"); + if (NULL == f) + { + printf("Couldn't open %s to write public key!\n", filename); + goto exit; + } + + written = fwrite(publicKey, 1, publicKeyLen, f); + if (written < publicKeyLen) + { + printf("Failed to write public key! Had %zu, wrote %zu\n", publicKeyLen, written); + goto exit; + } + + if (0 != fclose(f)) + { + printf("Warning: failed to fclose. Errno: %d\n", errno); + } + + printf("Wrote public key.\n"); + + sprintf(filename, "%s.prv", nameBuf); + f = fopen(filename, "wb"); + if (NULL == f) + { + printf("Couldn't open %s to write private key!\n", filename); + goto exit; + } + written = fwrite(privateKey, 1, privateKeyLen, f); + if (written < privateKeyLen) + { + printf("Failed to write private key! Had %zu, wrote %zu\n", privateKeyLen, written); + goto exit; + } + + if (0 != fclose(f)) + { + printf("Warning: failed to fclose. Errno: %d\n", errno); + } + f = NULL; + + printf("Wrote private key.\n"); + } + else + { + printf("Failed!\n"); + } + +exit: + + OICFree(publicKey); + OICClearMemory(privateKey, privateKeyLen); + OICFree(privateKey); + if (NULL != f) + { + if (0 != fclose(f)) + { + printf("Warning: failed to fclose. errno: %d\n", errno); + } + } +} + +int main() +{ + for (;;) + { + int option; + + printf("-- Certificate Generator Sample Utility --\n\n"); + + printf(" 1. Generate a new key pair\n"); + printf(" 2. Generate a self-signed CA certificate (requires a key pair for the CA)\n"); + printf(" 3. Generate an identity certificate for a particular device UUID\n"); + printf(" (requires the CA's private key and certificate, and the device's public key)\n"); + printf(" 4. Generate a role certificate for a particular device UUID and role\n"); + printf(" (requires the CA's private key and certificate, and the device's public key)\n"); + + printf("\n"); + printf(" 0. Exit\n"); + printf("\n"); + printf("Select: "); + + if (scanf("%d", &option) < 1) + { + printf("Failed to read the option! Exiting.\n"); + return 0; + } + + while ('\n' != getchar()); /* Consume the rest of the line so it doesn't get fed to the next input. */ + + switch (option) + { + case 1: + DoGenKeyPair(); + break; + case 2: + DoGenCertificate(CA_CERT); + break; + case 3: + DoGenCertificate(IDENTITY_CERT); + break; + case 4: + DoGenCertificate(ROLE_CERT); + break; + case 0: + return 0; + default: + printf("Invalid option %d\n\n", option); + break; + } + } + + return 0; +} \ No newline at end of file diff --git a/resource/csdk/security/provisioning/sample/provisioningclient.c b/resource/csdk/security/provisioning/sample/provisioningclient.c index 3fea5b0..abe6849 100644 --- a/resource/csdk/security/provisioning/sample/provisioningclient.c +++ b/resource/csdk/security/provisioning/sample/provisioningclient.c @@ -243,7 +243,7 @@ static void getCsrCB(void* ctx, size_t nOfRes, OCPMGetCsrResult_t* arr, bool has "-----END CERTIFICATE REQUEST-----", arr[i].csr, arr[i].csrLen, - pemBuffer, + (unsigned char *)pemBuffer, sizeof(pemBuffer), &olen); if (ret < 0) @@ -254,7 +254,7 @@ static void getCsrCB(void* ctx, size_t nOfRes, OCPMGetCsrResult_t* arr, bool has } else { - OICStrcpyPartial(pemBuffer, sizeof(pemBuffer), arr[i].csr, arr[i].csrLen); + OICStrcpyPartial(pemBuffer, sizeof(pemBuffer), (const char *)arr[i].csr, arr[i].csrLen); } mbedtls_x509_csr_init(&csr); diff --git a/resource/csdk/security/src/certhelpers.c b/resource/csdk/security/src/certhelpers.c index ff3ba65..3f55801 100644 --- a/resource/csdk/security/src/certhelpers.c +++ b/resource/csdk/security/src/certhelpers.c @@ -23,23 +23,26 @@ #include "logger.h" #include #include +#include #include "oic_malloc.h" #include "oic_string.h" #include "cacommon.h" +#include "ocrandom.h" #include "cacommonutil.h" #include "ocpayload.h" #include "payload_logging.h" #include "pmutility.h" +#include "srmutility.h" // headers required for mbed TLS +#include "mbedtls/config.h" #include "mbedtls/platform.h" -#include "mbedtls/ssl.h" #include "mbedtls/entropy.h" #include "mbedtls/ctr_drbg.h" -#include "mbedtls/pkcs12.h" -#include "mbedtls/ssl_internal.h" #include "mbedtls/x509_csr.h" +#include "mbedtls/x509_crt.h" +#include "mbedtls/oid.h" #ifndef NDEBUG #include "mbedtls/debug.h" @@ -51,7 +54,9 @@ #endif #include -#define TAG "OIC_CSR" +#include "certhelpers.h" + +#define TAG "OIC_CERTHELPERS" /** * @def PERSONALIZATION_STRING diff --git a/resource/csdk/stack/SConscript b/resource/csdk/stack/SConscript index e9a9b24..d15e068 100644 --- a/resource/csdk/stack/SConscript +++ b/resource/csdk/stack/SConscript @@ -188,7 +188,8 @@ liboctbstack_src = [ OCTBSTACK_SRC + 'ocserverrequest.c', OCTBSTACK_SRC + 'occollection.c', OCTBSTACK_SRC + 'oicgroup.c', - OCTBSTACK_SRC + 'ocendpoint.c' + OCTBSTACK_SRC + 'ocendpoint.c', + OCTBSTACK_SRC + 'occertutility.c' ] if with_tcp == True: diff --git a/resource/csdk/stack/include/occertutility.h b/resource/csdk/stack/include/occertutility.h new file mode 100644 index 0000000..43c5bf8 --- /dev/null +++ b/resource/csdk/stack/include/occertutility.h @@ -0,0 +1,179 @@ +//****************************************************************** +// +// Copyright 2017 Microsoft +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//****************************************************************** + +#if defined(__WITH_TLS__) || defined(__WITH_DTLS__) + +#ifndef OCCERTUTILITY_H_ +#define OCCERTUTILITY_H_ + +#include "securevirtualresourcetypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Generate a random serial number suitable for use with a certificate. + * Callers must record used serial numbers and ensure serial numbers do not + * get repeated! + * + * @param[OUT] serial Pointer to a string that will be set to the location of the ASCII serial number. + * Memory for this buffer will be allocated internally; caller must call OICFree on serial + * when finished to free its memory. + * @param[OUT] serialLen Variable to receive size of serial, which will include the terminating NULL. + * + * @return OC_STACK_OK if generated successfully; error otherwise. + */ +OCStackResult OCGenerateRandomSerialNumber(char **serial, size_t *serialLen); + +/** + * Generate a new NIST P-256 elliptic curve key pair and return the public and private + * keys as PEM encoded strings. + * + * @param[OUT] publicKey Pointer to receive PEM encoded public key. Memory is allocated internally + * and must be freed by caller with OICFree. + * @param[OUT] publicKeyLen Variable to receive length of publicKey, which will include terminating NULL. + * @param[OUT] privateKey Pointer to receive PEM encoded private key. Memory is allocated internally + * and, when finished, caller should first call OICClearMemory to securely erase + * its contents and then freed with OICFree. Caller is responsible for ensuring + * this key remains protected and secret! + * @param[OUT] privateKeyLen Variable to receive length of privateKey, which will include terminating NULL. + */ +OCStackResult OCGenerateKeyPair(char **publicKey, size_t *publicKeyLen, + char **privateKey, size_t *privateKeyLen); + +/** + * Generate a certificate to act as a Certificate Authority (CA). + * + * @param subject Comma-separated list of RDN types and values: + * e.g. "C=US, O=Open Connectivity Foundation, CN=Main CA" + * @param subjectPublicKey Subject's public key in PEM format + * @param issuerCert Issuer's certificate in PEM format + * If this certificate will be self-signed, pass in NULL. + * @param issuerPrivateKey Issuer's private key in PEM format + * If self-signing (issuerCert is NULL), this must be the private + * key corresponding to subjectPublicKey. + * @param serial String containing the serial number in ASCII numerals: + * e.g., "12345". Caller must ensure each certificate issued by a + * CA has a unique serial number, and it is recommended to generate + * them randomly by producing 19 random bytes and converting them to + * a numerical value. See GenerateRandomSerialNumber(). + * @param notValidBefore The notValidBefore date in UTC for the certificate in the form YYYYMMDDhhmmss + * e.g., "20131231235959" for December 31st 2013 at 23:59:59 + * @param notValidAfter The notValidAfter date in UTC for the certificate in the form YYYYMMDDhhmmss + * e.g., "20140101010203" for January 1st 2014 at 01:02:03 + * @param[OUT] certificate Pointer to a buffer that will receive the PEM-encoded certificate. Memory will be + * allocated internally; caller must call OICFree on certificate when finished to free + * its memory. + * @param[OUT] certificateLen Variable to receive the size of certificate, which will include the terminating NULL. + * + * @return OC_STACK_OK if successful, error code otherwise + */ +OCStackResult OCGenerateCACertificate( + const char *subject, + const char *subjectPublicKey, + const char *issuerCert, + const char *issuerPrivateKey, + const char *serial, + const char *notValidBefore, + const char *notValidAfter, + char **certificate, + size_t *certificateLen); + +/** + * Generate a certificate for a device's identity. + * + * @param subjectUuid UUID for the device to use the certificate. + * @param subjectPublicKey Subject's public key in PEM format + * @param issuerCert Issuer's certificate in PEM format + * @param issuerPrivateKey Issuer's private key in PEM format + * @param serial String containing the serial number in ASCII numerals: + * e.g., "12345". Caller must ensure each certificate issued by a + * CA has a unique serial number, and it is recommended to generate + * them randomly by producing 19 random bytes and converting them to + * a numerical value. See GenerateRandomSerialNumber(). + * @param notValidBefore The notValidBefore date in UTC for the certificate in the form YYYYMMDDhhmmss + * e.g., "20131231235959" for December 31st 2013 at 23:59:59 + * @param notValidAfter The notValidAfter date in UTC for the certificate in the form YYYYMMDDhhmmss + * e.g., "20140101010203" for January 1st 2014 at 01:02:03 + * @param[OUT] certificate Pointer to a buffer that will receive the PEM-encoded certificate. Memory will be + * allocated internally; caller must call OICFree on certificate when finished to free + * its memory. + * @param[OUT] certificateLen Variable to receive the size of certificate, which will include the terminating NULL. + * + * @return OC_STACK_OK if successful, error code otherwise + */ +OCStackResult OCGenerateIdentityCertificate( + const OicUuid_t *subjectUuid, + const char *subjectPublicKey, + const char *issuerCert, + const char *issuerPrivateKey, + const char *serial, + const char *notValidBefore, + const char *notValidAfter, + char **certificate, + size_t *certificateLen); + +/** + * Generate a certificate for a device's role. + * + * @param subjectUuid UUID for the device to use the certificate. + * @param subjectPublicKey Subject's public key in PEM format + * @param issuerCert Issuer's certificate in PEM format + * @param issuerPrivateKey Issuer's private key in PEM format + * @param serial String containing the serial number in ASCII numerals: + * e.g., "12345". Caller must ensure each certificate issued by a + * CA has a unique serial number, and it is recommended to generate + * them randomly by producing 19 random bytes and converting them to + * a numerical value. See GenerateRandomSerialNumber(). + * @param notValidBefore The notValidBefore date in UTC for the certificate in the form YYYYMMDDhhmmss + * e.g., "20131231235959" for December 31st 2013 at 23:59:59 + * @param notValidAfter The notValidAfter date in UTC for the certificate in the form YYYYMMDDhhmmss + * e.g., "20140101010203" for January 1st 2014 at 01:02:03 + * @param role The role to encode in the certificate. + * @param authority The role authority to encode in the certificate. This field is optional; + * to omit, use NULL, and authority is construed to be the certificate issuer. + * @param[OUT] certificate Pointer to a buffer that will receive the PEM-encoded certificate. Memory will be + * allocated internally; caller must call OICFree on certificate when finished to free + * its memory. + * @param[OUT] certificateLen Variable to receive the size of certificate, which will include the terminating NULL. + * + * @return OC_STACK_OK if successful, error code otherwise + */ +OCStackResult OCGenerateRoleCertificate( + const OicUuid_t *subjectUuid, + const char *subjectPublicKey, + const char *issuerCert, + const char *issuerPrivateKey, + const char *serial, + const char *notValidBefore, + const char *notValidAfter, + const char *role, + const char *authority, + char **certificate, + size_t *certificateLen); + +#ifdef __cplusplus +} +#endif + +#endif /* OCCERTUTILITY_H_ */ + +#endif /* defined(__WITH_TLS__) || defined(__WITH_DTLS__) */ \ No newline at end of file diff --git a/resource/csdk/stack/octbstack_product_secured.def b/resource/csdk/stack/octbstack_product_secured.def index 4be4628..41c6049 100644 --- a/resource/csdk/stack/octbstack_product_secured.def +++ b/resource/csdk/stack/octbstack_product_secured.def @@ -23,6 +23,11 @@ OCDiscoverOwnedDevices OCDiscoverSingleDevice OCDiscoverUnownedDevices OCDoOwnershipTransfer +OCGenerateCACertificate +OCGenerateIdentityCertificate +OCGenerateKeyPair +OCGenerateRandomSerialNumber +OCGenerateRoleCertificate OCGetACLResource OCGetCredResource OCGetCSRResource diff --git a/resource/csdk/stack/src/occertutility.c b/resource/csdk/stack/src/occertutility.c new file mode 100644 index 0000000..2ee9de4 --- /dev/null +++ b/resource/csdk/stack/src/occertutility.c @@ -0,0 +1,612 @@ +/* ***************************************************************** + * + * Copyright 2017 Microsoft. All Rights Reserved. + * + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * *****************************************************************/ + +#if defined(__WITH_TLS__) || defined(__WITH_DTLS__) + +#include "iotivity_config.h" + +#include "logger.h" +#include +#include +#include +#include "oic_malloc.h" +#include "oic_string.h" +#include "cacommon.h" +#include "ocrandom.h" +#include "cacommonutil.h" + +#include "ocpayload.h" +#include "payload_logging.h" +#include "pmutility.h" +#include "srmutility.h" + +// headers required for mbed TLS +#include "mbedtls/config.h" +#include "mbedtls/platform.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/x509_csr.h" +#include "mbedtls/x509_crt.h" +#include "mbedtls/oid.h" + +#ifndef NDEBUG +#include "mbedtls/debug.h" +#include "mbedtls/version.h" +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif +#include + +#include "occertutility.h" + +#define TAG "OIC_OCCERTUTILITY" + +/** + * @def PERSONALIZATION_STRING + * @brief Personalization string for the mbedtls RNG + */ +#define PERSONALIZATION_STRING "IOTIVITY_RND" + +#define MAX_URI_QUERY MAX_URI_LENGTH + MAX_QUERY_LENGTH + +#define MAX_STRING_LEN 254 + +/* ASN.1 DER encoding of the EKU for identity certificates (1.3.6.1.4.1.44924.1.6) */ +static const unsigned char s_ekuIdentity[] = { 0x30, 0x0C, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xDE, 0x7C, 0x01, 0x06 }; + +/* ASN.1 DER encoding of the EKU for role certificates (1.3.6.1.4.1.44924.1.7) */ +static const unsigned char s_ekuRole[] = { 0x30, 0x0C, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xDE, 0x7C, 0x01, 0x07 }; + +/* ASN.1 DER encoding of the EKU for both identity and roles (for use by CAs) */ +static const unsigned char s_ekuCA[] = { 0x30, 0x18, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xDE, 0x7C, 0x01, 0x06, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xDE, 0x7C, 0x01, 0x07 }; + +/** + * Generates elliptic curve keypair. + * + * @param[out] pk mbedtls public key container + * + * @return 0 on success or <0 on error + */ +static int GenerateEccKeyPair(mbedtls_pk_context *pk) +{ + int ret = 0; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + + OIC_LOG_V(DEBUG, TAG, "In %s", __func__); + VERIFY_NON_NULL_RET(pk, TAG, "Param pk is NULL", -1); + + // Initialize the DRBG context + mbedtls_ctr_drbg_init(&ctr_drbg); + mbedtls_entropy_init(&entropy); + ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, + &entropy, (const unsigned char *)PERSONALIZATION_STRING, sizeof(PERSONALIZATION_STRING)); + + if (0 > ret) + { + OIC_LOG_V(ERROR, TAG, "Seed initialization failed! %d", ret); + OIC_LOG_V(DEBUG, TAG, "Out %s", __func__); + goto exit; + } + mbedtls_ctr_drbg_set_prediction_resistance(&ctr_drbg, MBEDTLS_CTR_DRBG_PR_ON); + ret = mbedtls_pk_setup(pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)); + if (0 > ret) + { + OIC_LOG_V(ERROR, TAG, "mbedtls_pk_setup error %d", ret); + OIC_LOG_V(DEBUG, TAG, "Out %s", __func__); + goto exit; + } + ret = mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, mbedtls_pk_ec(*pk), mbedtls_ctr_drbg_random, &ctr_drbg); + if (0 > ret) + { + OIC_LOG(ERROR, TAG, "mbedtls_ecp_gen_keypair error"); + OIC_LOG_V(DEBUG, TAG, "Out %s", __func__); + goto exit; + } + +exit: + + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + + OIC_LOG_V(DEBUG, TAG, "Out %s", __func__); + return 0; +} + +OCStackResult OCGenerateRandomSerialNumber(char **serial, size_t *serialLen) +{ + int ret = 0; + OCStackResult res = OC_STACK_ERROR; + unsigned char random[20]; /* Per RFC 5280, 20 octets is the maximum length of a serial number. */ + mbedtls_mpi serialMpi; + + VERIFY_NOT_NULL_RETURN(TAG, serial, ERROR, OC_STACK_INVALID_PARAM); + VERIFY_NOT_NULL_RETURN(TAG, serialLen, ERROR, OC_STACK_INVALID_PARAM); + + mbedtls_mpi_init(&serialMpi); + memset(serial, 0, sizeof(*serial)); + + VERIFY_SUCCESS(TAG, OCGetRandomBytes(random, sizeof(random)), ERROR); + + /* Per RFC 5280, 20 octets is the maximum length of a serial number. In ASN.1, if the highest-order + * bit is set it causes a padding octet to be written, which would be 21 and non-compliant. + * Therefore, always clear the highest-order bit. Integers in ASN.1 are always big-Endian. + */ + random[0] &= 0x7F; + + /* Import into a large integer object and then output as a string. */ + ret = mbedtls_mpi_read_binary(&serialMpi, random, sizeof(random)); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + + /* Get the needed string length and allocate. */ + ret = mbedtls_mpi_write_string(&serialMpi, 10, NULL, 0, serialLen); + VERIFY_SUCCESS(TAG, ret == MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL, ERROR); + *serial = OICCalloc(1, *serialLen); + VERIFY_NOT_NULL(TAG, *serial, ERROR); + + /* Do the write for real. */ + ret = mbedtls_mpi_write_string(&serialMpi, 10, *serial, *serialLen, serialLen); + VERIFY_SUCCESS(TAG, ret == 0, ERROR); + + res = OC_STACK_OK; + +exit: + + if (OC_STACK_OK != res) + { + OICFree(*serial); + *serial = NULL; + *serialLen = 0; + } + mbedtls_mpi_free(&serialMpi); + + return res; +} + +OCStackResult OCGenerateKeyPair(char **publicKey, size_t *publicKeyLen, + char **privateKey, size_t *privateKeyLen) +{ + int ret = 0; + mbedtls_pk_context keyPair; + unsigned char buf[2048]; + + mbedtls_pk_init(&keyPair); + + VERIFY_NOT_NULL_RETURN(TAG, publicKey, ERROR, OC_STACK_INVALID_PARAM); + VERIFY_NOT_NULL_RETURN(TAG, publicKeyLen, ERROR, OC_STACK_INVALID_PARAM); + VERIFY_NOT_NULL_RETURN(TAG, privateKey, ERROR, OC_STACK_INVALID_PARAM); + VERIFY_NOT_NULL_RETURN(TAG, privateKeyLen, ERROR, OC_STACK_INVALID_PARAM); + + *publicKey = NULL; + *publicKeyLen = 0; + *privateKey = NULL; + *privateKeyLen = 0; + + ret = OCInternalGenerateKeyPair(&keyPair); + if (ret != 0) + { + OIC_LOG_V(ERROR, TAG, "Failed to generate key pair: %d", ret); + goto exit; + } + + ret = mbedtls_pk_write_pubkey_pem(&keyPair, buf, sizeof(buf)); + if (ret != 0) + { + OIC_LOG_V(ERROR, TAG, "Failed to export public key as PEM: %d", ret); + goto exit; + } + + *publicKeyLen = strlen(buf) + 1; + *publicKey = OICCalloc(1, *publicKeyLen); + if (NULL == *publicKey) + { + OIC_LOG(ERROR, TAG, "Could not allocate memory for public key"); + ret = -1; + goto exit; + } + memcpy(*publicKey, buf, *publicKeyLen); + + ret = mbedtls_pk_write_key_pem(&keyPair, buf, sizeof(buf)); + if (ret != 0) + { + OIC_LOG_V(ERROR, TAG, "Failed to export private key as PEM: %d", ret); + goto exit; + } + + *privateKeyLen = strlen(buf) + 1; + *privateKey = OICCalloc(1, *privateKeyLen); + if (NULL == *privateKey) + { + OIC_LOG(ERROR, TAG, "Could not allocate memory for private key"); + ret = -1; + goto exit; + } + memcpy(*privateKey, buf, *privateKeyLen); + +exit: + + mbedtls_pk_free(&keyPair); + + OICClearMemory(buf, sizeof(buf)); + + if (ret != 0) + { + OICFree(*publicKey); + OICClearMemory(*privateKey, *privateKeyLen); + OICFree(*privateKey); + + *publicKey = NULL; + *publicKeyLen = 0; + *privateKey = NULL; + *privateKeyLen = 0; + + return OC_STACK_ERROR; + } + else + { + return OC_STACK_OK; + } +} + +typedef enum { + CERT_TYPE_CA, + CERT_TYPE_IDENTITY, + CERT_TYPE_ROLE +} CertificateType_t; + +static OCStackResult GenerateCertificate( + CertificateType_t certType, + const char *subject, + const char *subjectPublicKey, + const char *issuerCert, + const char *issuerPrivateKey, + const char *serial, + const char *notValidBefore, + const char *notValidAfter, + const char *role, + const char *authority, + OCByteString *certificate) +{ + OCStackResult res = OC_STACK_INVALID_PARAM; + int ret = 0; + mbedtls_x509write_cert outCertCtx; + mbedtls_pk_context subjKeyCtx; + mbedtls_pk_context issKeyCtx; + mbedtls_x509_crt issCertCtx; + mbedtls_mpi serialMpi; + mbedtls_x509_general_names names; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + + char buf[2048]; + + if (NULL == subjectPublicKey || NULL == issuerPrivateKey || NULL == subject || NULL == serial || + NULL == notValidBefore || NULL == notValidAfter) + { + return OC_STACK_INVALID_PARAM; + } + + mbedtls_x509write_crt_init(&outCertCtx); + mbedtls_pk_init(&subjKeyCtx); + mbedtls_pk_init(&issKeyCtx); + mbedtls_x509_crt_init(&issCertCtx); + mbedtls_mpi_init(&serialMpi); + memset(&names, 0, sizeof(names)); + mbedtls_ctr_drbg_init(&ctr_drbg); + mbedtls_entropy_init(&entropy); + memset(certificate, 0, sizeof(*certificate)); + + ret = mbedtls_mpi_read_string(&serialMpi, 10, serial); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + + ret = mbedtls_pk_parse_public_key(&subjKeyCtx, subjectPublicKey, strlen(subjectPublicKey) + 1); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + + ret = mbedtls_pk_parse_key(&issKeyCtx, issuerPrivateKey, strlen(issuerPrivateKey) + 1, NULL, 0); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + + /* If issuerCert is NULL, then the cert will be self-signed. */ + if (NULL != issuerCert) + { + ret = mbedtls_x509_crt_parse(&issCertCtx, issuerCert, strlen(issuerCert) + 1); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + } + + ret = mbedtls_x509write_crt_set_validity(&outCertCtx, notValidBefore, notValidAfter); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + + mbedtls_x509write_crt_set_version(&outCertCtx, MBEDTLS_X509_CRT_VERSION_3); + mbedtls_x509write_crt_set_md_alg(&outCertCtx, MBEDTLS_MD_SHA256); + + res = OC_STACK_ERROR; + + ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, + &entropy, PERSONALIZATION_STRING, sizeof(PERSONALIZATION_STRING)); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + mbedtls_ctr_drbg_set_prediction_resistance(&ctr_drbg, MBEDTLS_CTR_DRBG_PR_ON); + + ret = mbedtls_x509write_crt_set_serial(&outCertCtx, &serialMpi); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + + ret = mbedtls_x509write_crt_set_subject_name(&outCertCtx, subject); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + + if (NULL != issuerCert) + { + // mbedtls_x509_dn_gets returns the number of bytes written to buf. + ret = mbedtls_x509_dn_gets(buf, sizeof(buf), &issCertCtx.subject); + VERIFY_SUCCESS(TAG, 0 < ret, ERROR); + ret = mbedtls_x509write_crt_set_issuer_name(&outCertCtx, buf); + } + else + { + /* If self-signed, use the same contents of subject for the issuer name. */ + ret = mbedtls_x509write_crt_set_issuer_name(&outCertCtx, subject); + } + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + + mbedtls_x509write_crt_set_subject_key(&outCertCtx, &subjKeyCtx); + + mbedtls_x509write_crt_set_issuer_key(&outCertCtx, &issKeyCtx); + + if (certType == CERT_TYPE_CA) + { + ret = mbedtls_x509write_crt_set_basic_constraints(&outCertCtx, 1, -1); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + ret = mbedtls_x509write_crt_set_key_usage(&outCertCtx, + MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_CERT_SIGN); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + } + else + { + ret = mbedtls_x509write_crt_set_basic_constraints(&outCertCtx, 0, 0); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + ret = mbedtls_x509write_crt_set_key_usage(&outCertCtx, + MBEDTLS_X509_KU_DIGITAL_SIGNATURE | + MBEDTLS_X509_KU_KEY_ENCIPHERMENT | + MBEDTLS_X509_KU_DATA_ENCIPHERMENT | + MBEDTLS_X509_KU_KEY_AGREEMENT); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + } + + switch (certType) + { + case CERT_TYPE_ROLE: + ret = mbedtls_x509write_crt_set_extension(&outCertCtx, + MBEDTLS_OID_EXTENDED_KEY_USAGE, MBEDTLS_OID_SIZE(MBEDTLS_OID_EXTENDED_KEY_USAGE), + 0, + s_ekuRole, sizeof(s_ekuRole)); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + ret = snprintf(buf, sizeof(buf), "CN=%s%s%s", role, (NULL != authority) ? ",OU=" : "", (NULL != authority) ? authority : ""); + VERIFY_SUCCESS(TAG, ret < sizeof(buf), ERROR); + names.next = NULL; + names.general_name.name_type = MBEDTLS_X509_GENERALNAME_DIRECTORYNAME; + ret = mbedtls_x509_string_to_names(&names.general_name.directory_name, buf); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + + ret = mbedtls_x509write_crt_set_subject_alt_names(&outCertCtx, &names); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + break; + + case CERT_TYPE_IDENTITY: + ret = mbedtls_x509write_crt_set_extension(&outCertCtx, + MBEDTLS_OID_EXTENDED_KEY_USAGE, MBEDTLS_OID_SIZE(MBEDTLS_OID_EXTENDED_KEY_USAGE), + 0, + s_ekuIdentity, sizeof(s_ekuIdentity)); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + break; + + case CERT_TYPE_CA: + ret = mbedtls_x509write_crt_set_extension(&outCertCtx, + MBEDTLS_OID_EXTENDED_KEY_USAGE, MBEDTLS_OID_SIZE(MBEDTLS_OID_EXTENDED_KEY_USAGE), + 0, + s_ekuCA, sizeof(s_ekuCA)); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + break; + + default: + assert(false); + VERIFY_SUCCESS(TAG, false, ERROR); + } + + ret = mbedtls_x509write_crt_pem(&outCertCtx, buf, sizeof(buf), mbedtls_ctr_drbg_random, &ctr_drbg); + VERIFY_SUCCESS(TAG, 0 == ret, ERROR); + + certificate->len = strlen(buf) + 1; + certificate->bytes = (uint8_t *)OICCalloc(1, certificate->len); + VERIFY_NOT_NULL(TAG, certificate->bytes, ERROR); + memcpy(certificate->bytes, buf, certificate->len); + + res = OC_STACK_OK; + +exit: + + if (OC_STACK_OK != res) + { + OICFree(certificate->bytes); + certificate->bytes = NULL; + certificate->len = 0; + } + + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + mbedtls_asn1_free_named_data_list(&names.general_name.directory_name); + mbedtls_mpi_free(&serialMpi); + mbedtls_x509_crt_free(&issCertCtx); + mbedtls_pk_free(&issKeyCtx); + mbedtls_pk_free(&subjKeyCtx); + mbedtls_x509write_crt_free(&outCertCtx); + + return res; +} + +OCStackResult OCGenerateCACertificate( + const char *subject, + const char *subjectPublicKey, + const char *issuerCert, + const char *issuerPrivateKey, + const char *serial, + const char *notValidBefore, + const char *notValidAfter, + char **certificate, + size_t *certificateLen) +{ + OCStackResult res = OC_STACK_OK; + OCByteString byteStr = { 0 }; + + res = GenerateCertificate( + CERT_TYPE_CA, + subject, + subjectPublicKey, + issuerCert, + issuerPrivateKey, + serial, + notValidBefore, + notValidAfter, + NULL, + NULL, + &byteStr); + + if (OC_STACK_OK == res) + { + *certificate = byteStr.bytes; + *certificateLen = byteStr.len; + } + + return res; +} + +OCStackResult OCGenerateIdentityCertificate( + const OicUuid_t *subjectUuid, + const char *subjectPublicKey, + const char *issuerCert, + const char *issuerPrivateKey, + const char *serial, + const char *notValidBefore, + const char *notValidAfter, + char **certificate, + size_t *certificateLen) +{ + OCStackResult res = OC_STACK_OK; + OCByteString byteStr = { 0 }; + char uuidStr[UUID_STRING_SIZE] = { 0 } ; + char subject[sizeof(uuidStr) + sizeof(SUBJECT_PREFIX)] = { 0 } ; + + if (NULL == issuerCert) + { + return OC_STACK_INVALID_PARAM; + } + + if (!OCConvertUuidToString(subjectUuid->id, uuidStr)) + { + OIC_LOG(ERROR, TAG, "Could not convert UUID"); + return OC_STACK_INVALID_PARAM; + } + + if (snprintf(subject, sizeof(subject), "%s%s", SUBJECT_PREFIX, uuidStr) == sizeof(subject)) + { + OIC_LOG(ERROR, TAG, "Could not write subject string"); + return OC_STACK_INVALID_PARAM; + } + + res = GenerateCertificate( + CERT_TYPE_IDENTITY, + subject, + subjectPublicKey, + issuerCert, + issuerPrivateKey, + serial, + notValidBefore, + notValidAfter, + NULL, + NULL, + &byteStr); + + if (OC_STACK_OK == res) + { + *certificate = byteStr.bytes; + *certificateLen = byteStr.len; + } + + return res; +} + +OCStackResult OCGenerateRoleCertificate( + const OicUuid_t *subjectUuid, + const char *subjectPublicKey, + const char *issuerCert, + const char *issuerPrivateKey, + const char *serial, + const char *notValidBefore, + const char *notValidAfter, + const char *role, + const char *authority, + char **certificate, + size_t *certificateLen) +{ + OCStackResult res = OC_STACK_ERROR; + OCByteString byteStr; + char uuidStr[UUID_STRING_SIZE] = { 0 }; + char subject[sizeof(uuidStr) + sizeof(SUBJECT_PREFIX)] = { 0 }; + + memset(&byteStr, 0, sizeof(byteStr)); + + if (NULL == role || NULL == issuerCert) + { + return OC_STACK_INVALID_PARAM; + } + + if (!OCConvertUuidToString(subjectUuid->id, uuidStr)) + { + OIC_LOG(ERROR, TAG, "Could not convert UUID"); + return OC_STACK_INVALID_PARAM; + } + + if (snprintf(subject, sizeof(subject), "%s%s", SUBJECT_PREFIX, uuidStr) == sizeof(subject)) + { + OIC_LOG(ERROR, TAG, "Could not write subject string"); + return OC_STACK_INVALID_PARAM; + } + + res = GenerateCertificate( + CERT_TYPE_ROLE, + subject, + subjectPublicKey, + issuerCert, + issuerPrivateKey, + serial, + notValidBefore, + notValidAfter, + role, + authority, + &byteStr); + + if (OC_STACK_OK == res) + { + *certificate = byteStr.bytes; + *certificateLen = byteStr.len; + } + + return res; +} + +#endif /* defined(__WITH_TLS__) || defined(__WITH_DTLS__) */ \ No newline at end of file -- 2.7.4