aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib
diff options
context:
space:
mode:
authorTor Brede Vekterli <vekterli@verizonmedia.com>2020-02-20 12:03:41 +0000
committerTor Brede Vekterli <vekterli@verizonmedia.com>2020-02-20 14:23:24 +0000
commitae87223307b0fe3294e27d86719c3acb702ffed6 (patch)
treef7e4da24416919f2f2754cac3ad208e8baf6efc3 /vespalib
parentacfb0463e55aa4878cc3087ffcee356696eabf2a (diff)
Move crypto utility code out into vespalib and use for test credentials
Currently offers only the following functionality: * Generate P-256 EC private keys and export to PEM * Generate X509 certificates and export to PEM Instead of using hardcoded private key/certs for unit tests, use crypto utility code to generate new credentials once per test process. Since these certs now use a SAN of `localhost` it also means we no longer need to disable hostname validation for networked unit tests.
Diffstat (limited to 'vespalib')
-rw-r--r--vespalib/CMakeLists.txt2
-rw-r--r--vespalib/src/tests/crypto/CMakeLists.txt10
-rw-r--r--vespalib/src/tests/crypto/crypto_test.cpp37
-rw-r--r--vespalib/src/tests/net/tls/direct_buffer_bio/direct_buffer_bio_test.cpp1
-rw-r--r--vespalib/src/tests/net/tls/openssl_impl/CMakeLists.txt1
-rw-r--r--vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp5
-rw-r--r--vespalib/src/vespa/vespalib/CMakeLists.txt1
-rw-r--r--vespalib/src/vespa/vespalib/crypto/CMakeLists.txt11
-rw-r--r--vespalib/src/vespa/vespalib/crypto/crypto_exception.cpp (renamed from vespalib/src/vespa/vespalib/net/tls/crypto_exception.cpp)2
-rw-r--r--vespalib/src/vespa/vespalib/crypto/crypto_exception.h (renamed from vespalib/src/vespa/vespalib/net/tls/crypto_exception.h)2
-rw-r--r--vespalib/src/vespa/vespalib/crypto/openssl_crypto_impl.cpp (renamed from vespalib/src/tests/net/tls/openssl_impl/crypto_utils.cpp)212
-rw-r--r--vespalib/src/vespa/vespalib/crypto/openssl_crypto_impl.h44
-rw-r--r--vespalib/src/vespa/vespalib/crypto/openssl_typedefs.h (renamed from vespalib/src/vespa/vespalib/net/tls/impl/openssl_typedefs.h)2
-rw-r--r--vespalib/src/vespa/vespalib/crypto/private_key.cpp11
-rw-r--r--vespalib/src/vespa/vespalib/crypto/private_key.h34
-rw-r--r--vespalib/src/vespa/vespalib/crypto/x509_certificate.cpp62
-rw-r--r--vespalib/src/vespa/vespalib/crypto/x509_certificate.h (renamed from vespalib/src/tests/net/tls/openssl_impl/crypto_utils.h)77
-rw-r--r--vespalib/src/vespa/vespalib/net/crypto_engine.cpp4
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt1
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/impl/direct_buffer_bio.cpp7
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/impl/direct_buffer_bio.h6
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.cpp4
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.h14
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.cpp6
-rw-r--r--vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h4
-rw-r--r--vespalib/src/vespa/vespalib/test/make_tls_options_for_testing.cpp133
26 files changed, 429 insertions, 264 deletions
diff --git a/vespalib/CMakeLists.txt b/vespalib/CMakeLists.txt
index 9339cdacea0..979184acbae 100644
--- a/vespalib/CMakeLists.txt
+++ b/vespalib/CMakeLists.txt
@@ -30,6 +30,7 @@ vespa_define_module(
src/tests/component
src/tests/compress
src/tests/compression
+ src/tests/crypto
src/tests/data/databuffer
src/tests/data/input_reader
src/tests/data/lz4_encode_decode
@@ -139,6 +140,7 @@ vespa_define_module(
src/vespa/vespalib
src/vespa/vespalib/btree
src/vespa/vespalib/component
+ src/vespa/vespalib/crypto
src/vespa/vespalib/data
src/vespa/vespalib/data/slime
src/vespa/vespalib/datastore
diff --git a/vespalib/src/tests/crypto/CMakeLists.txt b/vespalib/src/tests/crypto/CMakeLists.txt
new file mode 100644
index 00000000000..b930b5715b5
--- /dev/null
+++ b/vespalib/src/tests/crypto/CMakeLists.txt
@@ -0,0 +1,10 @@
+# Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespalib_crypto_crypto_test_app TEST
+ SOURCES
+ crypto_test.cpp
+ DEPENDS
+ vespalib
+ gtest
+)
+vespa_add_test(NAME vespalib_crypto_crypto_test_app COMMAND vespalib_crypto_crypto_test_app)
+
diff --git a/vespalib/src/tests/crypto/crypto_test.cpp b/vespalib/src/tests/crypto/crypto_test.cpp
new file mode 100644
index 00000000000..8daba954793
--- /dev/null
+++ b/vespalib/src/tests/crypto/crypto_test.cpp
@@ -0,0 +1,37 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/vespalib/crypto/private_key.h>
+#include <vespa/vespalib/crypto/x509_certificate.h>
+#include <vespa/vespalib/gtest/gtest.h>
+#include <gmock/gmock.h>
+
+using namespace ::testing;
+
+namespace vespalib::crypto {
+
+// FIXME these tests are very high level and simple since the current crypto utility API we provide
+// is extremely simple and does not support loading PEMs, signing or verifying.
+
+TEST(CryptoTest, generated_p256_ec_private_key_can_be_exported_to_pem_format) {
+ auto key = PrivateKey::generate_p256_ec_key();
+ auto pem = key->private_to_pem();
+ EXPECT_THAT(pem, StartsWith("-----BEGIN PRIVATE KEY-----"));
+}
+
+TEST(CryptoTest, generated_x509_certificate_can_be_exported_to_pem_format) {
+ auto dn = X509Certificate::DistinguishedName()
+ .country("NO").locality("Trondheim")
+ .organization("Cool Unit Test Writers")
+ .organizational_unit("Only the finest tests, yes")
+ .add_common_name("cooltests.example.com");
+ auto subject = X509Certificate::SubjectInfo(std::move(dn));
+ auto key = PrivateKey::generate_p256_ec_key();
+ auto params = X509Certificate::Params::self_signed(std::move(subject), key);
+ auto cert = X509Certificate::generate_from(std::move(params));
+ auto pem = cert->to_pem();
+ EXPECT_THAT(pem, StartsWith("-----BEGIN CERTIFICATE-----"));
+}
+
+}
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/vespalib/src/tests/net/tls/direct_buffer_bio/direct_buffer_bio_test.cpp b/vespalib/src/tests/net/tls/direct_buffer_bio/direct_buffer_bio_test.cpp
index 134df9b17eb..a51cfb688c1 100644
--- a/vespalib/src/tests/net/tls/direct_buffer_bio/direct_buffer_bio_test.cpp
+++ b/vespalib/src/tests/net/tls/direct_buffer_bio/direct_buffer_bio_test.cpp
@@ -5,6 +5,7 @@
#include <cassert>
using namespace vespalib;
+using namespace vespalib::crypto;
using namespace vespalib::net::tls::impl;
struct Fixture {
diff --git a/vespalib/src/tests/net/tls/openssl_impl/CMakeLists.txt b/vespalib/src/tests/net/tls/openssl_impl/CMakeLists.txt
index e8f77d36e16..799e2291d7c 100644
--- a/vespalib/src/tests/net/tls/openssl_impl/CMakeLists.txt
+++ b/vespalib/src/tests/net/tls/openssl_impl/CMakeLists.txt
@@ -1,7 +1,6 @@
# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
vespa_add_executable(vespalib_net_tls_openssl_impl_test_app TEST
SOURCES
- crypto_utils.cpp
openssl_impl_test.cpp
DEPENDS
vespalib
diff --git a/vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp b/vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp
index 54c8c19fc64..4586beef910 100644
--- a/vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp
+++ b/vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp
@@ -1,5 +1,6 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include "crypto_utils.h"
+#include <vespa/vespalib/crypto/private_key.h>
+#include <vespa/vespalib/crypto/x509_certificate.h>
#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/vespalib/data/smart_buffer.h>
#include <vespa/vespalib/net/tls/authorization_mode.h>
@@ -11,11 +12,11 @@
#include <vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h>
#include <vespa/vespalib/test/make_tls_options_for_testing.h>
#include <vespa/vespalib/test/peer_policy_utils.h>
-#include <iostream>
#include <stdexcept>
#include <stdlib.h>
using namespace vespalib;
+using namespace vespalib::crypto;
using namespace vespalib::net::tls;
using namespace vespalib::net::tls::impl;
diff --git a/vespalib/src/vespa/vespalib/CMakeLists.txt b/vespalib/src/vespa/vespalib/CMakeLists.txt
index 4249f6333a4..95f6a407914 100644
--- a/vespalib/src/vespa/vespalib/CMakeLists.txt
+++ b/vespalib/src/vespa/vespalib/CMakeLists.txt
@@ -3,6 +3,7 @@ vespa_add_library(vespalib
SOURCES
$<TARGET_OBJECTS:vespalib_vespalib_btree>
$<TARGET_OBJECTS:vespalib_vespalib_component>
+ $<TARGET_OBJECTS:vespalib_vespalib_crypto>
$<TARGET_OBJECTS:vespalib_vespalib_data>
$<TARGET_OBJECTS:vespalib_vespalib_data_slime>
$<TARGET_OBJECTS:vespalib_vespalib_datastore>
diff --git a/vespalib/src/vespa/vespalib/crypto/CMakeLists.txt b/vespalib/src/vespa/vespalib/crypto/CMakeLists.txt
new file mode 100644
index 00000000000..6000156fcfa
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/crypto/CMakeLists.txt
@@ -0,0 +1,11 @@
+# Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_library(vespalib_vespalib_crypto OBJECT
+ SOURCES
+ crypto_exception.cpp
+ openssl_crypto_impl.cpp
+ private_key.cpp
+ x509_certificate.cpp
+ DEPENDS
+)
+find_package(OpenSSL)
+target_include_directories(vespalib_vespalib_crypto PUBLIC ${OPENSSL_INCLUDE_DIR})
diff --git a/vespalib/src/vespa/vespalib/net/tls/crypto_exception.cpp b/vespalib/src/vespa/vespalib/crypto/crypto_exception.cpp
index 41bb2060c04..226d8664de6 100644
--- a/vespalib/src/vespa/vespalib/net/tls/crypto_exception.cpp
+++ b/vespalib/src/vespa/vespalib/crypto/crypto_exception.cpp
@@ -2,7 +2,7 @@
#include "crypto_exception.h"
-namespace vespalib::net::tls {
+namespace vespalib::crypto {
VESPA_IMPLEMENT_EXCEPTION(CryptoException, Exception);
diff --git a/vespalib/src/vespa/vespalib/net/tls/crypto_exception.h b/vespalib/src/vespa/vespalib/crypto/crypto_exception.h
index 696a158e058..0d0dcc8ceec 100644
--- a/vespalib/src/vespa/vespalib/net/tls/crypto_exception.h
+++ b/vespalib/src/vespa/vespalib/crypto/crypto_exception.h
@@ -3,7 +3,7 @@
#include <vespa/vespalib/util/exception.h>
-namespace vespalib::net::tls {
+namespace vespalib::crypto {
VESPA_DEFINE_EXCEPTION(CryptoException, Exception);
diff --git a/vespalib/src/tests/net/tls/openssl_impl/crypto_utils.cpp b/vespalib/src/vespa/vespalib/crypto/openssl_crypto_impl.cpp
index 14755360b51..72efbb841c2 100644
--- a/vespalib/src/tests/net/tls/openssl_impl/crypto_utils.cpp
+++ b/vespalib/src/vespa/vespalib/crypto/openssl_crypto_impl.cpp
@@ -1,13 +1,12 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-#include "crypto_utils.h"
-#include <vespa/vespalib/net/tls/crypto_exception.h>
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include "openssl_crypto_impl.h"
+#include <vespa/vespalib/crypto/crypto_exception.h>
#include <cassert>
#include <openssl/bn.h>
#include <openssl/rand.h>
#include <openssl/x509v3.h>
-namespace vespalib::net::tls::impl {
+namespace vespalib::crypto::openssl_impl {
namespace {
@@ -61,6 +60,77 @@ BioPtr new_memory_bio() {
return bio;
}
+} // anonymous namespace
+
+vespalib::string PrivateKeyImpl::private_to_pem() const {
+ BioPtr bio = new_memory_bio();
+ // TODO this API is const-broken even on 1.1.1, revisit in the future...
+ auto* mutable_pkey = const_cast<::EVP_PKEY*>(_pkey.get());
+ if (::PEM_write_bio_PrivateKey(bio.get(), mutable_pkey, nullptr, nullptr,
+ 0, nullptr, nullptr) != 1) {
+ throw CryptoException("PEM_write_bio_PrivateKey");
+ }
+ return bio_to_string(*bio);
+}
+
+std::shared_ptr<PrivateKeyImpl> PrivateKeyImpl::generate_openssl_p256_ec_key() {
+ // We first have to generate an EVP context for the keygen _parameters_...
+ EvpPkeyCtxPtr params_ctx(::EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr));
+ if (!params_ctx) {
+ throw CryptoException("EVP_PKEY_CTX_new_id");
+ }
+ if (::EVP_PKEY_paramgen_init(params_ctx.get()) != 1) {
+ throw CryptoException("EVP_PKEY_paramgen_init");
+ }
+ // Set EC keygen parameters to use prime256v1, which is the same as P-256
+ if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(params_ctx.get(), NID_X9_62_prime256v1) <= 0) {
+ throw CryptoException("EVP_PKEY_CTX_set_ec_paramgen_curve_nid");
+ }
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+ // Must tag _explicitly_ as a named curve or many won't accept our pretty keys.
+ // If we don't do this, explicit curve parameters are included with the key,
+ // and this is not widely supported nor needed since we're generating a key on
+ // a standardized curve.
+ if (EVP_PKEY_CTX_set_ec_param_enc(params_ctx.get(), OPENSSL_EC_NAMED_CURVE) <= 0) {
+ throw CryptoException("EVP_PKEY_CTX_set_ec_param_enc");
+ }
+#endif
+ // Note: despite being an EVP_PKEY this is not an actual key, just key parameters!
+ ::EVP_PKEY* params_raw = nullptr;
+ if (::EVP_PKEY_paramgen(params_ctx.get(), &params_raw) != 1) {
+ throw CryptoException("EVP_PKEY_paramgen");
+ }
+ EvpPkeyPtr params(params_raw);
+ // Now we can create a context for the proper key generation
+ EvpPkeyCtxPtr key_ctx(::EVP_PKEY_CTX_new(params.get(), nullptr));
+ if (!params_ctx) {
+ throw CryptoException("EVP_PKEY_CTX_new");
+ }
+ if (::EVP_PKEY_keygen_init(key_ctx.get()) != 1) {
+ throw CryptoException("EVP_PKEY_keygen_init");
+ }
+ // Finally, it's time to generate the key pair itself.
+ ::EVP_PKEY* pkey_raw = nullptr;
+ if (::EVP_PKEY_keygen(key_ctx.get(), &pkey_raw) != 1) {
+ throw CryptoException("EVP_PKEY_keygen");
+ }
+ EvpPkeyPtr generated_key(pkey_raw);
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
+ // On OpenSSL versions prior to 1.1.0, we must set the named curve ASN1 flag
+ // directly on the EC_KEY, as the EVP_PKEY wrapper doesn't exist (this is a
+ // half truth, as it exists on 1.0.2 stable, but not necessarily on all 1.0.2
+ // versions, and certainly not on 1.0.1).
+ EcKeyPtr ec_key(::EVP_PKEY_get1_EC_KEY(generated_key.get())); // Bumps ref count, needs free
+ if (!ec_key) {
+ throw CryptoException("EVP_PKEY_get1_EC_KEY");
+ }
+ ::EC_KEY_set_asn1_flag(ec_key.get(), OPENSSL_EC_NAMED_CURVE);
+#endif
+ return std::make_shared<PrivateKeyImpl>(std::move(generated_key), Type::EC);
+}
+
+namespace {
+
void assign_random_positive_serial_number(::X509& cert) {
/*
* From RFC3280, section 4.1.2.2:
@@ -165,88 +235,23 @@ void add_any_subject_alternate_names(::X509& subject, ::X509& issuer,
}
}
-} // anon ns
-
-vespalib::string PrivateKey::private_to_pem() const {
- BioPtr bio = new_memory_bio();
- // TODO this API is const-broken even on 1.1.1, revisit in the future...
- auto* mutable_pkey = const_cast<::EVP_PKEY*>(_pkey.get());
- if (::PEM_write_bio_PrivateKey(bio.get(), mutable_pkey, nullptr, nullptr,
- 0, nullptr, nullptr) != 1) {
- throw CryptoException("PEM_write_bio_PrivateKey");
- }
- return bio_to_string(*bio);
-}
-
-std::shared_ptr<PrivateKey> PrivateKey::generate_p256_ec_key() {
- // We first have to generate an EVP context for the keygen _parameters_...
- EvpPkeyCtxPtr params_ctx(::EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr));
- if (!params_ctx) {
- throw CryptoException("EVP_PKEY_CTX_new_id");
- }
- if (::EVP_PKEY_paramgen_init(params_ctx.get()) != 1) {
- throw CryptoException("EVP_PKEY_paramgen_init");
- }
- // Set EC keygen parameters to use prime256v1, which is the same as P-256
- if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(params_ctx.get(), NID_X9_62_prime256v1) <= 0) {
- throw CryptoException("EVP_PKEY_CTX_set_ec_paramgen_curve_nid");
- }
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
- // Must tag _explicitly_ as a named curve or many won't accept our pretty keys.
- // If we don't do this, explicit curve parameters are included with the key,
- // and this is not widely supported nor needed since we're generating a key on
- // a standardized curve.
- if (EVP_PKEY_CTX_set_ec_param_enc(params_ctx.get(), OPENSSL_EC_NAMED_CURVE) <= 0) {
- throw CryptoException("EVP_PKEY_CTX_set_ec_param_enc");
- }
-#endif
- // Note: despite being an EVP_PKEY this is not an actual key, just key parameters!
- ::EVP_PKEY* params_raw = nullptr;
- if (::EVP_PKEY_paramgen(params_ctx.get(), &params_raw) != 1) {
- throw CryptoException("EVP_PKEY_paramgen");
- }
- EvpPkeyPtr params(params_raw);
- // Now we can create a context for the proper key generation
- EvpPkeyCtxPtr key_ctx(::EVP_PKEY_CTX_new(params.get(), nullptr));
- if (!params_ctx) {
- throw CryptoException("EVP_PKEY_CTX_new");
- }
- if (::EVP_PKEY_keygen_init(key_ctx.get()) != 1) {
- throw CryptoException("EVP_PKEY_keygen_init");
- }
- // Finally, it's time to generate the key pair itself.
- ::EVP_PKEY* pkey_raw = nullptr;
- if (::EVP_PKEY_keygen(key_ctx.get(), &pkey_raw) != 1) {
- throw CryptoException("EVP_PKEY_keygen");
- }
- EvpPkeyPtr generated_key(pkey_raw);
-#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
- // On OpenSSL versions prior to 1.1.0, we must set the named curve ASN1 flag
- // directly on the EC_KEY, as the EVP_PKEY wrapper doesn't exist (this is a
- // half truth, as it exists on 1.0.2 stable, but not necessarily on all 1.0.2
- // versions, and certainly not on 1.0.1).
- EcKeyPtr ec_key(::EVP_PKEY_get1_EC_KEY(generated_key.get())); // Bumps ref count, needs free
- if (!ec_key) {
- throw CryptoException("EVP_PKEY_get1_EC_KEY");
- }
- ::EC_KEY_set_asn1_flag(ec_key.get(), OPENSSL_EC_NAMED_CURVE);
-#endif
- return std::make_shared<PrivateKey>(std::move(generated_key), Type::EC);
-}
+} // anonymous namespace
// Some references:
// https://stackoverflow.com/questions/256405/programmatically-create-x509-certificate-using-openssl
// https://opensource.apple.com/source/OpenSSL/OpenSSL-22/openssl/demos/x509/mkcert.c
-std::shared_ptr<X509Certificate> X509Certificate::generate_from(Params params) {
+std::shared_ptr<X509CertificateImpl> X509CertificateImpl::generate_openssl_x509_from(Params params) {
X509Ptr cert(::X509_new());
if (!cert) {
throw CryptoException("X509_new");
}
+ // FIXME make this const, currently is not due to OpenSSL API const issues (ugh).
+ auto& subject_key_impl = dynamic_cast<PrivateKeyImpl&>(*params.subject_key);
::X509_set_version(cert.get(), 2); // 2 actually means v3 :)
assign_random_positive_serial_number(*cert);
set_certificate_expires_from_now(*cert, params.valid_for);
// Internal key copy; does not take ownership
- if (::X509_set_pubkey(cert.get(), params.subject_key->native_key()) != 1) {
+ if (::X509_set_pubkey(cert.get(), subject_key_impl.native_key()) != 1) {
throw CryptoException("X509_set_pubkey");
}
// The "subject" is the target entity the certificate is intended to, well, certify.
@@ -259,13 +264,14 @@ std::shared_ptr<X509Certificate> X509Certificate::generate_from(Params params) {
// If we _do_ have an issuer, we'll record its Subject name as our Issuer name.
// Note that it's legal to have a self-signed non-CA certificate, though it obviously
// cannot be used to sign any subordinate certificates.
- ::X509_NAME* issuer_name = (params.issuer
- ? ::X509_get_subject_name(params.issuer->native_cert())
+ auto* issuer_cert_impl = dynamic_cast<X509CertificateImpl*>(params.issuer.get()); // May be nullptr.
+ ::X509_NAME* issuer_name = (issuer_cert_impl
+ ? ::X509_get_subject_name(issuer_cert_impl->native_cert())
: subj_name);
if (::X509_set_issuer_name(cert.get(), issuer_name) != 1) { // Makes internal copy
throw CryptoException("X509_set_issuer_name");
}
- ::X509& issuer_cert = params.issuer ? *params.issuer->native_cert() : *cert;
+ ::X509& issuer_cert = issuer_cert_impl ? *issuer_cert_impl->native_cert() : *cert;
const char* basic_constraints = params.is_ca ? "critical,CA:TRUE" : "critical,CA:FALSE";
const char* key_usage = params.is_ca ? "critical,keyCertSign,digitalSignature"
@@ -278,13 +284,14 @@ std::shared_ptr<X509Certificate> X509Certificate::generate_from(Params params) {
add_v3_ext(*cert, issuer_cert, NID_authority_key_identifier, "keyid:always");
add_any_subject_alternate_names(*cert, issuer_cert, params.subject_info.subject_alt_names);
- if (::X509_sign(cert.get(), params.issuer_key->native_key(), ::EVP_sha256()) == 0) {
+ auto& issuer_key_impl = dynamic_cast<PrivateKeyImpl&>(*params.issuer_key);
+ if (::X509_sign(cert.get(), issuer_key_impl.native_key(), ::EVP_sha256()) == 0) {
throw CryptoException("X509_sign");
}
- return std::make_shared<X509Certificate>(std::move(cert));
+ return std::make_shared<X509CertificateImpl>(std::move(cert));
}
-vespalib::string X509Certificate::to_pem() const {
+vespalib::string X509CertificateImpl::to_pem() const {
BioPtr bio = new_memory_bio();
// TODO this API is const-broken, revisit in the future...
auto* mutable_cert = const_cast<::X509*>(_cert.get());
@@ -295,47 +302,4 @@ vespalib::string X509Certificate::to_pem() const {
}
-X509Certificate::DistinguishedName::DistinguishedName() = default;
-X509Certificate::DistinguishedName::DistinguishedName(const DistinguishedName&) = default;
-X509Certificate::DistinguishedName& X509Certificate::DistinguishedName::operator=(const DistinguishedName&) = default;
-X509Certificate::DistinguishedName::DistinguishedName(DistinguishedName&&) = default;
-X509Certificate::DistinguishedName& X509Certificate::DistinguishedName::operator=(DistinguishedName&&) = default;
-X509Certificate::DistinguishedName::~DistinguishedName() = default;
-
-X509Certificate::Params::Params() = default;
-X509Certificate::Params::~Params() = default;
-
-X509Certificate::Params
-X509Certificate::Params::self_signed(SubjectInfo subject,
- std::shared_ptr<PrivateKey> key) {
- Params params;
- params.subject_info = std::move(subject);
- params.subject_key = key;
- params.issuer_key = std::move(key); // self-signed, subject == issuer
- params.is_ca = true;
- return params;
-}
-
-X509Certificate::Params
-X509Certificate::Params::issued_by(SubjectInfo subject,
- std::shared_ptr<PrivateKey> subject_key,
- std::shared_ptr<X509Certificate> issuer,
- std::shared_ptr<PrivateKey> issuer_key) {
- Params params;
- params.subject_info = std::move(subject);
- params.issuer = std::move(issuer);
- params.subject_key = std::move(subject_key);
- params.issuer_key = std::move(issuer_key);
- params.is_ca = false; // By default, caller can change for intermediate CAs
- return params;
-}
-
-CertKeyWrapper::CertKeyWrapper(std::shared_ptr<X509Certificate> cert_,
- std::shared_ptr<PrivateKey> key_)
- : cert(std::move(cert_)),
- key(std::move(key_))
-{}
-
-CertKeyWrapper::~CertKeyWrapper() = default;
-
}
diff --git a/vespalib/src/vespa/vespalib/crypto/openssl_crypto_impl.h b/vespalib/src/vespa/vespalib/crypto/openssl_crypto_impl.h
new file mode 100644
index 00000000000..87e4ee14e65
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/crypto/openssl_crypto_impl.h
@@ -0,0 +1,44 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespa/vespalib/crypto/openssl_typedefs.h>
+#include "private_key.h"
+#include "x509_certificate.h"
+
+namespace vespalib::crypto::openssl_impl {
+
+class PrivateKeyImpl : public PrivateKey {
+ EvpPkeyPtr _pkey;
+ Type _type;
+public:
+ PrivateKeyImpl(EvpPkeyPtr pkey, Type type)
+ : _pkey(std::move(pkey)),
+ _type(type)
+ {}
+ ~PrivateKeyImpl() override = default;
+
+ ::EVP_PKEY* native_key() noexcept { return _pkey.get(); }
+ const ::EVP_PKEY* native_key() const noexcept { return _pkey.get(); }
+
+ Type type() const noexcept override { return _type; }
+ vespalib::string private_to_pem() const override;
+
+ static std::shared_ptr<PrivateKeyImpl> generate_openssl_p256_ec_key();
+};
+
+class X509CertificateImpl : public X509Certificate {
+ X509Ptr _cert;
+public:
+ explicit X509CertificateImpl(X509Ptr cert) : _cert(std::move(cert)) {}
+ ~X509CertificateImpl() = default;
+
+ ::X509* native_cert() noexcept { return _cert.get(); }
+ const ::X509* native_cert() const noexcept { return _cert.get(); }
+
+ vespalib::string to_pem() const override;
+
+ // Generates an X509 certificate using a SHA-256 digest
+ static std::shared_ptr<X509CertificateImpl> generate_openssl_x509_from(Params params);
+};
+
+}
diff --git a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_typedefs.h b/vespalib/src/vespa/vespalib/crypto/openssl_typedefs.h
index afafe556338..2986a4515f7 100644
--- a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_typedefs.h
+++ b/vespalib/src/vespa/vespalib/crypto/openssl_typedefs.h
@@ -6,7 +6,7 @@
#include <openssl/crypto.h>
#include <openssl/x509.h>
-namespace vespalib::net::tls::impl {
+namespace vespalib::crypto {
struct BioDeleter {
void operator()(::BIO* bio) const noexcept {
diff --git a/vespalib/src/vespa/vespalib/crypto/private_key.cpp b/vespalib/src/vespa/vespalib/crypto/private_key.cpp
new file mode 100644
index 00000000000..7ece9418bef
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/crypto/private_key.cpp
@@ -0,0 +1,11 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include "private_key.h"
+#include "openssl_crypto_impl.h"
+
+namespace vespalib::crypto {
+
+std::shared_ptr<PrivateKey> PrivateKey::generate_p256_ec_key() {
+ return openssl_impl::PrivateKeyImpl::generate_openssl_p256_ec_key();
+}
+
+}
diff --git a/vespalib/src/vespa/vespalib/crypto/private_key.h b/vespalib/src/vespa/vespalib/crypto/private_key.h
new file mode 100644
index 00000000000..7ac5c31502c
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/crypto/private_key.h
@@ -0,0 +1,34 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#pragma once
+
+#include <vespa/vespalib/stllike/string.h>
+#include <memory>
+
+namespace vespalib::crypto {
+
+/*
+ * Represents an asymmetric cryptographic private key.
+ *
+ * Can only be used for private/public key crypto, not for secret key (e.g. AES) crypto.
+ * Currently only supports generating EC keys on the standard P-256 curve.
+ */
+class PrivateKey {
+public:
+ enum class Type {
+ EC,
+ RSA // TODO implement support..!
+ };
+
+ virtual ~PrivateKey() = default;
+
+ virtual Type type() const noexcept = 0;
+ // TODO should have a wrapper for this that takes care to securely erase
+ // string memory on destruction.
+ virtual vespalib::string private_to_pem() const = 0;
+
+ static std::shared_ptr<PrivateKey> generate_p256_ec_key();
+protected:
+ PrivateKey() = default;
+};
+
+}
diff --git a/vespalib/src/vespa/vespalib/crypto/x509_certificate.cpp b/vespalib/src/vespa/vespalib/crypto/x509_certificate.cpp
new file mode 100644
index 00000000000..ecd061e573a
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/crypto/x509_certificate.cpp
@@ -0,0 +1,62 @@
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "x509_certificate.h"
+#include "openssl_crypto_impl.h"
+
+namespace vespalib::crypto {
+
+X509Certificate::DistinguishedName::DistinguishedName() = default;
+X509Certificate::DistinguishedName::DistinguishedName(const DistinguishedName&) = default;
+X509Certificate::DistinguishedName& X509Certificate::DistinguishedName::operator=(const DistinguishedName&) = default;
+X509Certificate::DistinguishedName::DistinguishedName(DistinguishedName&&) noexcept = default;
+X509Certificate::DistinguishedName& X509Certificate::DistinguishedName::operator=(DistinguishedName&&) noexcept = default;
+X509Certificate::DistinguishedName::~DistinguishedName() = default;
+
+X509Certificate::Params::Params() = default;
+X509Certificate::Params::~Params() = default;
+
+X509Certificate::Params::Params(const Params&) = default;
+X509Certificate::Params& X509Certificate::Params::operator=(const Params&) = default;
+X509Certificate::Params::Params(Params&&) noexcept = default;
+X509Certificate::Params& X509Certificate::Params::operator=(Params&&) noexcept = default;
+
+X509Certificate::Params
+X509Certificate::Params::self_signed(SubjectInfo subject,
+ std::shared_ptr<PrivateKey> key)
+{
+ Params params;
+ params.subject_info = std::move(subject);
+ params.subject_key = key;
+ params.issuer_key = std::move(key); // self-signed, subject == issuer
+ params.is_ca = true;
+ return params;
+}
+
+X509Certificate::Params
+X509Certificate::Params::issued_by(SubjectInfo subject,
+ std::shared_ptr<PrivateKey> subject_key,
+ std::shared_ptr<X509Certificate> issuer,
+ std::shared_ptr<PrivateKey> issuer_key)
+{
+ Params params;
+ params.subject_info = std::move(subject);
+ params.issuer = std::move(issuer);
+ params.subject_key = std::move(subject_key);
+ params.issuer_key = std::move(issuer_key);
+ params.is_ca = false; // By default, caller can change for intermediate CAs
+ return params;
+}
+
+std::shared_ptr<X509Certificate> X509Certificate::generate_from(Params params) {
+ return openssl_impl::X509CertificateImpl::generate_openssl_x509_from(std::move(params));
+}
+
+CertKeyWrapper::CertKeyWrapper(std::shared_ptr<X509Certificate> cert_,
+ std::shared_ptr<PrivateKey> key_)
+ : cert(std::move(cert_)),
+ key(std::move(key_))
+{}
+
+CertKeyWrapper::~CertKeyWrapper() = default;
+
+}
diff --git a/vespalib/src/tests/net/tls/openssl_impl/crypto_utils.h b/vespalib/src/vespa/vespalib/crypto/x509_certificate.h
index 017dfbdbc12..9eea423c5a0 100644
--- a/vespalib/src/tests/net/tls/openssl_impl/crypto_utils.h
+++ b/vespalib/src/vespa/vespalib/crypto/x509_certificate.h
@@ -1,52 +1,32 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once
+#include "private_key.h"
#include <vespa/vespalib/stllike/string.h>
-#include <vespa/vespalib/net/tls/impl/openssl_typedefs.h>
#include <chrono>
#include <memory>
#include <vector>
-// TODOs
-// - add unit testing
-// - extend interfaces (separate PublicKey etc)
-// - hide all OpenSSL details from header
-// - move to appropriate new namespace/directory somewhere under vespalib
-
-namespace vespalib::net::tls::impl {
-
-class PrivateKey {
-public:
- enum class Type {
- EC,
- RSA // TODO implement support..!
- };
-private:
- EvpPkeyPtr _pkey;
- Type _type;
-public:
- PrivateKey(EvpPkeyPtr pkey, Type type)
- : _pkey(std::move(pkey)),
- _type(type)
- {}
-
- ::EVP_PKEY* native_key() noexcept { return _pkey.get(); }
- const ::EVP_PKEY* native_key() const noexcept { return _pkey.get(); }
-
- Type type() const noexcept { return _type; }
- vespalib::string private_to_pem() const;
-
- static std::shared_ptr<PrivateKey> generate_p256_ec_key();
-};
-
-
+namespace vespalib::crypto {
+
+/**
+ * Represents an X509 certificate instance and provides utility methods
+ * for generating new certificates on the fly. Certificates can be created
+ * for both Certificate Authorities and regular hosts (leaves).
+ *
+ * This implementation aims to ensure that best cryptographic practices are
+ * followed automatically. In particular:
+ * - The certificate digest is always SHA-256, never SHA-1 or MD5
+ * - The certificate serial number is a 160-bit secure random sequence
+ * (technically 159 bits since the MSB is always zero) rather than a
+ * collision-prone or predictable sequence number.
+ *
+ */
class X509Certificate {
- X509Ptr _cert;
public:
- explicit X509Certificate(X509Ptr cert) : _cert(std::move(cert)) {}
+ virtual ~X509Certificate() = default;
- ::X509* native_cert() noexcept { return _cert.get(); }
- const ::X509* native_cert() const noexcept { return _cert.get(); }
+ virtual vespalib::string to_pem() const = 0;
struct DistinguishedName {
vespalib::string _country; // "C"
@@ -61,9 +41,8 @@ public:
DistinguishedName();
DistinguishedName(const DistinguishedName&);
DistinguishedName& operator=(const DistinguishedName&);
- // TODO make these noexcept once vespalib::string has noexcept move.. or move at all!
- DistinguishedName(DistinguishedName&&);
- DistinguishedName& operator=(DistinguishedName&&);
+ DistinguishedName(DistinguishedName&&) noexcept;
+ DistinguishedName& operator=(DistinguishedName&&) noexcept;
~DistinguishedName();
// TODO could add rvalue overloads as well...
@@ -101,8 +80,13 @@ public:
Params();
~Params();
+ Params(const Params&);
+ Params& operator=(const Params&);
+ Params(Params&&) noexcept;
+ Params& operator=(Params&&) noexcept;
+
SubjectInfo subject_info;
- // TODO make public key, but private key has both and this is currently just for testing.
+ // TODO make public key, but private key has both.
std::shared_ptr<PrivateKey> subject_key;
std::shared_ptr<X509Certificate> issuer; // May be nullptr for self-signed certs
std::shared_ptr<PrivateKey> issuer_key;
@@ -119,9 +103,14 @@ public:
// Generates an X509 certificate using a SHA-256 digest
static std::shared_ptr<X509Certificate> generate_from(Params params);
- vespalib::string to_pem() const;
+protected:
+ X509Certificate() = default;
};
+/*
+ * Simple wrapper for storing both a X509 certificate and the private key
+ * that signed it. Useful for testing.
+ */
struct CertKeyWrapper {
std::shared_ptr<X509Certificate> cert;
std::shared_ptr<PrivateKey> key;
diff --git a/vespalib/src/vespa/vespalib/net/crypto_engine.cpp b/vespalib/src/vespa/vespalib/net/crypto_engine.cpp
index a92b0e06bbe..7bbc4b7523c 100644
--- a/vespalib/src/vespa/vespalib/net/crypto_engine.cpp
+++ b/vespalib/src/vespa/vespalib/net/crypto_engine.cpp
@@ -2,9 +2,9 @@
#include "crypto_engine.h"
#include <vespa/vespalib/data/smart_buffer.h>
+#include <vespa/vespalib/crypto/crypto_exception.h>
#include <vespa/vespalib/net/tls/authorization_mode.h>
#include <vespa/vespalib/net/tls/auto_reloading_tls_crypto_engine.h>
-#include <vespa/vespalib/net/tls/crypto_exception.h>
#include <vespa/vespalib/net/tls/maybe_tls_crypto_engine.h>
#include <vespa/vespalib/net/tls/statistics.h>
#include <vespa/vespalib/net/tls/tls_crypto_engine.h>
@@ -232,7 +232,7 @@ CryptoEngine::SP create_default_crypto_engine() {
CryptoEngine::SP try_create_default_crypto_engine() {
try {
return create_default_crypto_engine();
- } catch (net::tls::CryptoException &e) {
+ } catch (crypto::CryptoException &e) {
LOG(error, "failed to create default crypto engine: %s", e.what());
std::_Exit(78);
}
diff --git a/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt b/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt
index 6dc48db68e4..b7801f40959 100644
--- a/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt
+++ b/vespalib/src/vespa/vespalib/net/tls/CMakeLists.txt
@@ -5,7 +5,6 @@ vespa_add_library(vespalib_vespalib_net_tls OBJECT
auto_reloading_tls_crypto_engine.cpp
crypto_codec.cpp
crypto_codec_adapter.cpp
- crypto_exception.cpp
maybe_tls_crypto_engine.cpp
maybe_tls_crypto_socket.cpp
peer_credentials.cpp
diff --git a/vespalib/src/vespa/vespalib/net/tls/impl/direct_buffer_bio.cpp b/vespalib/src/vespa/vespalib/net/tls/impl/direct_buffer_bio.cpp
index 614722a9769..d7d02534242 100644
--- a/vespalib/src/vespa/vespalib/net/tls/impl/direct_buffer_bio.cpp
+++ b/vespalib/src/vespa/vespalib/net/tls/impl/direct_buffer_bio.cpp
@@ -1,11 +1,10 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "direct_buffer_bio.h"
-#include <vespa/vespalib/net/tls/crypto_exception.h>
+#include <vespa/vespalib/crypto/crypto_exception.h>
+#include <vespa/vespalib/util/backtrace.h>
#include <utility>
#include <cassert>
-#include <vespa/vespalib/util/backtrace.h>
-
#include <vespa/log/log.h>
LOG_SETUP(".vespalib.net.tls.impl.direct_buffer_bio");
@@ -19,6 +18,8 @@ LOG_SETUP(".vespalib.net.tls.impl.direct_buffer_bio");
* - https://github.com/indutny/uv_ssl_t/blob/master/src/bio.c
*/
+using namespace vespalib::crypto;
+
namespace vespalib::net::tls::impl {
namespace {
diff --git a/vespalib/src/vespa/vespalib/net/tls/impl/direct_buffer_bio.h b/vespalib/src/vespa/vespalib/net/tls/impl/direct_buffer_bio.h
index 581d43d6f29..8492bf2c436 100644
--- a/vespalib/src/vespa/vespalib/net/tls/impl/direct_buffer_bio.h
+++ b/vespalib/src/vespa/vespalib/net/tls/impl/direct_buffer_bio.h
@@ -1,7 +1,7 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once
-#include "openssl_typedefs.h"
+#include <vespa/vespalib/crypto/openssl_typedefs.h>
#include <openssl/bio.h>
/*
@@ -22,8 +22,8 @@
namespace vespalib::net::tls::impl {
-BioPtr new_mutable_direct_buffer_bio();
-BioPtr new_const_direct_buffer_bio();
+crypto::BioPtr new_mutable_direct_buffer_bio();
+crypto::BioPtr new_const_direct_buffer_bio();
struct MutableBufferView {
// Could use a pointer pair instead (or just modify the ptr), but being explicit is good for readability.
diff --git a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.cpp b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.cpp
index 6a79caa8264..1d87a50190e 100644
--- a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.cpp
+++ b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.cpp
@@ -3,8 +3,8 @@
#include "openssl_tls_context_impl.h"
#include "direct_buffer_bio.h"
+#include <vespa/vespalib/crypto/crypto_exception.h>
#include <vespa/vespalib/net/tls/crypto_codec.h>
-#include <vespa/vespalib/net/tls/crypto_exception.h>
#include <vespa/vespalib/net/tls/statistics.h>
#include <mutex>
@@ -36,6 +36,8 @@ LOG_SETUP(".vespalib.net.tls.openssl_crypto_codec_impl");
* light fades and turns to all-enveloping darkness.
*/
+using namespace vespalib::crypto;
+
namespace vespalib::net::tls::impl {
namespace {
diff --git a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.h b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.h
index ec8df853c16..80f3e12786a 100644
--- a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.h
+++ b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_crypto_codec_impl.h
@@ -1,7 +1,7 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once
-#include "openssl_typedefs.h"
+#include <vespa/vespalib/crypto/openssl_typedefs.h>
#include <vespa/vespalib/net/socket_address.h>
#include <vespa/vespalib/net/socket_spec.h>
#include <vespa/vespalib/net/tls/transport_security_options.h>
@@ -47,12 +47,12 @@ class OpenSslCryptoCodecImpl : public CryptoCodec {
// The context maintains shared verification callback state, so it must be
// kept alive explictly for at least as long as any codecs.
std::shared_ptr<OpenSslTlsContextImpl> _ctx;
- SocketSpec _peer_spec;
- SocketAddress _peer_address;
- SslPtr _ssl;
- ::BIO* _input_bio; // Owned by _ssl
- ::BIO* _output_bio; // Owned by _ssl
- Mode _mode;
+ SocketSpec _peer_spec;
+ SocketAddress _peer_address;
+ crypto::SslPtr _ssl;
+ ::BIO* _input_bio; // Owned by _ssl
+ ::BIO* _output_bio; // Owned by _ssl
+ Mode _mode;
std::optional<DeferredHandshakeParams> _deferred_handshake_params;
std::optional<HandshakeResult> _deferred_handshake_result;
public:
diff --git a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.cpp b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.cpp
index 98675ec6b0b..e66baf87999 100644
--- a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.cpp
+++ b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.cpp
@@ -1,9 +1,9 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "iana_cipher_map.h"
-#include "openssl_typedefs.h"
#include "openssl_tls_context_impl.h"
#include "openssl_crypto_codec_impl.h"
-#include <vespa/vespalib/net/tls/crypto_exception.h>
+#include <vespa/vespalib/crypto/crypto_exception.h>
+#include <vespa/vespalib/crypto/openssl_typedefs.h>
#include <vespa/vespalib/net/tls/statistics.h>
#include <vespa/vespalib/net/tls/transport_security_options.h>
#include <vespa/vespalib/util/stringfmt.h>
@@ -26,6 +26,8 @@ LOG_SETUP(".vespalib.net.tls.openssl_tls_context_impl");
# error "Provided OpenSSL version is too darn old, need at least 1.0.0"
#endif
+using namespace vespalib::crypto;
+
namespace vespalib::net::tls::impl {
namespace {
diff --git a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h
index c519b1ae874..badfe8306d1 100644
--- a/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h
+++ b/vespalib/src/vespa/vespalib/net/tls/impl/openssl_tls_context_impl.h
@@ -1,7 +1,7 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#pragma once
-#include "openssl_typedefs.h"
+#include <vespa/vespalib/crypto/openssl_typedefs.h>
#include <vespa/vespalib/net/socket_address.h>
#include <vespa/vespalib/net/tls/tls_context.h>
#include <vespa/vespalib/net/tls/transport_security_options.h>
@@ -13,7 +13,7 @@
namespace vespalib::net::tls::impl {
class OpenSslTlsContextImpl : public TlsContext {
- SslCtxPtr _ctx;
+ crypto::SslCtxPtr _ctx;
AuthorizationMode _authorization_mode;
std::shared_ptr<CertificateVerificationCallback> _cert_verify_callback;
TransportSecurityOptions _redacted_transport_options;
diff --git a/vespalib/src/vespa/vespalib/test/make_tls_options_for_testing.cpp b/vespalib/src/vespa/vespalib/test/make_tls_options_for_testing.cpp
index b31f2830976..ad46e7272cb 100644
--- a/vespalib/src/vespa/vespalib/test/make_tls_options_for_testing.cpp
+++ b/vespalib/src/vespa/vespalib/test/make_tls_options_for_testing.cpp
@@ -1,82 +1,77 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "make_tls_options_for_testing.h"
+#include <vespa/vespalib/crypto/private_key.h>
+#include <vespa/vespalib/crypto/x509_certificate.h>
-/*
- * Generated with the following commands:
- *
- * openssl ecparam -name prime256v1 -genkey -noout -out ca.key
- *
- * openssl req -new -x509 -nodes -key ca.key \
- * -sha256 -out ca.pem \
- * -subj '/C=US/L=LooneyVille/O=ACME/OU=ACME test CA/CN=acme.example.com' \
- * -days 10000
- *
- * openssl ecparam -name prime256v1 -genkey -noout -out host.key
- *
- * openssl req -new -key host.key -out host.csr \
- * -subj '/C=US/L=LooneyVille/O=Wile. E. Coyote, Ltd./CN=wile.example.com' \
- * -sha256
- *
- * openssl x509 -req -in host.csr \
- * -CA ca.pem \
- * -CAkey ca.key \
- * -CAcreateserial \
- * -out host.pem \
- * -days 10000 \
- * -sha256
- *
- * TODO generate keypairs and certs at test-time to avoid any hard-coding
- * There certs are valid until 2046, so that buys us some time..!
- */
-
-// ca.pem
-constexpr const char* ca_pem = R"(-----BEGIN CERTIFICATE-----
-MIIBuDCCAV4CCQDpVjQIixTxvDAKBggqhkjOPQQDAjBkMQswCQYDVQQGEwJVUzEU
-MBIGA1UEBwwLTG9vbmV5VmlsbGUxDTALBgNVBAoMBEFDTUUxFTATBgNVBAsMDEFD
-TUUgdGVzdCBDQTEZMBcGA1UEAwwQYWNtZS5leGFtcGxlLmNvbTAeFw0xODA4MzEx
-MDU3NDVaFw00NjAxMTYxMDU3NDVaMGQxCzAJBgNVBAYTAlVTMRQwEgYDVQQHDAtM
-b29uZXlWaWxsZTENMAsGA1UECgwEQUNNRTEVMBMGA1UECwwMQUNNRSB0ZXN0IENB
-MRkwFwYDVQQDDBBhY21lLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D
-AQcDQgAE1L7IzCN5pbyVnBATIHieuxq+hf9kWyn5yfjkXMhD52T5ITz1huq4nbiN
-YtRoRP7XmipI60R/uiCHzERcsVz4rDAKBggqhkjOPQQDAgNIADBFAiEA6wmZDBca
-y0aJ6ABtjbjx/vlmVDxdkaSZSgO8h2CkvIECIFktCkbZhDFfSvbqUScPOGuwkdGQ
-L/EW2Bxp+1BPcYoZ
------END CERTIFICATE-----)";
-
-// host.pem
-constexpr const char* cert_pem = R"(-----BEGIN CERTIFICATE-----
-MIIBsTCCAVgCCQD6GfDh0ltpsjAKBggqhkjOPQQDAjBkMQswCQYDVQQGEwJVUzEU
-MBIGA1UEBwwLTG9vbmV5VmlsbGUxDTALBgNVBAoMBEFDTUUxFTATBgNVBAsMDEFD
-TUUgdGVzdCBDQTEZMBcGA1UEAwwQYWNtZS5leGFtcGxlLmNvbTAeFw0xODA4MzEx
-MDU3NDVaFw00NjAxMTYxMDU3NDVaMF4xCzAJBgNVBAYTAlVTMRQwEgYDVQQHDAtM
-b29uZXlWaWxsZTEeMBwGA1UECgwVV2lsZS4gRS4gQ295b3RlLCBMdGQuMRkwFwYD
-VQQDDBB3aWxlLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
-e+Y4hxt66em0STviGUj6ZDbxzoLoubXWRml8JDFrEc2S2433KWw2npxYSKVCyo3a
-/Vo33V8/H0WgOXioKEZJxDAKBggqhkjOPQQDAgNHADBEAiAN+87hQuGv3z0Ja2BV
-b8PHq2vp3BJHjeMuxWu4BFPn0QIgYlvIHikspgGatXRNMZ1gPC0oCccsJFcie+Cw
-zL06UPI=
------END CERTIFICATE-----)";
-
-// host.key
-constexpr const char* key_pem = R"(-----BEGIN EC PRIVATE KEY-----
-MHcCAQEEID6di2PFYn8hPrxPbkFDGkSqF+K8L520In7nx3g0jwzOoAoGCCqGSM49
-AwEHoUQDQgAEe+Y4hxt66em0STviGUj6ZDbxzoLoubXWRml8JDFrEc2S2433KWw2
-npxYSKVCyo3a/Vo33V8/H0WgOXioKEZJxA==
------END EC PRIVATE KEY-----)";
+namespace {
+
+using namespace vespalib::crypto;
+
+struct TransientCryptoCredentials {
+ CertKeyWrapper root_ca;
+ CertKeyWrapper host_creds;
+ vespalib::net::tls::TransportSecurityOptions cached_transport_options;
+
+ TransientCryptoCredentials();
+ ~TransientCryptoCredentials();
+
+ static CertKeyWrapper make_root_ca() {
+ auto dn = X509Certificate::DistinguishedName()
+ .country("US").state("CA").locality("Sunnyvale")
+ .organization("ACME, Inc.")
+ .organizational_unit("ACME Root CA")
+ .add_common_name("acme.example.com");
+ auto subject = X509Certificate::SubjectInfo(std::move(dn));
+ auto key = PrivateKey::generate_p256_ec_key();
+ auto params = X509Certificate::Params::self_signed(std::move(subject), key);
+ auto cert = X509Certificate::generate_from(std::move(params));
+ return {std::move(cert), std::move(key)};
+ }
+
+ static CertKeyWrapper make_host_creds(const CertKeyWrapper& root_ca_creds) {
+ auto dn = X509Certificate::DistinguishedName()
+ .country("US").state("CA").locality("Sunnyvale")
+ .organization("Wile E. Coyote, Ltd.")
+ .organizational_unit("Unit Testing and Anvil Dropping Division")
+ .add_common_name("localhost"); // Should technically not be needed, but including it anyway.
+ auto subject = X509Certificate::SubjectInfo(std::move(dn));
+ subject.add_subject_alt_name("DNS:localhost");
+ auto key = PrivateKey::generate_p256_ec_key();
+ auto params = X509Certificate::Params::issued_by(std::move(subject), key, root_ca_creds.cert, root_ca_creds.key);
+ params.valid_for = std::chrono::hours(1);
+ auto cert = X509Certificate::generate_from(std::move(params));
+ return {std::move(cert), std::move(key)};
+ }
+
+ static const TransientCryptoCredentials& instance();
+};
+
+TransientCryptoCredentials::TransientCryptoCredentials()
+ : root_ca(make_root_ca()),
+ host_creds(make_host_creds(root_ca)),
+ cached_transport_options(vespalib::net::tls::TransportSecurityOptions::Params().
+ ca_certs_pem(root_ca.cert->to_pem()).
+ cert_chain_pem(host_creds.cert->to_pem()).
+ private_key_pem(host_creds.key->private_to_pem()).
+ authorized_peers(vespalib::net::tls::AuthorizedPeers::allow_all_authenticated()))
+{}
+
+TransientCryptoCredentials::~TransientCryptoCredentials() = default;
+
+const TransientCryptoCredentials& TransientCryptoCredentials::instance() {
+ static TransientCryptoCredentials test_creds;
+ return test_creds;
+}
+
+}
namespace vespalib::test {
SocketSpec local_spec("tcp/localhost:123");
vespalib::net::tls::TransportSecurityOptions make_tls_options_for_testing() {
- auto ts_builder = vespalib::net::tls::TransportSecurityOptions::Params().
- ca_certs_pem(ca_pem).
- cert_chain_pem(cert_pem).
- private_key_pem(key_pem).
- authorized_peers(vespalib::net::tls::AuthorizedPeers::allow_all_authenticated()).
- disable_hostname_validation(true); // FIXME this is to avoid mass breakage of TLS'd networking tests.
- return vespalib::net::tls::TransportSecurityOptions(std::move(ts_builder));
+ return TransientCryptoCredentials::instance().cached_transport_options;
}
} // namespace vespalib::test