From 699f5473cc3c3664445fb1892ca60d40c7a60360 Mon Sep 17 00:00:00 2001 From: Jaeden Amero Date: Thu, 26 Sep 2019 14:47:21 +0100 Subject: [PATCH 1/4] Add a device provisioning example Add a device provisioning example that works with two different types of secure element keys: - Keys already stored in the secure element (in physical slot 0 in our example), placed there by the secure element vendor or another secure facility before coming to the assembly house. - Keys generated within the secure element during assembly (into physical slot 2 in our example) The provisiong example fulfils the need post-device-assembly to notify Mbed Crypto about which keys are present where, so that applications can use those keys. Mbed Crypto will remember this information across reboots, so after provisioning, another application can be flashed onto the device which can use these keys. --- provisioning/.mbedignore | 2 + provisioning/README.md | 64 ++++++++ provisioning/main.c | 242 +++++++++++++++++++++++++++++ provisioning/mbed-os-atecc608a.lib | 1 + provisioning/mbed-os.lib | 1 + provisioning/mbed_app.json | 22 +++ provisioning/mbedtls_user_config.h | 27 ++++ 7 files changed, 359 insertions(+) create mode 100644 provisioning/.mbedignore create mode 100644 provisioning/README.md create mode 100644 provisioning/main.c create mode 100644 provisioning/mbed-os-atecc608a.lib create mode 100644 provisioning/mbed-os.lib create mode 100644 provisioning/mbed_app.json create mode 100644 provisioning/mbedtls_user_config.h diff --git a/provisioning/.mbedignore b/provisioning/.mbedignore new file mode 100644 index 0000000..5e49837 --- /dev/null +++ b/provisioning/.mbedignore @@ -0,0 +1,2 @@ +mbed-os/features/frameworks/unity/ + diff --git a/provisioning/README.md b/provisioning/README.md new file mode 100644 index 0000000..56b8695 --- /dev/null +++ b/provisioning/README.md @@ -0,0 +1,64 @@ +# Mbed Crypto factory device provisioning example + +This example demonstrates how to install keys into a device during a +hypothetical board assembly stage. Your device comprises a microcontroller, a +secure element (e.g. ATECC608A), and a PCB to connect and hold everything +together. When this README refers to "device", we mean the entire board +assembly, not just the microcontroller or the secure element. + +Let's say the secure element manufacturer shipped you a secure element with a +device private key already inside. This type of "pre-provisioned" secure +element is what you'll use as a part during the device assembly process. You've +taken this secure element, paired it with a nice Cortex-M microcontroller on a +board to make a device. Now, you'd like to be able to use this key from Mbed +Crypto. How does Mbed Crypto know the key exists? We can tell Mbed Crypto by +running a factory device provisionining application, which is typically +separate from the primary application and may be run as a final device assembly +step. + + +### Installing keys during factory device provisioning + +Mbed Crypto provides three ways to install secure element keys into your +device. + +- Register a pre-existing secure element key +- Generate a new key within a secure element +- Import a pre-existing key to a secure element (not all secure elements + support this method) + +This example demonstrates the first two methods. + + +#### Register a pre-existing secure element key + +For registering keys already present within a secure element, Mbed Crypto +provides a function to inform Mbed Crypto about the existence of the key: +`mbedtls_psa_register_se_key()`. This function adds the necessary metadata to +persistent storage so that the secure element keys can be used by an +application. + +With `psa_set_key_slot_number()`, you can specify which physical secure element +slot the key is in. This function operates on a key attributes structure. Fill +in any other necessary attributes and then call `mbedtls_psa_register_se_key()` +to notify Mbed Crypto that the key exists, where the key exists, what format +the key is, what the key can be used for, and so forth. + + +#### Generate a new key within a secure element + +For generating a new key, Mbed Crypto provides `psa_generate_key()`. The +physical location in which to generate the key is specified by the key's +attributes before the key is created: specifically, the lifetime and +optionally, for keys within a secure element, the physical secure element slot +number. + +For generated keys, unlike pre-existing secure element keys, calling +`psa_set_key_slot_number()` is not required. The driver will select a valid and +available key slot for the key you wish to generated based on the key +attributes you've requested during creation time. + +To generate a key, specify the lifetime with `psa_set_key_lifetime()` and +`PSA_ATECC608A_LIFETIME`. Fill in any other other necessary attributes, and +then call `psa_generate_key()` to request the key be generated within the +ATECC608A. diff --git a/provisioning/main.c b/provisioning/main.c new file mode 100644 index 0000000..ee49b77 --- /dev/null +++ b/provisioning/main.c @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2019, Arm Limited and affiliates + * 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. + */ + +#include +#include + +#if defined(ATCA_HAL_I2C) +#include "mbedtls/pk.h" +#include "psa/crypto.h" +#include "psa/lifecycle.h" +#include "atecc608a_se.h" + +/* The slot number for the device private key stored in the secure element by + * the secure element factory. + * + * ATECC608A secure elements with pre-provisioned keys often have that + * key present in slot 0, so we use 0 here for our example. + * + * Note that unlike most numerical values in the PSA Crypto API, 0 is a valid + * value for a slot number. Like many other secure elements, the ATECC608A + * numbers its slots from 0. + */ +#define EXAMPLE_FACTORY_KEY_SE_SLOT 0 + +/* The slot number for the device private key which is generated within the + * secure element (never leaving the secure element) by this example + * provisioning application. */ +#define EXAMPLE_GENERATED_KEY_SE_SLOT 2 + +/* The application-specific key ID for the secure element factory-provided + * device private key. This provisioning example application will associate the + * factory-provided key with this key ID for use by other applications. Any + * valid ID can be chosen here; the chosen ID does not need to correlate in any + * way with the physical location of the key (within the secure element). */ +#define EXAMPLE_FACTORY_KEY_ID 16 + +/* The application-specific key ID for the device private key imported into the + * secure element by this example provisioning application. */ +#define EXAMPLE_GENERATED_KEY_ID 18 + +static psa_status_t generate_key_on_device(void) +{ + psa_status_t status; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_key_handle_t handle; + + /* Set device private key attributes. */ + /* Note that it is not necessary to specify the physical slot number within + * the secure element. The secure element driver can automatically allocate + * a slot that fits the use case the key attributes request. */ + psa_set_key_id(&attributes, EXAMPLE_GENERATED_KEY_ID); + psa_set_key_lifetime(&attributes, PSA_ATECC608A_LIFETIME); + psa_set_key_slot_number(&attributes, EXAMPLE_GENERATED_KEY_SE_SLOT); + psa_set_key_type(&attributes, + PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_CURVE_SECP256R1)); + psa_set_key_bits(&attributes, 256); + psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN); + + /* Generate the key inside the secure element. */ + status = psa_generate_key(&attributes, &handle); + if (status != PSA_SUCCESS) { + return status; + } + + return psa_close_key(handle); +} + +static psa_status_t register_preprovisioned_keys(void) +{ + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + + /* Set device private key attributes. */ + psa_set_key_id(&attributes, EXAMPLE_FACTORY_KEY_ID); + psa_set_key_lifetime(&attributes, PSA_ATECC608A_LIFETIME); + psa_set_key_slot_number(&attributes, EXAMPLE_FACTORY_KEY_SE_SLOT); + psa_set_key_type(&attributes, + PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_CURVE_SECP256R1)); + psa_set_key_bits(&attributes, 256); + psa_set_key_algorithm(&attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN); + + /* Register the factory-created key with Mbed Crypto, so that Mbed Crypto + * knows that the key exists and how to find and access the key. This + * registration only needs doing once, as Mbed Crypto will remember the + * registration even across reboots. */ + return mbedtls_psa_register_se_key(&attributes); +} + +static void print_public_key(psa_key_id_t key_id) +{ + enum { PUBKEY_PEM_LEN = 256 }; + int ret; + psa_status_t status; + unsigned char *output; + psa_key_handle_t handle; + mbedtls_pk_context pk; + + printf("\tKey ID %lu:\n", key_id); + + /* Open the specified key. */ + status = psa_open_key(key_id, &handle); + if (status != PSA_SUCCESS) + { + printf("Failed to open key %lu with status=%ld\n", key_id, status); + return; + } + + output = calloc(1, PUBKEY_PEM_LEN); + if (!output) { + puts("Out of memory"); + return; + } + + /* mbedtls_* functions, rather than PSA functions, are used here because + * PSA currently lacks a way to format the key in the desired format. Mbed + * Crypto's PK module can transform PSA keys to our desired key format, so + * we employ Mbed Crypto's PK module here. */ + + mbedtls_pk_init(&pk); + ret = mbedtls_pk_setup_opaque(&pk, handle); + if (ret != 0) + { + printf("Failed to setup PK with ret=%d\n", ret); + goto done; + } + + ret = mbedtls_pk_write_pubkey_pem(&pk, output, PUBKEY_PEM_LEN); + if (ret != 0) { + printf("Failed to print pubkey with ret=%d\n", ret); + goto done; + } + + printf("%s\n", output); + +done: + mbedtls_pk_free(&pk); + free(output); +} + +int main(void) +{ + psa_status_t status; + printf("Provisioning device...\n"); + + /* Erase secure storage (PSA ITS and PSA PS) to give Mbed Crypto a blank + * slate to provision the device from. Any keys it used to know about will + * be forgotten. Any keys stored in PSA ITS will be erased. All metadata + * stored in PSA ITS about keys stored in secure elements will be erased. + * + * Note that the device may reboot as a part of this erase step, depending + * on how mbed_psa_reboot_and_request_new_security_state() is implemented. + * Currently, the function does not reboot in a manner such that this + * function would be executed again and again in a loop. + */ + printf("\tErasing secure storage... "); + fflush(stdout); + status = mbed_psa_reboot_and_request_new_security_state(PSA_LIFECYCLE_ASSEMBLY_AND_TEST); + if (status != PSA_SUCCESS) + { + printf("failed with status=%ld\n", status); + return status; + } + printf("done.\n"); + + printf("\tRegistering drivers... "); + fflush(stdout); + status = psa_register_se_driver(PSA_ATECC608A_LIFETIME, &atecc608a_drv_info); + if (status != PSA_SUCCESS) + { + printf("failed with status=%ld\n", status); + return status; + } + printf("done.\n"); + + printf("\tInitializing PSA Crypto... "); + fflush(stdout); + status = psa_crypto_init(); + if (status != PSA_SUCCESS) + { + printf("failed with status=%ld\n", status); + return status; + } + printf("done.\n"); + + /* The secure element factory put a device private key pair into a slot in + * the secure element. We need to tell Mbed Crypto that this key pair + * exists so that it can be used. */ + printf("\tRegistering factory-created keys... "); + fflush(stdout); + status = register_preprovisioned_keys(); + if (status != PSA_SUCCESS) + { + printf("failed with status=%ld\n", status); + return status; + } + printf("done.\n"); + + /* We also want to generate our own key entirely within the secure element + * during the factory device provisioning stage in device assembly. Ask + * Mbed Crypto to generate this key for us. */ + printf("\tGenerating keys within the secure element... "); + fflush(stdout); + status = generate_key_on_device(); + if (status != PSA_SUCCESS) + { + printf("\n\t\tfailed with status=%ld\n", status); + return status; + } + printf("done.\n"); + + printf("Device provisioned\n"); + + printf("\n---------------------------------------------------------------------\n\n"); + printf("Device public keys:\n"); + print_public_key(EXAMPLE_FACTORY_KEY_ID); + print_public_key(EXAMPLE_GENERATED_KEY_ID); + + return PSA_SUCCESS; +} +#else +int main(void) +{ + printf("Not all of the required options are defined:\n" + " - ATCA_HAL_I2C\n"); + return 0; +} +#endif diff --git a/provisioning/mbed-os-atecc608a.lib b/provisioning/mbed-os-atecc608a.lib new file mode 100644 index 0000000..cfee7a0 --- /dev/null +++ b/provisioning/mbed-os-atecc608a.lib @@ -0,0 +1 @@ +https://github.com/ARMmbed/mbed-os-atecc608a/#754d0e42acc15f67310c7b3c773cd5b9484b924e diff --git a/provisioning/mbed-os.lib b/provisioning/mbed-os.lib new file mode 100644 index 0000000..df97fc3 --- /dev/null +++ b/provisioning/mbed-os.lib @@ -0,0 +1 @@ +https://github.com/ARMmbed/mbed-os/ diff --git a/provisioning/mbed_app.json b/provisioning/mbed_app.json new file mode 100644 index 0000000..3f154f9 --- /dev/null +++ b/provisioning/mbed_app.json @@ -0,0 +1,22 @@ +{ + "target_overrides": { + "*": { + "platform.stdio-baud-rate": 9600, + "platform.stdio-convert-newlines": true, + "target.OUTPUT_EXT": "hex", + "mbed-trace.enable": 0 + }, + "NRF52_DK": { + "cryptoauthlib.i2c_sda": "P0_26", + "cryptoauthlib.i2c_scl": "P0_27" + }, + "SAML21J18A": { + "cryptoauthlib.i2c_sda": "PA08", + "cryptoauthlib.i2c_scl": "PA09" + } + }, + "macros": [ + "ATCAPRINTF", + "MBEDTLS_USER_CONFIG_FILE=\"mbedtls_user_config.h\"" + ] +} diff --git a/provisioning/mbedtls_user_config.h b/provisioning/mbedtls_user_config.h new file mode 100644 index 0000000..ac88050 --- /dev/null +++ b/provisioning/mbedtls_user_config.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2006-2019, 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) + */ + +/* Enable PSA use of secure elements. */ +#define MBEDTLS_PSA_CRYPTO_SE_C + +/* Make Mbed TLS use the PSA Crypto API. */ +#define MBEDTLS_USE_PSA_CRYPTO + +/* Enable printing of public keys in PEM format. */ +#define MBEDTLS_PEM_WRITE_C From 44e11728c7387f82a5e42402f12cf9f50269da47 Mon Sep 17 00:00:00 2001 From: Jaeden Amero Date: Fri, 11 Oct 2019 17:14:50 +0100 Subject: [PATCH 2/4] provisioning: Add CSR generation Automatically create and print a certificate signing request. This makes it convenient to create device certificates using never-left-the-device, PSA-managed private keys. Include instructions on how to create and verify a device certificate in the README. --- provisioning/README.md | 7 ++ provisioning/main.c | 103 +++++++++++++++++++++++++++++ provisioning/mbedtls_user_config.h | 6 +- 3 files changed, 115 insertions(+), 1 deletion(-) diff --git a/provisioning/README.md b/provisioning/README.md index 56b8695..1b42e0d 100644 --- a/provisioning/README.md +++ b/provisioning/README.md @@ -62,3 +62,10 @@ To generate a key, specify the lifetime with `psa_set_key_lifetime()` and `PSA_ATECC608A_LIFETIME`. Fill in any other other necessary attributes, and then call `psa_generate_key()` to request the key be generated within the ATECC608A. + + +### Generating CSRs on-device + +This example generates certificate signing requests (CSRs) which can be used +for TLS client authentication. The CSR is printed over the serial port, for +use with your certificate authority (CA). diff --git a/provisioning/main.c b/provisioning/main.c index ee49b77..a8a8190 100644 --- a/provisioning/main.c +++ b/provisioning/main.c @@ -20,9 +20,12 @@ #if defined(ATCA_HAL_I2C) #include "mbedtls/pk.h" +#include "mbedtls/x509.h" +#include "mbedtls/x509_csr.h" #include "psa/crypto.h" #include "psa/lifecycle.h" #include "atecc608a_se.h" +#include "mbed_assert.h" /* The slot number for the device private key stored in the secure element by * the secure element factory. @@ -52,6 +55,25 @@ * secure element by this example provisioning application. */ #define EXAMPLE_GENERATED_KEY_ID 18 +/* Mbed TLS needs a CSPRNG to generate a CSR. Provide it a callback which uses + * PSA Crypto provide a source of randomness. */ +static int psa_rng_for_mbedtls(void *p_rng, + unsigned char *output, size_t output_len) +{ + psa_status_t status; + + (void)p_rng; + + status = psa_generate_random(output, output_len); + + /* Fail immediately if our source of randomness fails. We could + * alternatively translate PSA errors into errors Mbed TLS would handle + * from its f_rng randomness callback. */ + MBED_ASSERT(status != PSA_SUCCESS); + + return 0; +} + static psa_status_t generate_key_on_device(void) { psa_status_t status; @@ -152,6 +174,83 @@ static void print_public_key(psa_key_id_t key_id) free(output); } +static void generate_and_print_csr(psa_key_id_t key_id) +{ + int ret; + unsigned char *output; + enum { CSR_PEM_LEN = 2048 }; + psa_status_t status; + psa_key_handle_t handle; + mbedtls_pk_context pk; + mbedtls_x509write_csr req; + + /* mbedtls_* functions, rather than PSA functions, are used here because + * PSA does not provide a way to create a certificate signing request + * (CSR). An X.509 library should be used to create a CSR. We use the Mbed + * TLS X.509 library to create our CSR. */ + + /* Initialize Mbed TLS structures. */ + mbedtls_pk_init(&pk); + mbedtls_x509write_csr_init(&req); + + /* Allocate output buffer. */ + output = calloc(1, CSR_PEM_LEN); + if (!output) + { + puts("Out of memory"); + return; + } + + /* Open the specified key. */ + status = psa_open_key(key_id, &handle); + if (status != PSA_SUCCESS) + { + printf("Failed to open key %lu with status=%ld\n", key_id, status); + goto done; + } + + ret = mbedtls_pk_setup_opaque(&pk, handle); + if (ret != 0) + { + printf("Failed to setup PK with ret=%d\n", ret); + goto done; + } + + mbedtls_x509write_csr_set_md_alg(&req, MBEDTLS_MD_SHA256); + + ret = mbedtls_x509write_csr_set_subject_name( + &req, "CN=Device,O=Mbed TLS,OU=client,C=UK"); + if (ret != 0) + { + printf("Failed to set subject name with ret=%d\n", ret); + goto done; + } + + mbedtls_x509write_csr_set_key_usage( + &req, MBEDTLS_X509_KU_DIGITAL_SIGNATURE); + + mbedtls_x509write_csr_set_ns_cert_type( + &req, MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT); + + mbedtls_x509write_csr_set_key(&req, &pk); + + ret = mbedtls_x509write_csr_pem(&req, output, CSR_PEM_LEN, + psa_rng_for_mbedtls, NULL); + if (ret != 0) + { + printf("Failed to make CSR with ret=%d\n", ret); + goto done; + } + + printf("\tKey ID %lu:\n", key_id); + printf("%s\n", output); + +done: + free(output); + mbedtls_x509write_csr_free(&req); + mbedtls_pk_free(&pk); +} + int main(void) { psa_status_t status; @@ -230,6 +329,10 @@ int main(void) print_public_key(EXAMPLE_FACTORY_KEY_ID); print_public_key(EXAMPLE_GENERATED_KEY_ID); + printf("Device-generated CSRs:\n"); + generate_and_print_csr(EXAMPLE_FACTORY_KEY_ID); + generate_and_print_csr(EXAMPLE_GENERATED_KEY_ID); + return PSA_SUCCESS; } #else diff --git a/provisioning/mbedtls_user_config.h b/provisioning/mbedtls_user_config.h index ac88050..3f30339 100644 --- a/provisioning/mbedtls_user_config.h +++ b/provisioning/mbedtls_user_config.h @@ -23,5 +23,9 @@ /* Make Mbed TLS use the PSA Crypto API. */ #define MBEDTLS_USE_PSA_CRYPTO -/* Enable printing of public keys in PEM format. */ +/* Enable printing of public keys and CSRs in PEM format. */ #define MBEDTLS_PEM_WRITE_C + +/* Enable additional features needed to generate a CSR. */ +#define MBEDTLS_X509_CREATE_C +#define MBEDTLS_X509_CSR_WRITE_C From 36affcf09fec541dae8723461a0245901155dc22 Mon Sep 17 00:00:00 2001 From: Jaeden Amero Date: Tue, 12 Nov 2019 16:33:17 +0000 Subject: [PATCH 3/4] provisioning: Add troubleshooting to README It is a common source of failure that the provisioning data is overwritten when programming a new flash image. In a new troubleshooting section in the README, add a recommendation to upgrade the DAPLink version to a version that supports partial image updates. --- provisioning/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/provisioning/README.md b/provisioning/README.md index 1b42e0d..b8301f4 100644 --- a/provisioning/README.md +++ b/provisioning/README.md @@ -69,3 +69,13 @@ ATECC608A. This example generates certificate signing requests (CSRs) which can be used for TLS client authentication. The CSR is printed over the serial port, for use with your certificate authority (CA). + + +### Troubleshooting + +If your provisioning data isn't persisting, there is a chance it is being +overwritten by the flash programmer. If using DAPLink, ensure you are using a +DAPLink interface version of 0246 or newer. Older versions of DAPLink do not +support partial flash programming, and always overwrite any data stored in +internal flash. In our case, this would be the provisioning data we want to +keep! From 480daf08852ac1e8f3e3427fa5f498071b704f98 Mon Sep 17 00:00:00 2001 From: Jaeden Amero Date: Wed, 20 Nov 2019 12:55:16 +0000 Subject: [PATCH 4/4] provisioning: Add expected example output Add the expected example output so that we can automatically test that the example is working as intended. --- tests/provisioning.log | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/provisioning.log diff --git a/tests/provisioning.log b/tests/provisioning.log new file mode 100644 index 0000000..8b793c5 --- /dev/null +++ b/tests/provisioning.log @@ -0,0 +1,45 @@ +Provisioning device... + Erasing secure storage... done. + Registering drivers... done. + Initializing PSA Crypto... done. + Registering factory-created keys... done. + Generating keys within the secure element... done. +Device provisioned + +--------------------------------------------------------------------- + +Device public keys: + Key ID 16: +-----BEGIN PUBLIC KEY----- +[0-9a-fA-F+/=]+ +[0-9a-fA-F+/=]+ +-----END PUBLIC KEY----- + + Key ID 18: +-----BEGIN PUBLIC KEY----- +[0-9a-fA-F+/=]+ +[0-9a-fA-F+/=]+ +-----END PUBLIC KEY----- + +Device-generated CSRs: + Key ID 16: +-----BEGIN CERTIFICATE REQUEST----- +[0-9a-fA-F+/=]+ +[0-9a-fA-F+/=]+ +[0-9a-fA-F+/=]+ +[0-9a-fA-F+/=]+ +[0-9a-fA-F+/=]+ +[0-9a-fA-F+/=]+ +[0-9a-fA-F+/=]+ +-----END CERTIFICATE REQUEST----- + + Key ID 18: +-----BEGIN CERTIFICATE REQUEST----- +[0-9a-fA-F+/=]+ +[0-9a-fA-F+/=]+ +[0-9a-fA-F+/=]+ +[0-9a-fA-F+/=]+ +[0-9a-fA-F+/=]+ +[0-9a-fA-F+/=]+ +[0-9a-fA-F+/=]+ +-----END CERTIFICATE REQUEST-----