| /** |
| * @file tlsSe050Client.cpp |
| * @author NXP Semiconductors |
| * @version 1.0 |
| * @par License |
| * |
| * Copyright 2019,2020 NXP |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * @par Description: |
| * Simple TLS client |
| * - Fetch client certificate from provisioned SE |
| * - Load OpenSSL engine |
| * - Establish TLS connection (use client key pair provisioned in SE) and send message#1 |
| * - Close TLS connection |
| * - Close comm. link of OpenSSL engine with SE |
| * - Fetch client certificate from provisioned SE |
| * - Re-open comm. link of OpenSSL engine with SE |
| * - Establish TLS connection (use client key pair provisioned in SE) and send message#2 |
| * - Close TLS connection |
| * |
| * Precondition: |
| * - SE (SE050 or A71CH) has been provisioned with appropriate credentials |
| * - Server side has been setup and has been provisioned with appropriate credentials |
| * |
| */ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/ioctl.h> |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <netdb.h> |
| #include <fcntl.h> |
| #include <openssl/ssl.h> |
| #include <openssl/engine.h> |
| |
| #define SSS_USE_FTR_FILE |
| #define FTR_FILE_SYSTEM |
| |
| #include "snw_common.h" |
| |
| /* App version */ |
| const int v_major = 0; |
| const int v_minor = 9; |
| const int v_patch = 2; |
| |
| int certVerifyCb(int preverifyOk, X509_STORE_CTX *pX509CTX) |
| { |
| char szData[256]; |
| X509 *cert = X509_STORE_CTX_get_current_cert(pX509CTX); |
| int depth = X509_STORE_CTX_get_error_depth(pX509CTX); |
| |
| if (!preverifyOk) { |
| int err = X509_STORE_CTX_get_error(pX509CTX); |
| printf("Error with certificate at depth: %i\n", depth); |
| X509_NAME_oneline(X509_get_issuer_name(cert), szData, 256); |
| printf(" issuer = %s\n", szData); |
| X509_NAME_oneline(X509_get_subject_name(cert), szData, 256); |
| printf(" subject = %s\n", szData); |
| printf(" err %i:%s\n", err, X509_verify_cert_error_string(err)); |
| } |
| else { |
| printf("Certificate at depth: %i\n", depth); |
| X509_NAME_oneline(X509_get_issuer_name(cert), szData, 256); |
| printf(" issuer = %s\n", szData); |
| X509_NAME_oneline(X509_get_subject_name(cert), szData, 256); |
| printf(" subject = %s\n", szData); |
| } |
| return preverifyOk; |
| } |
| |
| #define MAX_DER_CERT_SIZE 1024 |
| #define MAX_DATA_OBJECT_SIZE 1024 |
| |
| int main(int argc, char **argv) |
| { |
| int nStatus = 0; |
| ENGINE *e; |
| const SSL_METHOD *method; |
| SSL_CTX *pSSLContext; |
| SSL *pSSLHandle; |
| char pDestinationURL[256]; |
| int nDestinationPort = 8080; |
| int nRet = SE_TLS_CLIENT_OK; |
| int sockfd; |
| char fileRootCA[256]; |
| char keyType[256]; |
| char fileClientKey[256]; |
| X509 *certRcv; |
| char clientMessage[256]; |
| int clientMessageLen; |
| char engineId[256]; |
| U8 clientCerDer[MAX_DER_CERT_SIZE]; |
| size_t clientCerDerLen = MAX_DER_CERT_SIZE; |
| char clientCertFile[256]; // Optional filename of client certificate |
| int certId = 0; |
| U8 dataObject[MAX_DATA_OBJECT_SIZE]; |
| U16 dataObjectLen = MAX_DATA_OBJECT_SIZE; |
| int objectIndex = 0; |
| U16 dataOffset; |
| U16 dataLen; |
| U8 clientCerDer_2[MAX_DER_CERT_SIZE]; |
| size_t clientCerDer_2Len = MAX_DER_CERT_SIZE; |
| ex_sss_boot_ctx_t gdirectSeSessionCtx; |
| |
| printf("%s (Rev.%d.%d.%d)\n", argv[0], v_major, v_minor, v_patch); |
| |
| // Evaluate command line arguments |
| if (argc < 5 || argc > 7) { |
| printf("Usage: %s <ipAddress:port> <EC|RSA> <caCert.pem> <clientKey.pem|clientKeyRef.pem> [<clientCert.pem>]\n", argv[0]); |
| nRet = SE_TLS_CLIENT_CMDLINE_ARGS; |
| goto leaving; |
| } |
| |
| memset(&gdirectSeSessionCtx, 0, sizeof(ex_sss_boot_ctx_t)); |
| |
| // Deal with first argument: <address|name>:<port> |
| // *********************************************** |
| { |
| unsigned int i; |
| int fColonFound = 0; |
| int nSuccess; |
| |
| if (strlen(argv[1]) >= sizeof(pDestinationURL)) { |
| // A bit too cautious (as port number may also be attached) |
| nRet = SE_TLS_CLIENT_BUFFER_TOO_SMALL; |
| goto leaving; |
| } |
| |
| for (i = 0; i < strlen(argv[1]); i++) { |
| if (argv[1][i] == ':') { |
| strncpy(pDestinationURL, argv[1], i); |
| pDestinationURL[i] = 0; |
| fColonFound = 1; |
| // printf("servername/address: %s\n", pDestinationURL); |
| break; |
| } |
| else { |
| // Simply copy the full argument |
| strcpy(pDestinationURL, argv[1]); |
| } |
| } |
| |
| if ((fColonFound == 1) && (i != 0)) { |
| nSuccess = sscanf(&argv[1][i], ":%5u[0-9]", (unsigned int *)&nDestinationPort); |
| if (nSuccess != 1) { |
| nRet = SE_TLS_CLIENT_ILLEGAL_PORT_NR; |
| goto leaving; |
| } |
| } |
| else { |
| // Choose default value for port |
| nDestinationPort = 8080; |
| } |
| } |
| printf("\t servername:port = %s:%d\n", pDestinationURL, nDestinationPort); |
| |
| // Deal with second argument: key type (either EC or RSA for now) |
| // *************************************************************** |
| if (strlen(argv[2]) >= sizeof(keyType)) { |
| nRet = SE_TLS_CLIENT_BUFFER_TOO_SMALL; |
| goto leaving; |
| } |
| strcpy(keyType, argv[2]); |
| if ( (strcmp(keyType, "RSA") != 0) && (strcmp(keyType, "EC") != 0) ) { |
| printf("\t keyType: %s is not defined, choose from EC or RSA.\n", keyType); |
| nRet = SE_TLS_CLIENT_CMDLINE_ARGS; |
| goto leaving; |
| } |
| printf("\t keyType: %s\n", keyType); |
| |
| // Deal with third argument: root CA certificate |
| // ********************************************** |
| if (strlen(argv[3]) >= sizeof(fileRootCA)) { |
| nRet = SE_TLS_CLIENT_BUFFER_TOO_SMALL; |
| goto leaving; |
| } |
| strcpy(fileRootCA, argv[3]); |
| printf("\t rootCA: %s\n", fileRootCA); |
| |
| // Deal with fourth argument: key or reference key |
| // ********************************************** |
| if (strlen(argv[4]) >= sizeof(fileClientKey)) { |
| nRet = SE_TLS_CLIENT_BUFFER_TOO_SMALL; |
| goto leaving; |
| } |
| strcpy(fileClientKey, argv[4]); |
| printf("\t clientKey: %s\n", fileClientKey); |
| |
| // Deal with optional fifth argument |
| // ********************************** |
| if (argc == 6) { |
| if (strlen(argv[5]) >= sizeof(clientCertFile)) { |
| nRet = SE_TLS_CLIENT_BUFFER_TOO_SMALL; |
| goto leaving; |
| } |
| strcpy(clientCertFile, argv[5]); |
| printf("\t clientCertFile: %s\n", clientCertFile); |
| } |
| else { |
| clientCertFile[0] = 0; |
| } |
| |
| strcpy(clientMessage, "Hello to Server from TLS client using credentials stored in Secure Element.\n"); |
| clientMessageLen = strlen(clientMessage); |
| |
| nRet = wrapConnectToSe(&gdirectSeSessionCtx); |
| if (nRet != SE_TLS_CLIENT_OK) { |
| printf("Failed to connect to Secure Element.\n"); |
| goto leaving; |
| } |
| |
| // Ensure the matching TLS Client certificate has been provisioned before running the demo |
| if ( strcmp(keyType, "RSA") == 0) { |
| certId = OBJID_DEMO_RSA_TLS_CLIENT_CERT; |
| } |
| else if (strcmp(keyType, "EC") == 0) { |
| certId = OBJID_DEMO_EC_TLS_CLIENT_CERT; |
| } |
| else { |
| printf("Don't know about keyType %s (leaving)\n", keyType); |
| goto leaving; |
| } |
| // This initial certificate fetch fails on A71CH! |
| #if 1 |
| nRet = seGetClientCertificate(&(gdirectSeSessionCtx.ks), certId, clientCerDer, &clientCerDerLen); |
| if (nRet != SE_TLS_CLIENT_OK) { |
| printf("Failed to retrieve client certificate.\n"); |
| goto leaving; |
| } |
| #endif |
| |
| wrapDisconnectFromSe(&gdirectSeSessionCtx); |
| |
| SSL_load_error_strings(); |
| OpenSSL_add_all_algorithms(); |
| |
| if (SSL_library_init() < 0) { |
| nRet = SE_TLS_CLIENT_SSL_LIB_INIT_ERROR; |
| goto leaving; |
| } |
| |
| #if (OPENSSL_VERSION_NUMBER < 0x10101000L) |
| method = TLSv1_2_method(); |
| #else |
| method = TLS_method(); |
| #endif |
| |
| if ((pSSLContext = SSL_CTX_new(method)) == NULL) { |
| printf("SSL_CTX_new failed - Unable to create SSL Context\n"); |
| nRet = SE_TLS_CLIENT_SSL_INIT_ERROR; |
| goto leaving; |
| } |
| |
| #if SSS_HAVE_A71CH |
| strcpy(engineId, "e4sss_a71ch"); |
| #elif SSS_HAVE_SE05X |
| strcpy(engineId, "e4sss"); |
| #endif |
| |
| if (!(e = ENGINE_by_id(engineId))) |
| { |
| fprintf(stderr, "Error finding OpenSSL Engine by id (id = %s)\n", engineId); |
| nRet = SE_ERR_SE_LOADING_ENGINE_ERR; |
| goto leaving; |
| } |
| else |
| { |
| unsigned int logLevel = 0x05; |
| fprintf(stderr, "Setting log level OpenSSL-engine %s to 0x%02X.\n", engineId, logLevel); |
| // Illustrates setting log level of e4sss engine. |
| // 0x04 : Error message |
| // 0x02 : Debug messages |
| // 0x01 : Flow messages |
| // (0x07 corresponds to all, which is the default) |
| ENGINE_ctrl(e, ENGINE_CMD_BASE, logLevel, NULL, NULL); |
| } |
| |
| sockfd = socket(AF_INET, SOCK_STREAM, 0); |
| if (sockfd == -1) { |
| nRet = SE_TLS_CLIENT_TCP_SETUP_ERROR; |
| goto leaving; |
| } |
| |
| if (!SSL_CTX_load_verify_locations(pSSLContext, fileRootCA, NULL)) { |
| nRet = SE_TLS_CLIENT_SSL_CERT_ERROR; |
| goto leaving; |
| } |
| |
| if (clientCertFile[0] != 0) { |
| if (!SSL_CTX_use_certificate_file(pSSLContext, clientCertFile, SSL_FILETYPE_PEM)) { |
| printf("Client Certificate (%s) Loading error.\n", clientCertFile); |
| nRet = SE_TLS_CLIENT_SSL_CERT_ERROR; |
| goto leaving; |
| } |
| } |
| else if (!SSL_CTX_use_certificate_ASN1(pSSLContext, clientCerDerLen, clientCerDer)) { |
| printf("Client Certificate Loading error.\n"); |
| nRet = SE_TLS_CLIENT_SSL_CERT_ERROR; |
| goto leaving; |
| } |
| |
| if (SSL_CTX_use_PrivateKey_file(pSSLContext, fileClientKey, SSL_FILETYPE_PEM) != 1){ |
| printf("Client Private Key Loading error.\n"); |
| nRet = SE_TLS_CLIENT_SSL_KEY_ERROR; |
| goto leaving; |
| } |
| |
| SSL_CTX_set_verify(pSSLContext, SSL_VERIFY_PEER, certVerifyCb); |
| // SSL_CTX_set_verify(pSSLContext, SSL_VERIFY_PEER, NULL); |
| |
| pSSLHandle = SSL_new(pSSLContext); |
| |
| nRet = snwTcpConnect(sockfd, pDestinationURL, nDestinationPort); |
| if (nRet != SE_TLS_CLIENT_OK) { |
| printf("TCP Connection error.\n"); |
| goto leaving; |
| } |
| |
| SSL_set_fd(pSSLHandle, sockfd); |
| |
| nRet = snwSetSocketNonBlocking(sockfd); |
| if (nRet != SE_TLS_CLIENT_OK) { |
| printf("Unable to set the socket to Non-Blocking.\n"); |
| goto leaving; |
| } |
| |
| nRet = snwSslConnect(pSSLHandle, sockfd, 2000); |
| if (nRet != SE_TLS_CLIENT_OK) { |
| printf("Unable to establish ssl session.\n"); |
| goto leaving; |
| } |
| |
| if (X509_V_OK != SSL_get_verify_result(pSSLHandle)) { |
| printf("Server Certificate Verification failed"); |
| nRet = SE_TLS_CLIENT_SSL_CONNECT_ERROR; |
| goto leaving; |
| } |
| |
| // Ensure a valid certificate was returned, otherwise no certificate exchange happened! |
| if (NULL == (certRcv = SSL_get_peer_certificate(pSSLHandle)) ) { |
| printf(" No certificate exchange happened"); |
| nRet = SE_TLS_CLIENT_SSL_CONNECT_ERROR; |
| goto leaving; |
| } |
| else { |
| X509_NAME *subj; |
| char szData[256]; |
| |
| if ((subj = X509_get_subject_name(certRcv)) && |
| (X509_NAME_get_text_by_NID(subj, NID_commonName, szData, 256) > 0)) { |
| printf("Peer Certificate CN: %s\n", szData); |
| } |
| X509_free(certRcv); |
| } |
| |
| nRet = snwSslWrite(pSSLHandle, sockfd, (unsigned char *)clientMessage, &clientMessageLen, 2000); |
| if (nRet != SE_TLS_CLIENT_OK) { |
| printf("Unable to send message to server: nRet = %d.\n", nRet); |
| goto leaving; |
| } |
| |
| nRet = SSL_shutdown(pSSLHandle); |
| if (nRet == 0) { |
| sleep(1); |
| printf("SSL_shutdown: Repeat shutdown.\n"); |
| nRet = SSL_shutdown(pSSLHandle); |
| } |
| |
| if (nRet == 1) { |
| printf("SSL_shutdown: successful.\n"); |
| nRet = SE_TLS_CLIENT_OK; |
| } |
| else if (nRet == 0) { |
| printf("SSL_shutdown: not yet finished (second attempt).\n"); |
| } |
| else { |
| int errorCode; |
| errorCode = SSL_get_error(pSSLHandle, nRet); |
| printf("SSL_shutdown: error on shutdown (nRet=%d, SSL_error_code=%d)\n", |
| nRet, errorCode); |
| // if (errorCode == SSL_ERROR_ZERO_RETURN ) |
| } |
| |
| nRet = SSL_clear(pSSLHandle); |
| if (nRet == 0) { |
| int errorCode; |
| printf("SSL_clear failed.\n"); |
| errorCode = SSL_get_error(pSSLHandle, nRet); |
| printf("errorCode: %d\n", errorCode); |
| } |
| else if (nRet == 1) { |
| printf("SSL_clear successful.\n"); |
| nRet = SE_TLS_CLIENT_OK; |
| } |
| |
| // NOTE: Close engine connection to SE via Engine control interface |
| fprintf(stderr, "Close connection to secure element through Engine control interface (Engine=%s).\n", engineId); |
| ENGINE_ctrl(e, ENGINE_CMD_BASE+2, 0, NULL, NULL); |
| |
| // To demonstrate one can alternate OpenSSL engine access and direct access |
| // to the SE, another direct call to the SE |
| printf("Re-establish connection to SE ...\n"); |
| |
| nRet = wrapConnectToSe(&gdirectSeSessionCtx); |
| if (nRet != SE_TLS_CLIENT_OK) { |
| printf("Failed to connect to Secure Element.\n"); |
| goto leaving; |
| } |
| |
| printf("Successfully established connection to SE\n"); |
| { |
| nRet = seGetClientCertificate(&(gdirectSeSessionCtx.ks), certId, clientCerDer_2, &clientCerDer_2Len); |
| if (nRet != SE_TLS_CLIENT_OK) { |
| printf("Failed to retrieve client certificate.\n"); |
| goto leaving; |
| } |
| snwPrintDataAsHex(clientCerDer_2, clientCerDer_2Len); |
| } |
| |
| wrapDisconnectFromSe(&gdirectSeSessionCtx); |
| |
| // NOTE: Open engine connection to SE via Engine control interface |
| fprintf(stderr, "Open connection to secure element through Engine control interface (Engine=%s).\n", engineId); |
| ENGINE_ctrl(e, ENGINE_CMD_BASE+1, 0, NULL, NULL); |
| |
| if ((pSSLContext = SSL_CTX_new(method)) == NULL) { |
| printf("SSL_CTX_new failed - Unable to create SSL Context\n"); |
| nRet = SE_TLS_CLIENT_SSL_INIT_ERROR; |
| goto leaving; |
| } |
| |
| sockfd = socket(AF_INET, SOCK_STREAM, 0); |
| if (sockfd == -1) { |
| nRet = SE_TLS_CLIENT_TCP_SETUP_ERROR; |
| goto leaving; |
| } |
| |
| if (!SSL_CTX_load_verify_locations(pSSLContext, fileRootCA, NULL)) { |
| nRet = SE_TLS_CLIENT_SSL_CERT_ERROR; |
| goto leaving; |
| } |
| |
| if (clientCertFile[0] != 0) { |
| if (!SSL_CTX_use_certificate_file(pSSLContext, clientCertFile, SSL_FILETYPE_PEM)) { |
| printf("Client Certificate (%s) Loading error.\n", clientCertFile); |
| nRet = SE_TLS_CLIENT_SSL_CERT_ERROR; |
| goto leaving; |
| } |
| } |
| |
| else if (!SSL_CTX_use_certificate_ASN1(pSSLContext, clientCerDer_2Len, clientCerDer_2)) { |
| printf("Client Certificate Loading error.\n"); |
| nRet = SE_TLS_CLIENT_SSL_CERT_ERROR; |
| goto leaving; |
| } |
| |
| if (SSL_CTX_use_PrivateKey_file(pSSLContext, fileClientKey, SSL_FILETYPE_PEM) != 1){ |
| printf("Client Private Key Loading error.\n"); |
| nRet = SE_TLS_CLIENT_SSL_KEY_ERROR; |
| goto leaving; |
| } |
| |
| SSL_CTX_set_verify(pSSLContext, SSL_VERIFY_PEER, certVerifyCb); |
| // SSL_CTX_set_verify(pSSLContext, SSL_VERIFY_PEER, NULL); |
| |
| pSSLHandle = SSL_new(pSSLContext); |
| |
| nRet = snwTcpConnect(sockfd, pDestinationURL, nDestinationPort); |
| if (nRet != SE_TLS_CLIENT_OK) { |
| printf("TCP Connection error.\n"); |
| goto leaving; |
| } |
| |
| SSL_set_fd(pSSLHandle, sockfd); |
| |
| nRet = snwSetSocketNonBlocking(sockfd); |
| if (nRet != SE_TLS_CLIENT_OK) { |
| printf("Unable to set the socket to Non-Blocking.\n"); |
| goto leaving; |
| } |
| |
| nRet = snwSslConnect(pSSLHandle, sockfd, 2000); |
| if (nRet != SE_TLS_CLIENT_OK) { |
| printf("Unable to establish ssl session.\n"); |
| goto leaving; |
| } |
| |
| if (X509_V_OK != SSL_get_verify_result(pSSLHandle)) { |
| printf("Server Certificate Verification failed"); |
| nRet = SE_TLS_CLIENT_SSL_CONNECT_ERROR; |
| goto leaving; |
| } |
| |
| // Ensure a valid certificate was returned, otherwise no certificate exchange happened! |
| if (NULL == (certRcv = SSL_get_peer_certificate(pSSLHandle)) ) { |
| printf(" No certificate exchange happened"); |
| nRet = SE_TLS_CLIENT_SSL_CONNECT_ERROR; |
| goto leaving; |
| } |
| else { |
| X509_NAME *subj; |
| char szData[256]; |
| |
| if ((subj = X509_get_subject_name(certRcv)) && |
| (X509_NAME_get_text_by_NID(subj, NID_commonName, szData, 256) > 0)) { |
| printf("Peer Certificate CN: %s\n", szData); |
| } |
| X509_free(certRcv); |
| } |
| |
| strcpy(clientMessage, "Another message from TLS client using credentials stored in Secure Element.\n"); |
| clientMessageLen = strlen(clientMessage); |
| |
| nRet = snwSslWrite(pSSLHandle, sockfd, (unsigned char *)clientMessage, &clientMessageLen, 2000); |
| if (nRet != SE_TLS_CLIENT_OK) { |
| printf("Unable to send message to server: nRet = %d.\n", nRet); |
| goto leaving; |
| } |
| |
| nRet = SSL_shutdown(pSSLHandle); |
| if (nRet == 0) { |
| sleep(1); |
| printf("SSL_shutdown: Repeat shutdown.\n"); |
| nRet = SSL_shutdown(pSSLHandle); |
| } |
| |
| if (nRet == 1) { |
| printf("SSL_shutdown: successful.\n"); |
| nRet = SE_TLS_CLIENT_OK; |
| } |
| else if (nRet == 0) { |
| printf("SSL_shutdown: not yet finished (second attempt).\n"); |
| } |
| else { |
| int errorCode; |
| errorCode = SSL_get_error(pSSLHandle, nRet); |
| printf("SSL_shutdown: error on shutdown (nRet=%d, SSL_error_code=%d)\n", |
| nRet, errorCode); |
| // if (errorCode == SSL_ERROR_ZERO_RETURN ) |
| } |
| |
| nRet = SSL_clear(pSSLHandle); |
| if (nRet == 0) { |
| int errorCode; |
| printf("SSL_clear failed.\n"); |
| errorCode = SSL_get_error(pSSLHandle, nRet); |
| printf("errorCode: %d\n", errorCode); |
| } |
| else if (nRet == 1) { |
| printf("SSL_clear successful.\n"); |
| nRet = SE_TLS_CLIENT_OK; |
| } |
| |
| printf("\n\t servername:port = %s:%d\n", pDestinationURL, nDestinationPort); |
| printf("\t rootCA: %s\n", fileRootCA); |
| // printf("\t Retrieved client certificate from SE (stored at index 0)\n"); |
| printf("\t clientKey: %s\n\n", fileClientKey); |
| |
| leaving: |
| printf("\n******** TLS Client Example (Credentials in Secure Element) = %s ********\n", |
| (nRet == SE_TLS_CLIENT_OK) ? "Pass" : "Fail"); |
| nStatus = (nRet == SE_TLS_CLIENT_OK) ? OK_STATUS : FAILURE_STATUS; |
| |
| return nStatus; |
| } |