Helpers to create key pairs, identity, and role certificates
authorKevin Kane <kkane@microsoft.com>
Thu, 9 Feb 2017 02:03:43 +0000 (18:03 -0800)
committerKevin Kane <kkane@microsoft.com>
Tue, 28 Feb 2017 17:21:52 +0000 (17:21 +0000)
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 <kkane@microsoft.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/17303
Reviewed-by: Alex Kelley <alexke@microsoft.com>
Tested-by: jenkins-iotivity <jenkins@iotivity.org>
extlibs/mbedtls/config-iotivity.h
resource/csdk/connectivity/src/adapter_util/ca_adapter_net_ssl.c
resource/csdk/security/provisioning/sample/SConscript
resource/csdk/security/provisioning/sample/certgenerator.cpp [new file with mode: 0644]
resource/csdk/security/provisioning/sample/provisioningclient.c
resource/csdk/security/src/certhelpers.c
resource/csdk/stack/SConscript
resource/csdk/stack/include/occertutility.h [new file with mode: 0644]
resource/csdk/stack/octbstack_product_secured.def
resource/csdk/stack/src/occertutility.c [new file with mode: 0644]

index 8f2082c..38ebdc5 100644 (file)
 #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
index 7fee2f1..5d84865 100644 (file)
@@ -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;
index b1d1bb4..edbc1aa 100644 (file)
@@ -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 (file)
index 0000000..7b3aa26
--- /dev/null
@@ -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 <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <vector>
+
+#include "ocstack.h"
+#include "oic_malloc.h"
+#include "ocrandom.h"
+#include "occertutility.h"
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+/** @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<char> publicKey;
+    std::vector<char> privateKey;
+    std::vector<char> 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
index 3fea5b0..abe6849 100644 (file)
@@ -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);
index ff3ba65..3f55801 100644 (file)
 #include "logger.h"
 #include <stddef.h>
 #include <string.h>
+#include <assert.h>
 #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 <fcntl.h>
 
-#define TAG "OIC_CSR"
+#include "certhelpers.h"
+
+#define TAG "OIC_CERTHELPERS"
 
 /**
  * @def PERSONALIZATION_STRING
index e9a9b24..d15e068 100644 (file)
@@ -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 (file)
index 0000000..43c5bf8
--- /dev/null
@@ -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
index 4be4628..41c6049 100644 (file)
@@ -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 (file)
index 0000000..2ee9de4
--- /dev/null
@@ -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 <stddef.h>
+#include <string.h>
+#include <assert.h>
+#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 <unistd.h>
+#endif
+#include <fcntl.h>
+
+#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