text: /*
   *  Certificate generation and signing
   *
   *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
   *  SPDX-License-Identifier: Apache-2.0
   *
   *  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.
   *
   *  This file is part of mbed TLS (https://tls.mbed.org)
   */
  
  #if !defined(MBEDTLS_CONFIG_FILE)
  #include "mbedtls/config.h"
  #else
  #include MBEDTLS_CONFIG_FILE
  #endif
  
  #if defined(MBEDTLS_PLATFORM_C)
  #include "mbedtls/platform.h"
  #else
  #include <stdio.h>
  #define mbedtls_printf     printf
  #endif
  
  #if !defined(MBEDTLS_X509_CRT_WRITE_C) || \
      !defined(MBEDTLS_X509_CRT_PARSE_C) || !defined(MBEDTLS_FS_IO) || \
      !defined(MBEDTLS_ENTROPY_C) || !defined(MBEDTLS_CTR_DRBG_C) || \
      !defined(MBEDTLS_ERROR_C) || !defined(MBEDTLS_SHA256_C) || \
      !defined(MBEDTLS_PEM_WRITE_C)
  int main( void )
  {
      mbedtls_printf( "MBEDTLS_X509_CRT_WRITE_C and/or MBEDTLS_X509_CRT_PARSE_C and/or "
              "MBEDTLS_FS_IO and/or MBEDTLS_SHA256_C and/or "
              "MBEDTLS_ENTROPY_C and/or MBEDTLS_CTR_DRBG_C and/or "
              "MBEDTLS_ERROR_C not defined.\n");
      return( 0 );
  }
  #else
  
  #include "mbedtls/x509_crt.h"
  #include "mbedtls/x509_csr.h"
  #include "mbedtls/entropy.h"
  #include "mbedtls/ctr_drbg.h"
  #include "mbedtls/error.h"
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  
  #if defined(MBEDTLS_X509_CSR_PARSE_C)
  #define USAGE_CSR                                                           \
      "    request_file=%%s     default: (empty)\n"                           \
      "                        If request_file is specified, subject_key,\n"  \
      "                        subject_pwd and subject_name are ignored!\n"
  #else
  #define USAGE_CSR ""
  #endif /* MBEDTLS_X509_CSR_PARSE_C */
  
  #define DFL_ISSUER_CRT          ""
  #define DFL_REQUEST_FILE        ""
  #define DFL_SUBJECT_KEY         "subject.key"
  #define DFL_ISSUER_KEY          "ca.key"
  #define DFL_SUBJECT_PWD         ""
  #define DFL_ISSUER_PWD          ""
  #define DFL_OUTPUT_FILENAME     "cert.crt"
  #define DFL_SUBJECT_NAME        "CN=Cert,O=mbed TLS,C=UK"
  #define DFL_ISSUER_NAME         "CN=CA,O=mbed TLS,C=UK"
  #define DFL_NOT_BEFORE          "20010101000000"
  #define DFL_NOT_AFTER           "20301231235959"
  #define DFL_SERIAL              "1"
  #define DFL_SELFSIGN            0
  #define DFL_IS_CA               0
  #define DFL_MAX_PATHLEN         -1
  #define DFL_KEY_USAGE           0
  #define DFL_NS_CERT_TYPE        0
  
  #define USAGE \
      "\n usage: cert_write param=<>...\n"                \
      "\n acceptable parameters:\n"                       \
      USAGE_CSR                                           \
      "    subject_key=%%s      default: subject.key\n"   \
      "    subject_pwd=%%s      default: (empty)\n"       \
      "    subject_name=%%s     default: CN=Cert,O=mbed TLS,C=UK\n"   \
      "\n"                                                \
      "    issuer_crt=%%s       default: (empty)\n"       \
      "                        If issuer_crt is specified, issuer_name is\n"  \
      "                        ignored!\n"                \
      "    issuer_name=%%s      default: CN=CA,O=mbed TLS,C=UK\n"     \
      "\n"                                                \
      "    selfsign=%%d         default: 0 (false)\n"     \
      "                        If selfsign is enabled, issuer_name and\n" \
      "                        issuer_key are required (issuer_crt and\n" \
      "                        subject_* are ignored\n"   \
      "    issuer_key=%%s       default: ca.key\n"        \
      "    issuer_pwd=%%s       default: (empty)\n"       \
      "    output_file=%%s      default: cert.crt\n"      \
      "    serial=%%s           default: 1\n"             \
      "    not_before=%%s       default: 20010101000000\n"\
      "    not_after=%%s        default: 20301231235959\n"\
      "    is_ca=%%d            default: 0 (disabled)\n"  \
      "    max_pathlen=%%d      default: -1 (none)\n"     \
      "    key_usage=%%s        default: (empty)\n"       \
      "                        Comma-separated-list of values:\n"     \
      "                          digital_signature\n"     \
      "                          non_repudiation\n"       \
      "                          key_encipherment\n"      \
      "                          data_encipherment\n"     \
      "                          key_agreement\n"         \
      "                          key_cert_sign\n"  \
      "                          crl_sign\n"              \
      "    ns_cert_type=%%s     default: (empty)\n"       \
      "                        Comma-separated-list of values:\n"     \
      "                          ssl_client\n"            \
      "                          ssl_server\n"            \
      "                          email\n"                 \
      "                          object_signing\n"        \
      "                          ssl_ca\n"                \
      "                          email_ca\n"              \
      "                          object_signing_ca\n"     \
      "\n"
  
  /*
   * global options
   */
  struct options
  {
      const char *issuer_crt;     /* filename of the issuer certificate   */
      const char *request_file;   /* filename of the certificate request  */
      const char *subject_key;    /* filename of the subject key file     */
      const char *issuer_key;     /* filename of the issuer key file      */
      const char *subject_pwd;    /* password for the subject key file    */
      const char *issuer_pwd;     /* password for the issuer key file     */
      const char *output_file;    /* where to store the constructed key file  */
      const char *subject_name;   /* subject name for certificate         */
      const char *issuer_name;    /* issuer name for certificate          */
      const char *not_before;     /* validity period not before           */
      const char *not_after;      /* validity period not after            */
      const char *serial;         /* serial number string                 */
      int selfsign;               /* selfsign the certificate             */
      int is_ca;                  /* is a CA certificate                  */
      int max_pathlen;            /* maximum CA path length               */
      unsigned char key_usage;    /* key usage flags                      */
      unsigned char ns_cert_type; /* NS cert type                         */
  } opt;
  
  int write_certificate( mbedtls_x509write_cert *crt, const char *output_file,
                         int (*f_rng)(void *, unsigned char *, size_t),
                         void *p_rng )
  {
      int ret;
      FILE *f;
      unsigned char output_buf[4096];
      size_t len = 0;
  
      memset( output_buf, 0, 4096 );
      if( ( ret = mbedtls_x509write_crt_pem( crt, output_buf, 4096, f_rng, p_rng ) ) < 0 )
          return( ret );
  
      len = strlen( (char *) output_buf );
  
      if( ( f = fopen( output_file, "w" ) ) == NULL )
          return( -1 );
  
      if( fwrite( output_buf, 1, len, f ) != len )
      {
          fclose( f );
          return( -1 );
      }
  
      fclose( f );
  
      return( 0 );
  }
  
  int main( int argc, char *argv[] )
  {
      int ret = 0;
      mbedtls_x509_crt issuer_crt;
      mbedtls_pk_context loaded_issuer_key, loaded_subject_key;
      mbedtls_pk_context *issuer_key = &loaded_issuer_key,
                  *subject_key = &loaded_subject_key;
      char buf[1024];
      char issuer_name[256];
      int i;
      char *p, *q, *r;
  #if defined(MBEDTLS_X509_CSR_PARSE_C)
      char subject_name[256];
      mbedtls_x509_csr csr;
  #endif
      mbedtls_x509write_cert crt;
      mbedtls_mpi serial;
      mbedtls_entropy_context entropy;
      mbedtls_ctr_drbg_context ctr_drbg;
      const char *pers = "crt example app";
  
      /*
       * Set to sane values
       */
      mbedtls_x509write_crt_init( &crt );
      mbedtls_x509write_crt_set_md_alg( &crt, MBEDTLS_MD_SHA256 );
      mbedtls_pk_init( &loaded_issuer_key );
      mbedtls_pk_init( &loaded_subject_key );
      mbedtls_mpi_init( &serial );
      mbedtls_ctr_drbg_init( &ctr_drbg );
  #if defined(MBEDTLS_X509_CSR_PARSE_C)
      mbedtls_x509_csr_init( &csr );
  #endif
      mbedtls_x509_crt_init( &issuer_crt );
      memset( buf, 0, 1024 );
  
      if( argc == 0 )
      {
      usage:
          mbedtls_printf( USAGE );
          ret = 1;
          goto exit;
      }
  
      opt.issuer_crt          = DFL_ISSUER_CRT;
      opt.request_file        = DFL_REQUEST_FILE;
      opt.subject_key         = DFL_SUBJECT_KEY;
      opt.issuer_key          = DFL_ISSUER_KEY;
      opt.subject_pwd         = DFL_SUBJECT_PWD;
      opt.issuer_pwd          = DFL_ISSUER_PWD;
      opt.output_file         = DFL_OUTPUT_FILENAME;
      opt.subject_name        = DFL_SUBJECT_NAME;
      opt.issuer_name         = DFL_ISSUER_NAME;
      opt.not_before          = DFL_NOT_BEFORE;
      opt.not_after           = DFL_NOT_AFTER;
      opt.serial              = DFL_SERIAL;
      opt.selfsign            = DFL_SELFSIGN;
      opt.is_ca               = DFL_IS_CA;
      opt.max_pathlen         = DFL_MAX_PATHLEN;
      opt.key_usage           = DFL_KEY_USAGE;
      opt.ns_cert_type        = DFL_NS_CERT_TYPE;
  
      for( i = 1; i < argc; i++ )
      {
  
          p = argv[i];
          if( ( q = strchr( p, '=' ) ) == NULL )
              goto usage;
          *q++ = '\0';
  
          if( strcmp( p, "request_file" ) == 0 )
              opt.request_file = q;
          else if( strcmp( p, "subject_key" ) == 0 )
              opt.subject_key = q;
          else if( strcmp( p, "issuer_key" ) == 0 )
              opt.issuer_key = q;
          else if( strcmp( p, "subject_pwd" ) == 0 )
              opt.subject_pwd = q;
          else if( strcmp( p, "issuer_pwd" ) == 0 )
              opt.issuer_pwd = q;
          else if( strcmp( p, "issuer_crt" ) == 0 )
              opt.issuer_crt = q;
          else if( strcmp( p, "output_file" ) == 0 )
              opt.output_file = q;
          else if( strcmp( p, "subject_name" ) == 0 )
          {
              opt.subject_name = q;
          }
          else if( strcmp( p, "issuer_name" ) == 0 )
          {
              opt.issuer_name = q;
          }
          else if( strcmp( p, "not_before" ) == 0 )
          {
              opt.not_before = q;
          }
          else if( strcmp( p, "not_after" ) == 0 )
          {
              opt.not_after = q;
          }
          else if( strcmp( p, "serial" ) == 0 )
          {
              opt.serial = q;
          }
          else if( strcmp( p, "selfsign" ) == 0 )
          {
              opt.selfsign = atoi( q );
              if( opt.selfsign < 0 || opt.selfsign > 1 )
                  goto usage;
          }
          else if( strcmp( p, "is_ca" ) == 0 )
          {
              opt.is_ca = atoi( q );
              if( opt.is_ca < 0 || opt.is_ca > 1 )
                  goto usage;
          }
          else if( strcmp( p, "max_pathlen" ) == 0 )
          {
              opt.max_pathlen = atoi( q );
              if( opt.max_pathlen < -1 || opt.max_pathlen > 127 )
                  goto usage;
          }
          else if( strcmp( p, "key_usage" ) == 0 )
          {
              while( q != NULL )
              {
                  if( ( r = strchr( q, ',' ) ) != NULL )
                      *r++ = '\0';
  
                  if( strcmp( q, "digital_signature" ) == 0 )
                      opt.key_usage |= MBEDTLS_X509_KU_DIGITAL_SIGNATURE;
                  else if( strcmp( q, "non_repudiation" ) == 0 )
                      opt.key_usage |= MBEDTLS_X509_KU_NON_REPUDIATION;
                  else if( strcmp( q, "key_encipherment" ) == 0 )
                      opt.key_usage |= MBEDTLS_X509_KU_KEY_ENCIPHERMENT;
                  else if( strcmp( q, "data_encipherment" ) == 0 )
                      opt.key_usage |= MBEDTLS_X509_KU_DATA_ENCIPHERMENT;
                  else if( strcmp( q, "key_agreement" ) == 0 )
                      opt.key_usage |= MBEDTLS_X509_KU_KEY_AGREEMENT;
                  else if( strcmp( q, "key_cert_sign" ) == 0 )
                      opt.key_usage |= MBEDTLS_X509_KU_KEY_CERT_SIGN;
                  else if( strcmp( q, "crl_sign" ) == 0 )
                      opt.key_usage |= MBEDTLS_X509_KU_CRL_SIGN;
                  else
                      goto usage;
  
                  q = r;
              }
          }
          else if( strcmp( p, "ns_cert_type" ) == 0 )
          {
              while( q != NULL )
              {
                  if( ( r = strchr( q, ',' ) ) != NULL )
                      *r++ = '\0';
  
                  if( strcmp( q, "ssl_client" ) == 0 )
                      opt.ns_cert_type |= MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT;
                  else if( strcmp( q, "ssl_server" ) == 0 )
                      opt.ns_cert_type |= MBEDTLS_X509_NS_CERT_TYPE_SSL_SERVER;
                  else if( strcmp( q, "email" ) == 0 )
                      opt.ns_cert_type |= MBEDTLS_X509_NS_CERT_TYPE_EMAIL;
                  else if( strcmp( q, "object_signing" ) == 0 )
                      opt.ns_cert_type |= MBEDTLS_X509_NS_CERT_TYPE_OBJECT_SIGNING;
                  else if( strcmp( q, "ssl_ca" ) == 0 )
                      opt.ns_cert_type |= MBEDTLS_X509_NS_CERT_TYPE_SSL_CA;
                  else if( strcmp( q, "email_ca" ) == 0 )
                      opt.ns_cert_type |= MBEDTLS_X509_NS_CERT_TYPE_EMAIL_CA;
                  else if( strcmp( q, "object_signing_ca" ) == 0 )
                      opt.ns_cert_type |= MBEDTLS_X509_NS_CERT_TYPE_OBJECT_SIGNING_CA;
                  else
                      goto usage;
  
                  q = r;
              }
          }
          else
              goto usage;
      }
  
      mbedtls_printf("\n");
  
      /*
       * 0. Seed the PRNG
       */
      mbedtls_printf( "  . Seeding the random number generator..." );
      fflush( stdout );
  
      mbedtls_entropy_init( &entropy );
      if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy,
                                 (const unsigned char *) pers,
                                 strlen( pers ) ) ) != 0 )
      {
          mbedtls_strerror( ret, buf, 1024 );
          mbedtls_printf( " failed\n  !  mbedtls_ctr_drbg_seed returned %d - %s\n", ret, buf );
          goto exit;
      }
  
      mbedtls_printf( " ok\n" );
  
      // Parse serial to MPI
      //
      mbedtls_printf( "  . Reading serial number..." );
      fflush( stdout );
  
      if( ( ret = mbedtls_mpi_read_string( &serial, 10, opt.serial ) ) != 0 )
      {
          mbedtls_strerror( ret, buf, 1024 );
          mbedtls_printf( " failed\n  !  mbedtls_mpi_read_string returned -0x%02x - %s\n\n", -ret, buf );
          goto exit;
      }
  
      mbedtls_printf( " ok\n" );
  
      // Parse issuer certificate if present
      //
      if( !opt.selfsign && strlen( opt.issuer_crt ) )
      {
          /*
           * 1.0.a. Load the certificates
           */
          mbedtls_printf( "  . Loading the issuer certificate ..." );
          fflush( stdout );
  
          if( ( ret = mbedtls_x509_crt_parse_file( &issuer_crt, opt.issuer_crt ) ) != 0 )
          {
              mbedtls_strerror( ret, buf, 1024 );
              mbedtls_printf( " failed\n  !  mbedtls_x509_crt_parse_file returned -0x%02x - %s\n\n", -ret, buf );
              goto exit;
          }
  
          ret = mbedtls_x509_dn_gets( issuer_name, sizeof(issuer_name),
                                   &issuer_crt.subject );
          if( ret < 0 )
          {
              mbedtls_strerror( ret, buf, 1024 );
              mbedtls_printf( " failed\n  !  mbedtls_x509_dn_gets returned -0x%02x - %s\n\n", -ret, buf );
              goto exit;
          }
  
          opt.issuer_name = issuer_name;
  
          mbedtls_printf( " ok\n" );
      }
  
  #if defined(MBEDTLS_X509_CSR_PARSE_C)
      // Parse certificate request if present
      //
      if( !opt.selfsign && strlen( opt.request_file ) )
      {
          /*
           * 1.0.b. Load the CSR
           */
          mbedtls_printf( "  . Loading the certificate request ..." );
          fflush( stdout );
  
          if( ( ret = mbedtls_x509_csr_parse_file( &csr, opt.request_file ) ) != 0 )
          {
              mbedtls_strerror( ret, buf, 1024 );
              mbedtls_printf( " failed\n  !  mbedtls_x509_csr_parse_file returned -0x%02x - %s\n\n", -ret, buf );
              goto exit;
          }
  
          ret = mbedtls_x509_dn_gets( subject_name, sizeof(subject_name),
                                   &csr.subject );
          if( ret < 0 )
          {
              mbedtls_strerror( ret, buf, 1024 );
              mbedtls_printf( " failed\n  !  mbedtls_x509_dn_gets returned -0x%02x - %s\n\n", -ret, buf );
              goto exit;
          }
  
          opt.subject_name = subject_name;
          subject_key = &csr.pk;
  
          mbedtls_printf( " ok\n" );
      }
  #endif /* MBEDTLS_X509_CSR_PARSE_C */
  
      /*
       * 1.1. Load the keys
       */
      if( !opt.selfsign && !strlen( opt.request_file ) )
      {
          mbedtls_printf( "  . Loading the subject key ..." );
          fflush( stdout );
  
          ret = mbedtls_pk_parse_keyfile( &loaded_subject_key, opt.subject_key,
                                   opt.subject_pwd );
          if( ret != 0 )
          {
              mbedtls_strerror( ret, buf, 1024 );
              mbedtls_printf( " failed\n  !  mbedtls_pk_parse_keyfile returned -0x%02x - %s\n\n", -ret, buf );
              goto exit;
          }
  
          mbedtls_printf( " ok\n" );
      }
  
      mbedtls_printf( "  . Loading the issuer key ..." );
      fflush( stdout );
  
      ret = mbedtls_pk_parse_keyfile( &loaded_issuer_key, opt.issuer_key,
                               opt.issuer_pwd );
      if( ret != 0 )
      {
          mbedtls_strerror( ret, buf, 1024 );
          mbedtls_printf( " failed\n  !  mbedtls_pk_parse_keyfile returned -x%02x - %s\n\n", -ret, buf );
          goto exit;
      }
  
      // Check if key and issuer certificate match
      //
      if( strlen( opt.issuer_crt ) )
      {
          if( !mbedtls_pk_can_do( &issuer_crt.pk, MBEDTLS_PK_RSA ) ||
              mbedtls_mpi_cmp_mpi( &mbedtls_pk_rsa( issuer_crt.pk )->N,
                           &mbedtls_pk_rsa( *issuer_key )->N ) != 0 ||
              mbedtls_mpi_cmp_mpi( &mbedtls_pk_rsa( issuer_crt.pk )->E,
                           &mbedtls_pk_rsa( *issuer_key )->E ) != 0 )
          {
              mbedtls_printf( " failed\n  !  issuer_key does not match issuer certificate\n\n" );
              ret = -1;
              goto exit;
          }
      }
  
      mbedtls_printf( " ok\n" );
  
      if( opt.selfsign )
      {
          opt.subject_name = opt.issuer_name;
          subject_key = issuer_key;
      }
  
      mbedtls_x509write_crt_set_subject_key( &crt, subject_key );
      mbedtls_x509write_crt_set_issuer_key( &crt, issuer_key );
  
      /*
       * 1.0. Check the names for validity
       */
      if( ( ret = mbedtls_x509write_crt_set_subject_name( &crt, opt.subject_name ) ) != 0 )
      {
          mbedtls_strerror( ret, buf, 1024 );
          mbedtls_printf( " failed\n  !  mbedtls_x509write_crt_set_subject_name returned -0x%02x - %s\n\n", -ret, buf );
          goto exit;
      }
  
      if( ( ret = mbedtls_x509write_crt_set_issuer_name( &crt, opt.issuer_name ) ) != 0 )
      {
          mbedtls_strerror( ret, buf, 1024 );
          mbedtls_printf( " failed\n  !  mbedtls_x509write_crt_set_issuer_name returned -0x%02x - %s\n\n", -ret, buf );
          goto exit;
      }
  
      mbedtls_printf( "  . Setting certificate values ..." );
      fflush( stdout );
  
      ret = mbedtls_x509write_crt_set_serial( &crt, &serial );
      if( ret != 0 )
      {
          mbedtls_strerror( ret, buf, 1024 );
          mbedtls_printf( " failed\n  !  mbedtls_x509write_crt_set_serial returned -0x%02x - %s\n\n", -ret, buf );
          goto exit;
      }
  
      ret = mbedtls_x509write_crt_set_validity( &crt, opt.not_before, opt.not_after );
      if( ret != 0 )
      {
          mbedtls_strerror( ret, buf, 1024 );
          mbedtls_printf( " failed\n  !  mbedtls_x509write_crt_set_validity returned -0x%02x - %s\n\n", -ret, buf );
          goto exit;
      }
  
      mbedtls_printf( " ok\n" );
  
      mbedtls_printf( "  . Adding the Basic Constraints extension ..." );
      fflush( stdout );
  
      ret = mbedtls_x509write_crt_set_basic_constraints( &crt, opt.is_ca,
                                                 opt.max_pathlen );
      if( ret != 0 )
      {
          mbedtls_strerror( ret, buf, 1024 );
          mbedtls_printf( " failed\n  !  x509write_crt_set_basic_contraints returned -0x%02x - %s\n\n", -ret, buf );
          goto exit;
      }
  
      mbedtls_printf( " ok\n" );
  
  #if defined(MBEDTLS_SHA1_C)
      mbedtls_printf( "  . Adding the Subject Key Identifier ..." );
      fflush( stdout );
  
      ret = mbedtls_x509write_crt_set_subject_key_identifier( &crt );
      if( ret != 0 )
      {
          mbedtls_strerror( ret, buf, 1024 );
          mbedtls_printf( " failed\n  !  mbedtls_x509write_crt_set_subject_key_identifier returned -0x%02x - %s\n\n", -ret, buf );
          goto exit;
      }
  
      mbedtls_printf( " ok\n" );
  
      mbedtls_printf( "  . Adding the Authority Key Identifier ..." );
      fflush( stdout );
  
      ret = mbedtls_x509write_crt_set_authority_key_identifier( &crt );
      if( ret != 0 )
      {
          mbedtls_strerror( ret, buf, 1024 );
          mbedtls_printf( " failed\n  !  mbedtls_x509write_crt_set_authority_key_identifier returned -0x%02x - %s\n\n", -ret, buf );
          goto exit;
      }
  
      mbedtls_printf( " ok\n" );
  #endif /* MBEDTLS_SHA1_C */
  
      if( opt.key_usage )
      {
          mbedtls_printf( "  . Adding the Key Usage extension ..." );
          fflush( stdout );
  
          ret = mbedtls_x509write_crt_set_key_usage( &crt, opt.key_usage );
          if( ret != 0 )
          {
              mbedtls_strerror( ret, buf, 1024 );
              mbedtls_printf( " failed\n  !  mbedtls_x509write_crt_set_key_usage returned -0x%02x - %s\n\n", -ret, buf );
              goto exit;
          }
  
          mbedtls_printf( " ok\n" );
      }
  
      if( opt.ns_cert_type )
      {
          mbedtls_printf( "  . Adding the NS Cert Type extension ..." );
          fflush( stdout );
  
          ret = mbedtls_x509write_crt_set_ns_cert_type( &crt, opt.ns_cert_type );
          if( ret != 0 )
          {
              mbedtls_strerror( ret, buf, 1024 );
              mbedtls_printf( " failed\n  !  mbedtls_x509write_crt_set_ns_cert_type returned -0x%02x - %s\n\n", -ret, buf );
              goto exit;
          }
  
          mbedtls_printf( " ok\n" );
      }
  
      /*
       * 1.2. Writing the request
       */
      mbedtls_printf( "  . Writing the certificate..." );
      fflush( stdout );
  
      if( ( ret = write_certificate( &crt, opt.output_file,
                                     mbedtls_ctr_drbg_random, &ctr_drbg ) ) != 0 )
      {
          mbedtls_strerror( ret, buf, 1024 );
          mbedtls_printf( " failed\n  !  write_certifcate -0x%02x - %s\n\n", -ret, buf );
          goto exit;
      }
  
      mbedtls_printf( " ok\n" );
  
  exit:
      mbedtls_x509write_crt_free( &crt );
      mbedtls_pk_free( &loaded_subject_key );
      mbedtls_pk_free( &loaded_issuer_key );
      mbedtls_mpi_free( &serial );
      mbedtls_ctr_drbg_free( &ctr_drbg );
      mbedtls_entropy_free( &entropy );
  
  #if defined(_WIN32)
      mbedtls_printf( "  + Press Enter to exit this program.\n" );
      fflush( stdout ); getchar();
  #endif
  
      return( ret );
  }
  #endif /* MBEDTLS_X509_CRT_WRITE_C && MBEDTLS_X509_CRT_PARSE_C &&
            MBEDTLS_FS_IO && MBEDTLS_ENTROPY_C && MBEDTLS_CTR_DRBG_C &&
            MBEDTLS_ERROR_C && MBEDTLS_PEM_WRITE_C */
  