Skip to content

Commit 4caac60

Browse files
Add support for EVP_PKEY for curl (#1698)
Allows to specify custom key handle to control SSL handshake. Compile-time to avoid exposing OpenSSL unnecessarily. Relates-To: HERESUP-72189 Signed-off-by: Andrey Kashcheev <ext-andrey.kashcheev@here.com>
1 parent 112dc7b commit 4caac60

5 files changed

Lines changed: 149 additions & 6 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ option(OLP_SDK_ENABLE_DEFAULT_CACHE_LMDB "Enable default cache implementation ba
5050
option(OLP_SDK_ENABLE_ANDROID_CURL "Enable curl based network layer for Android" OFF)
5151
option(OLP_SDK_ENABLE_IOS_BACKGROUND_DOWNLOAD "Enable iOS network layer downloading in background. Under testing." OFF)
5252
option(OLP_SDK_ENABLE_OFFLINE_MODE "Enable offline mode. Network layer is excluded from the build and all network requests returned with error." OFF)
53+
option(OLP_SDK_ENABLE_ENVELOPE_PKEY "Enable support for envelope pkey for curl based network layer." OFF)
5354
5455
# C++ standard version. Minimum supported version is 11.
5556
if (NOT CMAKE_CXX_STANDARD)

olp-cpp-sdk-core/include/olp/core/Config.h.in

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2021 HERE Europe B.V.
2+
* Copyright (C) 2021-2026 HERE Europe B.V.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,4 +17,7 @@
1717
* License-Filename: LICENSE
1818
*/
1919

20+
#pragma once
21+
2022
#cmakedefine OLP_SDK_ENABLE_DEFAULT_CACHE
23+
#cmakedefine OLP_SDK_ENABLE_ENVELOPE_PKEY

olp-cpp-sdk-core/include/olp/core/http/CertificateSettings.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2023 HERE Europe B.V.
2+
* Copyright (C) 2023-2026 HERE Europe B.V.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,12 @@
1919

2020
#pragma once
2121

22+
#include <olp/core/Config.h>
23+
24+
#ifdef OLP_SDK_ENABLE_ENVELOPE_PKEY
25+
#include <openssl/ossl_typ.h>
26+
#endif
27+
2228
#include <string>
2329

2430
#include "olp/core/CoreApi.h"
@@ -44,6 +50,13 @@ struct CORE_API CertificateSettings {
4450
* @brief The CA file as blob.
4551
*/
4652
std::string cert_file_blob;
53+
54+
#ifdef OLP_SDK_ENABLE_ENVELOPE_PKEY
55+
/**
56+
* @brief The ENV_PKEY handle as a pointer.
57+
*/
58+
EVP_PKEY* pkey_handle = nullptr;
59+
#endif
4760
};
4861

4962
} // namespace http

olp-cpp-sdk-core/src/http/curl/NetworkCurl.cpp

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,16 @@
3333
#include <csignal>
3434
#endif
3535

36-
#ifdef OLP_SDK_USE_MD5_CERT_LOOKUP
36+
#if defined(OLP_SDK_ENABLE_ENVELOPE_PKEY) || \
37+
defined(OLP_SDK_USE_MD5_CERT_LOOKUP)
3738
#include <openssl/ssl.h>
39+
#endif
40+
41+
#ifdef OLP_SDK_ENABLE_ENVELOPE_PKEY
42+
#include <openssl/err.h>
43+
#endif
44+
45+
#ifdef OLP_SDK_USE_MD5_CERT_LOOKUP
3846
#include <openssl/x509_vfy.h>
3947
#include <sys/stat.h>
4048
#include <cstdio>
@@ -50,7 +58,6 @@
5058

5159
namespace olp {
5260
namespace http {
53-
5461
namespace {
5562

5663
const char* kLogTag = "CURL";
@@ -785,7 +792,18 @@ ErrorCode NetworkCurl::SendImplementation(
785792
}
786793

787794
#ifdef OLP_SDK_CURL_HAS_SUPPORT_SSL_BLOBS
795+
#ifdef OLP_SDK_ENABLE_ENVELOPE_PKEY
796+
// In VSM/EVP_PKEY mode the private key is injected via the
797+
// InjectEnvelopeKey SSL_CTX_FUNCTION callback. Curl cannot combine
798+
// CURLOPT_SSLCERT_BLOB (cert-only PEM) with a missing CURLOPT_SSLKEY_BLOB:
799+
// it tries to extract the private key from the cert blob and fails with
800+
// "unable to set private key file: '(memory blob)' type PEM".
801+
// Therefore, skip the entire blob-based cert setup and let InjectEnvelopeKey
802+
// set both the client certificate and the EVP_PKEY via SSL_CTX_*.
803+
if (ssl_certificates_blobs_ && !certificate_settings_.pkey_handle) {
804+
#else
788805
if (ssl_certificates_blobs_) {
806+
#endif
789807
curl_easy_setopt(
790808
curl_handle, CURLOPT_SSLCERT_BLOB,
791809
olp::porting::get_ptr(ssl_certificates_blobs_->ssl_cert_blob));
@@ -810,7 +828,13 @@ ErrorCode NetworkCurl::SendImplementation(
810828
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 1L);
811829
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 2L);
812830

813-
#ifdef OLP_SDK_USE_MD5_CERT_LOOKUP
831+
#ifdef OLP_SDK_ENABLE_ENVELOPE_PKEY
832+
if (certificate_settings_.pkey_handle) {
833+
curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_FUNCTION,
834+
&NetworkCurl::InjectEnvelopeKey);
835+
curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_DATA, handle);
836+
}
837+
#elif defined(OLP_SDK_USE_MD5_CERT_LOOKUP)
814838
curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_FUNCTION,
815839
&NetworkCurl::AddMd5LookupMethod);
816840
curl_easy_setopt(curl_handle, CURLOPT_SSL_CTX_DATA, handle);
@@ -1464,5 +1488,90 @@ CURLcode NetworkCurl::AddMd5LookupMethod(CURL*, SSL_CTX* ssl_ctx,
14641488
}
14651489
#endif
14661490

1491+
#ifdef OLP_SDK_ENABLE_ENVELOPE_PKEY
1492+
CURLcode NetworkCurl::InjectEnvelopeKey(CURL*, SSL_CTX* ssl_ctx,
1493+
RequestHandle* handle) {
1494+
auto self = handle->self.lock();
1495+
if (!self) {
1496+
OLP_SDK_LOG_ERROR(kLogTag, "Unable to lock cURL handle");
1497+
return CURLE_ABORTED_BY_CALLBACK;
1498+
}
1499+
1500+
const auto& cert_blob = self->certificate_settings_.client_cert_file_blob;
1501+
if (!cert_blob.empty()) {
1502+
BIO* bio =
1503+
BIO_new_mem_buf(cert_blob.data(), static_cast<int>(cert_blob.size()));
1504+
if (!bio) {
1505+
OLP_SDK_LOG_ERROR(kLogTag, "InjectEnvelopeKey: BIO_new_mem_buf failed");
1506+
return CURLE_SSL_CERTPROBLEM;
1507+
}
1508+
X509* cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
1509+
BIO_free(bio);
1510+
if (!cert) {
1511+
OLP_SDK_LOG_ERROR(kLogTag,
1512+
"InjectEnvelopeKey: PEM_read_bio_X509 failed, error="
1513+
<< ERR_lib_error_string(ERR_get_error()));
1514+
return CURLE_SSL_CERTPROBLEM;
1515+
}
1516+
int rc = SSL_CTX_use_certificate(ssl_ctx, cert);
1517+
X509_free(cert);
1518+
if (rc != 1) {
1519+
OLP_SDK_LOG_ERROR(
1520+
kLogTag, "InjectEnvelopeKey: SSL_CTX_use_certificate failed, error="
1521+
<< ERR_lib_error_string(ERR_get_error()));
1522+
return CURLE_SSL_CERTPROBLEM;
1523+
}
1524+
}
1525+
1526+
const auto& ca_blob = self->certificate_settings_.cert_file_blob;
1527+
if (!ca_blob.empty()) {
1528+
BIO* bio =
1529+
BIO_new_mem_buf(ca_blob.data(), static_cast<int>(ca_blob.size()));
1530+
if (!bio) {
1531+
OLP_SDK_LOG_WARNING(
1532+
kLogTag,
1533+
"InjectEnvelopeKey: BIO_new_mem_buf failed for CA blob"
1534+
" — falling back to system CA bundle");
1535+
} else {
1536+
X509_STORE* store = SSL_CTX_get_cert_store(ssl_ctx);
1537+
int ca_count = 0;
1538+
X509* ca = nullptr;
1539+
while ((ca = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr)) !=
1540+
nullptr) {
1541+
if (store) {
1542+
X509_STORE_add_cert(store, ca);
1543+
}
1544+
X509_free(ca);
1545+
++ca_count;
1546+
}
1547+
BIO_free(bio);
1548+
// Clear EOF / "cert already in hash table" errors left by the loop
1549+
ERR_clear_error();
1550+
if (ca_count == 0) {
1551+
OLP_SDK_LOG_WARNING(kLogTag,
1552+
"InjectEnvelopeKey: no CA certificates parsed from"
1553+
" ca_blob — peer verification may fail");
1554+
} else {
1555+
OLP_SDK_LOG_DEBUG(kLogTag, "InjectEnvelopeKey: added "
1556+
<< ca_count
1557+
<< " CA cert(s) to trust store");
1558+
}
1559+
}
1560+
}
1561+
1562+
// Clear the error queue before calling SSL_CTX_use_PrivateKey so that
1563+
// ERR_get_error() in the error path below reflects the actual key error
1564+
ERR_clear_error();
1565+
if (SSL_CTX_use_PrivateKey(ssl_ctx,
1566+
self->certificate_settings_.pkey_handle) != 1) {
1567+
OLP_SDK_LOG_ERROR(kLogTag, "Failed to use provided EVP_PKEY, error="
1568+
<< ERR_lib_error_string(ERR_get_error()));
1569+
return CURLE_SSL_CERTPROBLEM;
1570+
}
1571+
1572+
return CURLE_OK;
1573+
}
1574+
#endif
1575+
14671576
} // namespace http
14681577
} // namespace olp

olp-cpp-sdk-core/src/http/curl/NetworkCurl.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,13 @@
3333

3434
#include <olp/core/porting/optional.h>
3535

36-
#if defined(OLP_SDK_ENABLE_ANDROID_CURL) && !defined(ANDROID_HOST)
36+
#if (defined(OLP_SDK_ENABLE_ANDROID_CURL) && !defined(ANDROID_HOST)) || \
37+
defined(OLP_SDK_ENABLE_ENVELOPE_PKEY)
3738
#include <openssl/ossl_typ.h>
39+
#endif
3840

41+
#if defined(OLP_SDK_ENABLE_ANDROID_CURL) && !defined(ANDROID_HOST) && \
42+
!defined(OLP_SDK_ENABLE_ENVELOPE_PKEY)
3943
#ifdef OPENSSL_NO_MD5
4044
#error cURL enabled network implementation for Android requires MD5 from OpenSSL
4145
#endif
@@ -317,6 +321,19 @@ class NetworkCurl : public Network,
317321
RequestHandle* handle);
318322
#endif
319323

324+
#ifdef OLP_SDK_ENABLE_ENVELOPE_PKEY
325+
/**
326+
* @brief Injects EVP_PKEY and certificates into SSL context.
327+
*
328+
* @param[in] curl cURL instance.
329+
* @param[in] ssl_ctx OpenSSL context.
330+
* @param[in] handle Related RequestHandle.
331+
* @return An error code for the operation.
332+
*/
333+
static CURLcode InjectEnvelopeKey(CURL* curl, SSL_CTX* ssl_ctx,
334+
RequestHandle* handle);
335+
#endif
336+
320337
/// Contexts for every network request.
321338
std::vector<RequestHandle> handles_;
322339

0 commit comments

Comments
 (0)