This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nRF91 How is it possible to generate EC 256 key pair and a self-signed X.509 certificate?

Hi,
I want to connect to Google Cloud IoT and for that I would like to generate the needed credentials on the nRF9160.
I need these ones, but the private key should be both in PEM and in DER format.
PEM is needed by the modem and DER is used by jwt_sign.
With kind regards,
Árpád
Parents
  • I this link useful? I don't know too much about this, but I can do some more investigation and ask internally if the link didn't help.

    Best regards,

    Simon

  • Hi Simon,

    thank your for your reply, but my question is how to generate all of this ON the nRF9160. Because if that would be possible

    than the private key should not leave the device ensuring a higher security level compared to generating these keys outside of the device and transmitting over some channel.

    Could you please ask for that? It would be good, if the key generation could happen on the device.

    With kind regards,

    Árpád

  • Hi Árpád,

    I cannot comment on when new features will be available, unfortunately. However, I see I was a bit too pessimistic in my previous reply. You can in fact make your own solution, and use the RNG support in the CC310 via the Secure Partition Manager, which has the spm_request_random_number() function. See Secure services. This just gives you entropy, and then you can use a pure SW library of your preference for the rest.

    Einar

  • Hi Einar,

    thanks for the hint. By trying to follow it, I get trapped by secure service causing a crash

    And I have other problem too: I want to print out the created keys to the console with

    mbedtls_pk_write_pubkey_pem and mbedtls_pk_write_key_pem but they need

    MBEDTLS_PK_WRITE_C to be defined, which needs a specialized mbedtls config file.

    (At least I have not found a Zephyr Kconfig macro to accomplish this.)

    How can I create and use such a config file without messing up Nordic's mbedtls configuration?

    Best regards,

    Árpád

  • Hi Árpád,

    PopradiArpad said:
    thanks for the hint. By trying to follow it, I get trapped by secure service causing a crash

    I see. Have you applied the workaround?

    PopradiArpad said:
    How can I create and use such a config file without messing up Nordic's mbedtls configuration?

    I have not had a chance to test this myself, but I would not expect setting MBEDTLS_PK_WRITE_C in the mbedTLS config header file would mess up anything? In what way does it cause problems?

    Einar

  • Hi Einar,

    thank you for your answer!

    to mbedTLS configuration:

    It's really much easier to configure mbedTLS directly by an mbedTLS config file then through the predefined Zephyr config symbols. The only tricky part was having that file within my project.

    But this prj.conf snippets does it:

    # Generate credentials
    CONFIG_MBEDTLS=y
    # Configure mbedTLS directly with its configuration file instead through Zephyr config symbols
    # Relative from ncs/modules/crypto/mbedtls/configs/config-tls-generic.h
    CONFIG_MBEDTLS_CFG_FILE="../../../../MY-PROJECT/config-tls.h"
    
     

    Writing the config file is easy: all the missing mbedTLS config definitions are checked by mbedTLS itself

    during compilation or in case of a link error it's easy to find by the guard macro name.

    to the secure service causing crash:

    later.

    Best regards,

    Árpád

  • Hi Einar,

    your workaround works like a charm :) 

    Here is step by step how to generate EC 256 key pair. (X509 certificate is not needed by Google Cloud IoT)

    For nRF Connect SDK v1.3.0

    1. Apply the needed pieces of workaround :

    secure_services.h
    -----------------
    // https://devzone.nordicsemi.com/f/nordic-q-a/63576/nrf91-how-is-it-possible-to-generate-ec-256-key-pair-and-a-self-signed-x-509-certificate https://github.com/nrfconnect/sdk-nrf/pull/2519/files
    #include <zephyr.h>
    ..
    // https://devzone.nordicsemi.com/f/nordic-q-a/63576/nrf91-how-is-it-possible-to-generate-ec-256-key-pair-and-a-self-signed-x-509-certificate https://github.com/nrfconnect/sdk-nrf/pull/2519/files
    //int spm_request_random_number(u8_t *output, size_t len, size_t *olen);
    static inline int spm_request_random_number(u8_t *output, size_t len, size_t *olen);
    int spm_request_random_number_nsc(u8_t *output, size_t len, size_t *olen);
    static inline int spm_request_random_number(u8_t *output, size_t len, size_t *olen)
    {
    	k_sched_lock();
    	int err = spm_request_random_number_nsc(output, len, olen);
    
    	k_sched_unlock();
    	return err;
    }
    
    secure_services.c
    -----------------
    // https://devzone.nordicsemi.com/f/nordic-q-a/63576/nrf91-how-is-it-possible-to-generate-ec-256-key-pair-and-a-self-signed-x-509-certificate https://github.com/nrfconnect/sdk-nrf/pull/2519/files
    //int spm_request_random_number(u8_t *output, size_t len, size_t *olen)
    int spm_request_random_number_nsc(u8_t *output, size_t len, size_t *olen)
    {
    	int err;
    
    	if (len != MBEDTLS_ENTROPY_MAX_GATHER) {
    		return -EINVAL;
    	}
    
    	err = mbedtls_hardware_poll(NULL, output, len, olen);
    	return err;
    }
    

    2. Add mbedTLS support in prj.conf:

    # Generate credentials
    # Based on
    #   ncs/zephyr/samples/net/cloud/google_iot_mqtt/prj.conf
    #   ncs/iluminate-hub-nrf9160/build_nrf9160dk_nrf9160ns/mcuboot/zephyr/.config
    CONFIG_MBEDTLS=y
    CONFIG_MBEDTLS_BUILTIN=y
    #   Configure mbedTLS directly with its configuration file instead through Zephyr config symbols
    #     Relative from ncs/modules/crypto/mbedtls/configs/config-tls-generic.h
    CONFIG_MBEDTLS_CFG_FILE="../../../../iluminate-hub-nrf9160/config-mbedtls.h"
    

    3. Configure mbedTLS by an own mbedTLS config file based on ncs/modules/crypto/mbedtls/configs/config-tls-generic.h and positioned in the project directory.

    I named it config-mbedtls.h.

    //Changes relative to ncs/modules/crypto/mbedtls/configs/config-tls-generic.h
    //----------------------------------------------------------------------------------
      // general settings
    #define MBEDTLS_ERROR_C
      // for printing mbedTLS keys and certificates
    #define MBEDTLS_PEM_WRITE_C
    #define MBEDTLS_PK_WRITE_C
    #define MBEDTLS_BASE64_C
      // for key generation
    #define MBEDTLS_PK_C
    #define MBEDTLS_ECP_C
    #define MBEDTLS_ECP_DP_SECP256R1_ENABLED
    #define MBEDTLS_OID_C
    #define MBEDTLS_ASN1_WRITE_C
    

    4. The C code

    #include "secure_services.h"
    #include "mbedtls/ecdsa.h"
    #include "mbedtls/pk.h"
    #include "mbedtls/error.h"
    
    #include <logging/log.h>
    LOG_MODULE_REGISTER(credentials, CONFIG_ASSET_TRACKER_LOG_LEVEL);
    
    
    // The generated random number has fix 144 length. See doc of spm_request_random_number
    #define GENERATED_RANDOM_NUMBER_LENGTH 144
    
    // See mbedtls_entropy_func as example
    static int gen_true_random_number(void *unused, unsigned char *output, size_t len )
    {
      // spm_request_random_number uses uint8_t and not unsigned char
      __ASSERT(sizeof(unsigned char) == sizeof(uint8_t), "Adapt type!");
    
      if( len > GENERATED_RANDOM_NUMBER_LENGTH )
      {
        LOG_ERR( "Max generatable real random number length exceeded");
        return -1;
      }
    
      static uint8_t rnd_number[GENERATED_RANDOM_NUMBER_LENGTH];
      memset( rnd_number, 0, GENERATED_RANDOM_NUMBER_LENGTH);
      size_t rnd_number_length = 0;
      int err = spm_request_random_number(rnd_number, GENERATED_RANDOM_NUMBER_LENGTH, &rnd_number_length);
      if( err != 0 )
      {
        LOG_ERR( "Can't get real random number: %d", err);
        return -1;
      }
    
      if( rnd_number_length < len )
      {
        LOG_ERR( "Generated real random number is too short");
        return -1;
      }
    
      memcpy( output, rnd_number, len );
    
      return 0;
    }
    
    static void log_key(mbedtls_pk_context * key, int (f_writer)( mbedtls_pk_context *, unsigned char *, size_t), char* err_buf, size_t err_buf_length)
    {
      unsigned char buf[1024];
      int err = f_writer(key, buf, sizeof(buf));
      if (err != 0)
      {
        mbedtls_strerror(err, err_buf, err_buf_length);
        LOG_ERR("Can not write -0x%04x - %s\n\n", (unsigned int) -err, err_buf);
        return;
      }
      LOG_INF("\n%s",buf);
    }
    
    static inline void log_private_key(mbedtls_pk_context * key, char* err_buf, size_t err_buf_length)
    {
      log_key(key, mbedtls_pk_write_key_pem, err_buf, err_buf_length);
    }
    
    static inline void log_public_key(mbedtls_pk_context * key, char* err_buf, size_t err_buf_length)
    {
      log_key(key, mbedtls_pk_write_pubkey_pem, err_buf, err_buf_length);
    }
    
    static void log_keys(mbedtls_pk_context * key, char* err_buf, size_t err_buf_length)
    {
      log_private_key(key, err_buf, err_buf_length);
      log_public_key(key, err_buf, err_buf_length);
    }
    
    
    static int generate_key(mbedtls_pk_context * key, char* err_buf, size_t err_buf_length)
    {
      int err;
    
      LOG_INF("Generating key pairs, it takes ~3 sec.");
      mbedtls_pk_init(key);
      if ((err = mbedtls_pk_setup(key, mbedtls_pk_info_from_type( MBEDTLS_PK_ECKEY))) != 0)
      {
        mbedtls_strerror(err, err_buf, err_buf_length);
        LOG_ERR("Can't setup key context -0x%04x - %s\n\n", (unsigned int) -err, err_buf);
        return err;
      }
    
      err = mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, /*ec_curve,*/
                                mbedtls_pk_ec(*key),
                                gen_true_random_number, NULL );
      if (err != 0)
      {
        mbedtls_strerror(err, err_buf, err_buf_length);
        LOG_ERR("Can not generate key -0x%04x - %s\n\n", (unsigned int) -err, err_buf);
        return err;
      }
      LOG_INF( "Key pair generated.");
    
      return 0;
    }
    
    // https://tls.mbed.org/kb/how-to/generate-a-self-signed-certificate
    // https://github.com/ARMmbed/mbedtls/blob/development/programs/pkey/gen_key.c
    /*
      Check whether the generated key pair are right:
      1. log_keys(&key)
      2. Copy the content into private_key.pem, public_key.pem
      3. Verify them with a sign and verify process
        cat blablabla > bla.txt
        openssl dgst -sha1 -sign private_key.pem bla.txt > signature.bin
        openssl dgst -sha1 -verify public_key.pem -signature signature.bin bla.txt
    */
    void main(void)
    {
      char err_buf[512];
    
      mbedtls_pk_context key;
     
      if (generate_key(&key, err_buf, sizeof(err_buf)) != 0)
        return;
    
      log_keys(&key, err_buf, sizeof(err_buf));
    
      mbedtls_pk_free(&key);
    }
    

    That's it.

    The x509 certificate creation needs more setup and code but because it's not needed I don't post it.

    Best regards,

    Árpád 

Reply
  • Hi Einar,

    your workaround works like a charm :) 

    Here is step by step how to generate EC 256 key pair. (X509 certificate is not needed by Google Cloud IoT)

    For nRF Connect SDK v1.3.0

    1. Apply the needed pieces of workaround :

    secure_services.h
    -----------------
    // https://devzone.nordicsemi.com/f/nordic-q-a/63576/nrf91-how-is-it-possible-to-generate-ec-256-key-pair-and-a-self-signed-x-509-certificate https://github.com/nrfconnect/sdk-nrf/pull/2519/files
    #include <zephyr.h>
    ..
    // https://devzone.nordicsemi.com/f/nordic-q-a/63576/nrf91-how-is-it-possible-to-generate-ec-256-key-pair-and-a-self-signed-x-509-certificate https://github.com/nrfconnect/sdk-nrf/pull/2519/files
    //int spm_request_random_number(u8_t *output, size_t len, size_t *olen);
    static inline int spm_request_random_number(u8_t *output, size_t len, size_t *olen);
    int spm_request_random_number_nsc(u8_t *output, size_t len, size_t *olen);
    static inline int spm_request_random_number(u8_t *output, size_t len, size_t *olen)
    {
    	k_sched_lock();
    	int err = spm_request_random_number_nsc(output, len, olen);
    
    	k_sched_unlock();
    	return err;
    }
    
    secure_services.c
    -----------------
    // https://devzone.nordicsemi.com/f/nordic-q-a/63576/nrf91-how-is-it-possible-to-generate-ec-256-key-pair-and-a-self-signed-x-509-certificate https://github.com/nrfconnect/sdk-nrf/pull/2519/files
    //int spm_request_random_number(u8_t *output, size_t len, size_t *olen)
    int spm_request_random_number_nsc(u8_t *output, size_t len, size_t *olen)
    {
    	int err;
    
    	if (len != MBEDTLS_ENTROPY_MAX_GATHER) {
    		return -EINVAL;
    	}
    
    	err = mbedtls_hardware_poll(NULL, output, len, olen);
    	return err;
    }
    

    2. Add mbedTLS support in prj.conf:

    # Generate credentials
    # Based on
    #   ncs/zephyr/samples/net/cloud/google_iot_mqtt/prj.conf
    #   ncs/iluminate-hub-nrf9160/build_nrf9160dk_nrf9160ns/mcuboot/zephyr/.config
    CONFIG_MBEDTLS=y
    CONFIG_MBEDTLS_BUILTIN=y
    #   Configure mbedTLS directly with its configuration file instead through Zephyr config symbols
    #     Relative from ncs/modules/crypto/mbedtls/configs/config-tls-generic.h
    CONFIG_MBEDTLS_CFG_FILE="../../../../iluminate-hub-nrf9160/config-mbedtls.h"
    

    3. Configure mbedTLS by an own mbedTLS config file based on ncs/modules/crypto/mbedtls/configs/config-tls-generic.h and positioned in the project directory.

    I named it config-mbedtls.h.

    //Changes relative to ncs/modules/crypto/mbedtls/configs/config-tls-generic.h
    //----------------------------------------------------------------------------------
      // general settings
    #define MBEDTLS_ERROR_C
      // for printing mbedTLS keys and certificates
    #define MBEDTLS_PEM_WRITE_C
    #define MBEDTLS_PK_WRITE_C
    #define MBEDTLS_BASE64_C
      // for key generation
    #define MBEDTLS_PK_C
    #define MBEDTLS_ECP_C
    #define MBEDTLS_ECP_DP_SECP256R1_ENABLED
    #define MBEDTLS_OID_C
    #define MBEDTLS_ASN1_WRITE_C
    

    4. The C code

    #include "secure_services.h"
    #include "mbedtls/ecdsa.h"
    #include "mbedtls/pk.h"
    #include "mbedtls/error.h"
    
    #include <logging/log.h>
    LOG_MODULE_REGISTER(credentials, CONFIG_ASSET_TRACKER_LOG_LEVEL);
    
    
    // The generated random number has fix 144 length. See doc of spm_request_random_number
    #define GENERATED_RANDOM_NUMBER_LENGTH 144
    
    // See mbedtls_entropy_func as example
    static int gen_true_random_number(void *unused, unsigned char *output, size_t len )
    {
      // spm_request_random_number uses uint8_t and not unsigned char
      __ASSERT(sizeof(unsigned char) == sizeof(uint8_t), "Adapt type!");
    
      if( len > GENERATED_RANDOM_NUMBER_LENGTH )
      {
        LOG_ERR( "Max generatable real random number length exceeded");
        return -1;
      }
    
      static uint8_t rnd_number[GENERATED_RANDOM_NUMBER_LENGTH];
      memset( rnd_number, 0, GENERATED_RANDOM_NUMBER_LENGTH);
      size_t rnd_number_length = 0;
      int err = spm_request_random_number(rnd_number, GENERATED_RANDOM_NUMBER_LENGTH, &rnd_number_length);
      if( err != 0 )
      {
        LOG_ERR( "Can't get real random number: %d", err);
        return -1;
      }
    
      if( rnd_number_length < len )
      {
        LOG_ERR( "Generated real random number is too short");
        return -1;
      }
    
      memcpy( output, rnd_number, len );
    
      return 0;
    }
    
    static void log_key(mbedtls_pk_context * key, int (f_writer)( mbedtls_pk_context *, unsigned char *, size_t), char* err_buf, size_t err_buf_length)
    {
      unsigned char buf[1024];
      int err = f_writer(key, buf, sizeof(buf));
      if (err != 0)
      {
        mbedtls_strerror(err, err_buf, err_buf_length);
        LOG_ERR("Can not write -0x%04x - %s\n\n", (unsigned int) -err, err_buf);
        return;
      }
      LOG_INF("\n%s",buf);
    }
    
    static inline void log_private_key(mbedtls_pk_context * key, char* err_buf, size_t err_buf_length)
    {
      log_key(key, mbedtls_pk_write_key_pem, err_buf, err_buf_length);
    }
    
    static inline void log_public_key(mbedtls_pk_context * key, char* err_buf, size_t err_buf_length)
    {
      log_key(key, mbedtls_pk_write_pubkey_pem, err_buf, err_buf_length);
    }
    
    static void log_keys(mbedtls_pk_context * key, char* err_buf, size_t err_buf_length)
    {
      log_private_key(key, err_buf, err_buf_length);
      log_public_key(key, err_buf, err_buf_length);
    }
    
    
    static int generate_key(mbedtls_pk_context * key, char* err_buf, size_t err_buf_length)
    {
      int err;
    
      LOG_INF("Generating key pairs, it takes ~3 sec.");
      mbedtls_pk_init(key);
      if ((err = mbedtls_pk_setup(key, mbedtls_pk_info_from_type( MBEDTLS_PK_ECKEY))) != 0)
      {
        mbedtls_strerror(err, err_buf, err_buf_length);
        LOG_ERR("Can't setup key context -0x%04x - %s\n\n", (unsigned int) -err, err_buf);
        return err;
      }
    
      err = mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, /*ec_curve,*/
                                mbedtls_pk_ec(*key),
                                gen_true_random_number, NULL );
      if (err != 0)
      {
        mbedtls_strerror(err, err_buf, err_buf_length);
        LOG_ERR("Can not generate key -0x%04x - %s\n\n", (unsigned int) -err, err_buf);
        return err;
      }
      LOG_INF( "Key pair generated.");
    
      return 0;
    }
    
    // https://tls.mbed.org/kb/how-to/generate-a-self-signed-certificate
    // https://github.com/ARMmbed/mbedtls/blob/development/programs/pkey/gen_key.c
    /*
      Check whether the generated key pair are right:
      1. log_keys(&key)
      2. Copy the content into private_key.pem, public_key.pem
      3. Verify them with a sign and verify process
        cat blablabla > bla.txt
        openssl dgst -sha1 -sign private_key.pem bla.txt > signature.bin
        openssl dgst -sha1 -verify public_key.pem -signature signature.bin bla.txt
    */
    void main(void)
    {
      char err_buf[512];
    
      mbedtls_pk_context key;
     
      if (generate_key(&key, err_buf, sizeof(err_buf)) != 0)
        return;
    
      log_keys(&key, err_buf, sizeof(err_buf));
    
      mbedtls_pk_free(&key);
    }
    

    That's it.

    The x509 certificate creation needs more setup and code but because it's not needed I don't post it.

    Best regards,

    Árpád 

Children
No Data
Related