diff options
author | Vasily Gerasimov <UgnineSirdis@ydb.tech> | 2025-02-28 15:01:14 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-02-28 15:01:14 +0200 |
commit | c5b531e5ab62ce07f4aaf50552679425d810459c (patch) | |
tree | 5625fdb100eccc95057510a23f8125fe60734245 | |
parent | 1547cff981d4a39f2a007760ca202b1071a95cd3 (diff) | |
download | ydb-c5b531e5ab62ce07f4aaf50552679425d810459c.tar.gz |
Client certificates options in ydb cli (#8406)
43 files changed, 962 insertions, 31 deletions
diff --git a/ydb/apps/dstool/lib/common.py b/ydb/apps/dstool/lib/common.py index 9f7b539126..fae1480f15 100644 --- a/ydb/apps/dstool/lib/common.py +++ b/ydb/apps/dstool/lib/common.py @@ -219,7 +219,8 @@ class ConnectionParams: g.add_argument('--grpc-port', type=int, default=2135, metavar='PORT', help='GRPC port to use for procedure invocation') g.add_argument('--mon-port', type=int, default=8765, metavar='PORT', help='HTTP monitoring port for viewer JSON access') g.add_argument('--token-file', type=FileType(encoding='ascii'), metavar='PATH', help='Path to token file') - g.add_argument('--ca-file', metavar='PATH', dest='cafile', type=str, help='Path to a file containing the PEM encoding of the server root certificates for tls connections.') + g.add_argument('--ca-file', metavar='PATH', dest='cafile', type=str, help='File containing PEM encoded root certificates for SSL/TLS connections. ' + 'If this parameter is empty, the default roots will be used.') g.add_argument('--http-timeout', type=int, default=5, help='Timeout for blocking socket I/O operations during HTTP(s) queries') g.add_argument('--insecure', action='store_true', help='Allow insecure HTTPS fetching') g.add_argument('--use-ip', action='store_true', help='Use IP addresses instead of hostnames when connecting to endpoints') diff --git a/ydb/apps/ydb/CHANGELOG.md b/ydb/apps/ydb/CHANGELOG.md index d26ff9d0f1..54ad8d414e 100644 --- a/ydb/apps/ydb/CHANGELOG.md +++ b/ydb/apps/ydb/CHANGELOG.md @@ -1,3 +1,4 @@ +* Add options for client certificates in SSL/TLS connections. * Add `ydb admin node config init` command to initialize directory with node config files. * Add `ydb admin cluster config generate` command to generate dynamic config from static config on cluster. * Fixed memory leak in tpcds generator. diff --git a/ydb/core/driver_lib/cli_base/cli_cmds_db.cpp b/ydb/core/driver_lib/cli_base/cli_cmds_db.cpp index a1e823a94b..5098375100 100644 --- a/ydb/core/driver_lib/cli_base/cli_cmds_db.cpp +++ b/ydb/core/driver_lib/cli_base/cli_cmds_db.cpp @@ -883,6 +883,8 @@ public: ClientConfig.MaxInFlight = CommandConfig.ClientConfig.MaxInFlight; ClientConfig.EnableSsl = CommandConfig.ClientConfig.EnableSsl; ClientConfig.SslCredentials.pem_root_certs = CommandConfig.ClientConfig.SslCredentials.pem_root_certs; + ClientConfig.SslCredentials.pem_cert_chain = CommandConfig.ClientConfig.SslCredentials.pem_cert_chain; + ClientConfig.SslCredentials.pem_private_key = CommandConfig.ClientConfig.SslCredentials.pem_private_key; } template<typename T> diff --git a/ydb/core/driver_lib/cli_base/cli_cmds_root.cpp b/ydb/core/driver_lib/cli_base/cli_cmds_root.cpp index a42c7cb20a..d86ecb0e01 100644 --- a/ydb/core/driver_lib/cli_base/cli_cmds_root.cpp +++ b/ydb/core/driver_lib/cli_base/cli_cmds_root.cpp @@ -186,6 +186,7 @@ public: throw TMisuseException() << message; } ParseCaCerts(config); + ParseClientCert(config); config.Address = Address; if (!hostname) { @@ -195,6 +196,10 @@ public: if (config.EnableSsl) { CommandConfig.ClientConfig.EnableSsl = config.EnableSsl; CommandConfig.ClientConfig.SslCredentials.pem_root_certs = config.CaCerts; + if (config.ClientCert) { + CommandConfig.ClientConfig.SslCredentials.pem_cert_chain = config.ClientCert; + CommandConfig.ClientConfig.SslCredentials.pem_private_key = config.ClientCertPrivateKey; + } } } diff --git a/ydb/core/driver_lib/cli_base/cli_grpc.h b/ydb/core/driver_lib/cli_base/cli_grpc.h index 21b5542311..ca02f4ab6c 100644 --- a/ydb/core/driver_lib/cli_base/cli_grpc.h +++ b/ydb/core/driver_lib/cli_base/cli_grpc.h @@ -94,6 +94,8 @@ public: ClientConfig.MaxInFlight = CommandConfig.ClientConfig.MaxInFlight; ClientConfig.EnableSsl = CommandConfig.ClientConfig.EnableSsl; ClientConfig.SslCredentials.pem_root_certs = CommandConfig.ClientConfig.SslCredentials.pem_root_certs; + ClientConfig.SslCredentials.pem_cert_chain = CommandConfig.ClientConfig.SslCredentials.pem_cert_chain; + ClientConfig.SslCredentials.pem_private_key = CommandConfig.ClientConfig.SslCredentials.pem_private_key; } static int PrepareConfigCredentials(NGRpcProxy::TGRpcClientConfig clientConfig, TConfig& commandConfig) { @@ -159,4 +161,3 @@ public: } } - diff --git a/ydb/core/driver_lib/cli_utils/cli_cmds_root.cpp b/ydb/core/driver_lib/cli_utils/cli_cmds_root.cpp index e06d12e51b..218904fb30 100644 --- a/ydb/core/driver_lib/cli_utils/cli_cmds_root.cpp +++ b/ydb/core/driver_lib/cli_utils/cli_cmds_root.cpp @@ -56,11 +56,16 @@ public: config.EnableSsl = endpoint.EnableSsl.GetRef(); } ParseCaCerts(config); + ParseClientCert(config); CommandConfig.ClientConfig = NYdbGrpc::TGRpcClientConfig(endpoint.Address); if (config.EnableSsl) { CommandConfig.ClientConfig.EnableSsl = config.EnableSsl; CommandConfig.ClientConfig.SslCredentials.pem_root_certs = config.CaCerts; + if (config.ClientCert) { + CommandConfig.ClientConfig.SslCredentials.pem_cert_chain = config.ClientCert; + CommandConfig.ClientConfig.SslCredentials.pem_private_key = config.ClientCertPrivateKey; + } } } }; diff --git a/ydb/core/driver_lib/cli_utils/cli_cmds_tenant.cpp b/ydb/core/driver_lib/cli_utils/cli_cmds_tenant.cpp index e8f527886c..0c783b079e 100644 --- a/ydb/core/driver_lib/cli_utils/cli_cmds_tenant.cpp +++ b/ydb/core/driver_lib/cli_utils/cli_cmds_tenant.cpp @@ -142,6 +142,8 @@ public: ClientConfig.MaxInFlight = CommandConfig.ClientConfig.MaxInFlight; ClientConfig.EnableSsl = CommandConfig.ClientConfig.EnableSsl; ClientConfig.SslCredentials.pem_root_certs = CommandConfig.ClientConfig.SslCredentials.pem_root_certs; + ClientConfig.SslCredentials.pem_cert_chain = CommandConfig.ClientConfig.SslCredentials.pem_cert_chain; + ClientConfig.SslCredentials.pem_private_key = CommandConfig.ClientConfig.SslCredentials.pem_private_key; } int Run(TConfig &config) override diff --git a/ydb/core/driver_lib/run/main.cpp b/ydb/core/driver_lib/run/main.cpp index b26225fc14..00be7c4568 100644 --- a/ydb/core/driver_lib/run/main.cpp +++ b/ydb/core/driver_lib/run/main.cpp @@ -89,8 +89,11 @@ int MainRun(const TKikimrRunConfig& runConfig, std::shared_ptr<TModuleFactories> configParser.SetupGlobalOpts(opts); NMsgBusProxy::TMsgBusClientConfig mbusConfig; mbusConfig.ConfigureLastGetopt(opts, "mb-"); - opts.AddLongOption("ca-file", "Path to a file containing the PEM encoding of the server root certificates for tls connections.\n").RequiredArgument("PATH"); + opts.AddLongOption("ca-file", "File containing PEM encoded root certificates for SSL/TLS connections. If this parameter is empty, the default roots will be used.\n").RequiredArgument("PATH"); NDriverClient::HideOptions(opts); + opts.AddLongOption("client-cert-file", "File containing client certificate for SSL/TLS connections (PKCS#12 or PEM-encoded)").RequiredArgument("PATH"); + opts.AddLongOption("client-cert-key-file", "File containing PEM encoded client certificate private key for SSL/TLS connections").RequiredArgument("PATH"); + opts.AddLongOption("client-cert-key-password-file", "File containing password for client certificate private key (if key is encrypted). If key file is encrypted, but this option is not set, password will be asked interactively").RequiredArgument("PATH"); opts.AddLongOption('s', "server", "Server address to connect (default $KIKIMR_SERVER)").RequiredArgument("ADDR[:NUM]"); opts.AddLongOption('k', "token", "Security token").RequiredArgument("TOKEN"); opts.AddLongOption('f', "token-file", "Security token file").RequiredArgument("PATH"); @@ -203,4 +206,3 @@ int ParameterizedMain(int argc, char **argv, std::shared_ptr<NKikimr::TModuleFac return 1; } } - diff --git a/ydb/public/lib/ydb_cli/commands/ydb_command.cpp b/ydb/public/lib/ydb_cli/commands/ydb_command.cpp index cfdbae4fa8..24c19a0bc7 100644 --- a/ydb/public/lib/ydb_cli/commands/ydb_command.cpp +++ b/ydb/public/lib/ydb_cli/commands/ydb_command.cpp @@ -29,6 +29,7 @@ TDriverConfig TYdbCommand::CreateDriverConfig(TConfig& config) { driverConfig.UseSecureConnection(config.CaCerts); if (config.IsNetworkIntensive) driverConfig.SetNetworkThreadsNum(16); + driverConfig.UseClientCertificate(config.ClientCert, config.ClientCertPrivateKey); return driverConfig; } diff --git a/ydb/public/lib/ydb_cli/commands/ydb_profile.cpp b/ydb/public/lib/ydb_cli/commands/ydb_profile.cpp index 903ba21628..7afbc969df 100644 --- a/ydb/public/lib/ydb_cli/commands/ydb_profile.cpp +++ b/ydb/public/lib/ydb_cli/commands/ydb_profile.cpp @@ -244,6 +244,15 @@ namespace { if (profile->Has("ca-file")) { Cout << " ca-file: " << profile->GetValue("ca-file").as<TString>() << Endl; } + if (profile->Has("client-cert-file")) { + Cout << " client-cert-file: " << profile->GetValue("client-cert-file").as<TString>() << Endl; + } + if (profile->Has("client-cert-key-file")) { + Cout << " client-cert-key-file: " << profile->GetValue("client-cert-key-file").as<TString>() << Endl; + } + if (profile->Has("client-cert-key-password-file")) { + Cout << " client-cert-key-password-file: " << profile->GetValue("client-cert-key-password-file").as<TString>() << Endl; + } } } @@ -307,6 +316,15 @@ void TCommandConnectionInfo::PrintInfo(TConfig& config) { if (config.CaCertsFile) { Cout << "ca-file: " << config.CaCertsFile << Endl; } + if (config.ClientCertFile) { + Cout << "client-cert-file: " << config.ClientCertFile << Endl; + } + if (config.ClientCertPrivateKeyFile) { + Cout << "client-cert-key-file: " << config.ClientCertPrivateKeyFile << Endl; + } + if (config.ClientCertPrivateKeyPasswordFile) { + Cout << "client-cert-key-password-file: " << config.ClientCertPrivateKeyPasswordFile << Endl; + } } void TCommandConnectionInfo::PrintVerboseInfo(TConfig& config) { @@ -382,7 +400,10 @@ void TCommandProfileCommon::GetOptionsFromStdin() { {"user", User}, {"password-file", PasswordFile}, {"iam-endpoint", IamEndpoint}, - {"ca-file", CaCertsFile} + {"ca-file", CaCertsFile}, + {"client-cert-file", ClientCertFile}, + {"client-cert-key-file", ClientCertPrivateKeyFile}, + {"client-cert-key-password-file", ClientCertPrivateKeyPasswordFile}, }; while (Cin.ReadLine(line)) { Strip(line, trimmedLine); @@ -432,6 +453,15 @@ void TCommandProfileCommon::ConfigureProfile(const TString& profileName, std::sh if (cmdLine && CaCertsFile) { profile->SetValue("ca-file", CaCertsFile); } + if (cmdLine && ClientCertFile) { + profile->SetValue("client-cert-file", ClientCertFile); + } + if (cmdLine && ClientCertPrivateKeyFile) { + profile->SetValue("client-cert-key-file", ClientCertPrivateKeyFile); + } + if (cmdLine && ClientCertPrivateKeyPasswordFile) { + profile->SetValue("client-cert-key-password-file", ClientCertPrivateKeyPasswordFile); + } if (interactive) { TString activeProfileName = profileManager->GetActiveProfileName(); @@ -673,7 +703,8 @@ bool TCommandProfileCommon::AnyProfileOptionInCommandLine() { return Endpoint || Database || TokenFile || Oauth2KeyFile || IamTokenFile || YcTokenFile || SaKeyFile || UseMetadataCredentials || User || - PasswordFile || IamEndpoint || AnonymousAuth || CaCertsFile; + PasswordFile || IamEndpoint || AnonymousAuth || CaCertsFile || + ClientCertFile || ClientCertPrivateKeyFile || ClientCertPrivateKeyPasswordFile; } TCommandCreateProfile::TCommandCreateProfile() @@ -711,8 +742,17 @@ void TCommandProfileCommon::Config(TConfig& config) { .RequiredArgument("STR").StoreResult(&IamEndpoint); } opts.AddLongOption("ca-file", - "Path to a file containing the PEM encoding of the server root certificates for tls connections.") + "File containing PEM encoded root certificates for SSL/TLS connections.") .RequiredArgument("PATH").StoreResult(&CaCertsFile); + opts.AddLongOption("client-cert-file", + "File containing client certificate for SSL/TLS connections (PKCS#12 or PEM-encoded).") + .RequiredArgument("PATH").StoreResult(&ClientCertFile); + opts.AddLongOption("client-cert-key-file", + "File containing PEM encoded client certificate private key for SSL/TLS connections.") + .RequiredArgument("PATH").StoreResult(&ClientCertPrivateKeyFile); + opts.AddLongOption("client-cert-key-password-file", + "File containing password for client certificate private key (if key is encrypted). If key file is encrypted, but this option is not set, password will be asked interactively.") + .RequiredArgument("PATH").StoreResult(&ClientCertPrivateKeyPasswordFile); if (!IsStdinInteractive()) { GetOptionsFromStdin(); } @@ -1064,8 +1104,14 @@ void TCommandUpdateProfile::Config(TConfig& config) { if (config.UseIamAuth) { opts.AddLongOption("no-iam-endpoint", "Delete endpoint of IAM service from the profile").StoreTrue(&NoIamEndpoint); } - opts.AddLongOption("no-ca-file", "Delete path to file containing the PEM encoding of the " - "server root certificates for tls connections from the profile").StoreTrue(&NoCaCertsFile); + opts.AddLongOption("no-ca-file", "Delete path to file containing the PEM encoded " + "root certificates for SSL/TLS connections from the profile").StoreTrue(&NoCaCertsFile); + opts.AddLongOption("no-client-cert-file", "Delete path to file containing client certificate " + "for SSL/TLS connections").StoreTrue(&NoClientCertFile); + opts.AddLongOption("no-client-cert-key-file", "Delete path to file containing PEM encoded client " + "certificate private key for SSL/TLS connections").StoreTrue(&NoClientCertPrivateKeyFile); + opts.AddLongOption("no-client-cert-key-password-file", "Delete path to file containing password for " + "client certificate private key (if key is encrypted)").StoreTrue(&NoClientCertPrivateKeyPasswordFile); } void TCommandUpdateProfile::ValidateNoOptions() { @@ -1080,21 +1126,21 @@ void TCommandUpdateProfile::ValidateNoOptions() { throw TMisuseException() << "You cannot enter authentication options and the \"--no-auth\" option at the same time"; } TStringBuilder str; - if (Endpoint && NoEndpoint) { - str << "\"--endpoint\" and \"--no-endpoint\""; - } else { - if (Database && NoDatabase) { - str << "\"--database and \"--no-database\""; - } else { - if (IamEndpoint && NoIamEndpoint) { - str << "\"--iam-endpoint\" and \"--no-iam-endpoint\""; - } else { - if (CaCertsFile && NoCaCertsFile) { - str << "\"--ca-file\" and \"--no-ca-file\""; - } + auto addMutuallyExclusiveOptionError = [&](bool validationResult, TStringBuf optionName) { + if (validationResult) { + if (str) { + str << ", "; } + str << "\"--" << optionName << "\" and \"--no-" << optionName << "\""; } - } + }; + addMutuallyExclusiveOptionError(Endpoint && NoEndpoint, "endpoint"); + addMutuallyExclusiveOptionError(Database && NoDatabase, "database"); + addMutuallyExclusiveOptionError(IamEndpoint && NoIamEndpoint, "iam-endpoint"); + addMutuallyExclusiveOptionError(CaCertsFile && NoCaCertsFile, "ca-file"); + addMutuallyExclusiveOptionError(ClientCertFile && NoClientCertFile, "client-cert-file"); + addMutuallyExclusiveOptionError(ClientCertPrivateKeyFile && NoClientCertPrivateKeyFile, "client-cert-key-file"); + addMutuallyExclusiveOptionError(NoClientCertPrivateKeyPasswordFile && NoClientCertPrivateKeyPasswordFile, "client-cert-key-password-file"); if (!str.empty()) { throw TMisuseException() << "Options " << str << " are mutually exclusive"; } @@ -1116,6 +1162,15 @@ void TCommandUpdateProfile::DropNoOptions(std::shared_ptr<IProfile> profile) { if (NoCaCertsFile) { profile->RemoveValue("ca-file"); } + if (NoClientCertFile) { + profile->RemoveValue("client-cert-file"); + } + if (NoClientCertPrivateKeyFile) { + profile->RemoveValue("client-cert-key-file"); + } + if (NoClientCertPrivateKeyPasswordFile) { + profile->RemoveValue("client-cert-key-password-file"); + } } void TCommandUpdateProfile::Parse(TConfig& config) { diff --git a/ydb/public/lib/ydb_cli/commands/ydb_profile.h b/ydb/public/lib/ydb_cli/commands/ydb_profile.h index d4df0f3669..34a1ff00da 100644 --- a/ydb/public/lib/ydb_cli/commands/ydb_profile.h +++ b/ydb/public/lib/ydb_cli/commands/ydb_profile.h @@ -43,7 +43,7 @@ protected: TConfig& config, bool interactive, bool cmdLine); TString ProfileName, Endpoint, Database, TokenFile, Oauth2KeyFile, YcTokenFile, SaKeyFile, - IamTokenFile, IamEndpoint, User, PasswordFile, CaCertsFile; + IamTokenFile, IamEndpoint, User, PasswordFile, CaCertsFile, ClientCertFile, ClientCertPrivateKeyFile, ClientCertPrivateKeyPasswordFile; bool UseMetadataCredentials = false; bool AnonymousAuth = false; @@ -141,6 +141,9 @@ private: bool NoAuth = false; bool NoIamEndpoint = false; bool NoCaCertsFile = false; + bool NoClientCertFile = false; + bool NoClientCertPrivateKeyFile = false; + bool NoClientCertPrivateKeyPasswordFile = false; }; class TCommandReplaceProfile : public TCommandProfileCommon { diff --git a/ydb/public/lib/ydb_cli/commands/ydb_root_common.cpp b/ydb/public/lib/ydb_cli/commands/ydb_root_common.cpp index 7e176b5424..bd2af7886d 100644 --- a/ydb/public/lib/ydb_cli/commands/ydb_root_common.cpp +++ b/ydb/public/lib/ydb_cli/commands/ydb_root_common.cpp @@ -18,6 +18,7 @@ #include "ydb_workload.h" #include <ydb/public/lib/ydb_cli/commands/interactive/interactive_cli.h> +#include <ydb/public/lib/ydb_cli/common/cert_format_converter.h> #include <ydb-cpp-sdk/client/types/credentials/oauth2_token_exchange/credentials.h> #include <ydb-cpp-sdk/client/types/credentials/oauth2_token_exchange/from_file.h> #include <ydb-cpp-sdk/client/types/credentials/oauth2_token_exchange/jwt_token_source.h> @@ -352,18 +353,19 @@ void TClientCommandRootCommon::ExtractParams(TConfig& config) { ParseDatabase(config); ParseAddress(config); ParseCaCerts(config); + ParseClientCert(config); ParseIamEndpoint(config); ParseCredentials(config); } namespace { - inline void PrintSettingFromProfile(const TString& setting, std::shared_ptr<IProfile> profile, bool explicitOption) { + inline void PrintSettingFromProfile(const TString& setting, const std::shared_ptr<IProfile>& profile, bool explicitOption) { Cerr << "Using " << setting << " due to configuration in" << (explicitOption ? "" : " active") << " profile \"" << profile->GetName() << "\"" << (explicitOption ? " from explicit --profile option" : "") << Endl; } - inline TString GetProfileSource(std::shared_ptr<IProfile> profile, bool explicitOption) { + inline TString GetProfileSource(const std::shared_ptr<IProfile>& profile, bool explicitOption) { Y_ABORT_UNLESS(profile, "No profile to get source"); if (explicitOption) { return TStringBuilder() << "profile \"" << profile->GetName() << "\" from explicit --profile option"; @@ -372,7 +374,7 @@ namespace { } } -bool TClientCommandRootCommon::TryGetParamFromProfile(const TString& name, std::shared_ptr<IProfile> profile, bool explicitOption, +bool TClientCommandRootCommon::TryGetParamFromProfile(const TString& name, const std::shared_ptr<IProfile>& profile, bool explicitOption, std::function<bool(const TString&, const TString&, bool)> callback) { if (profile && profile->Has(name)) { return callback(profile->GetValue(name).as<TString>(), GetProfileSource(profile, explicitOption), explicitOption); @@ -380,6 +382,34 @@ bool TClientCommandRootCommon::TryGetParamFromProfile(const TString& name, std:: return false; } +bool TClientCommandRootCommon::TryGetParamsPackFromProfile(const std::shared_ptr<IProfile>& profile, bool explicitOption, + std::function<bool(const TString& /*source*/, bool /*explicit*/, const std::vector<TString>& /*values*/)> callback, + const std::initializer_list<TString>& names) { + if (!profile) { + return false; + } + bool hasAtLeastOne = false; + for (const TString& name : names) { + if (profile->Has(name)) { + hasAtLeastOne = true; + break; + } + } + if (hasAtLeastOne) { + std::vector<TString> values; + values.reserve(names.size()); + for (const TString& name : names) { + if (profile->Has(name)) { + values.emplace_back(profile->GetValue(name).as<TString>()); + } else { + values.emplace_back(); + } + } + return callback(GetProfileSource(profile, explicitOption), explicitOption, values); + } + return false; +} + void TClientCommandRootCommon::ParseCaCerts(TConfig& config) { auto getCaFile = [this, &config] (const TString& param, const TString& sourceText, bool explicitOption) { if (!IsCaCertsFileSet && (explicitOption || !Profile)) { @@ -407,16 +437,74 @@ void TClientCommandRootCommon::ParseCaCerts(TConfig& config) { } } +void TClientCommandRootCommon::ParseClientCert(TConfig& config) { + auto getClientCertFiles = [this, &config] (const TString& sourceText, bool explicitOption, const std::vector<TString>& values) { + Y_ABORT_UNLESS(values.size() == 3); + const TString& clientCertFileParam = values[0]; + const TString& clientCertPrivateKeyFileParam = values[1]; + const TString& clientCertPrivateKeyPasswordFileParam = values[2]; + if (!IsClientCertFileSet && (explicitOption || !Profile)) { + config.ClientCertFile = clientCertFileParam; + config.ClientCertPrivateKeyFile = clientCertPrivateKeyFileParam; + config.ClientCertPrivateKeyPasswordFile = clientCertPrivateKeyPasswordFileParam; + IsClientCertFileSet = true; + GetClientCert(config); + } + if (!IsVerbose()) { + return true; + } + Cerr << "Using client certificate from file: " << clientCertFileParam << Endl; + config.ConnectionParams["client-cert-file"].push_back({clientCertFileParam, sourceText}); + config.ConnectionParams["client-cert-key-file"].push_back({clientCertPrivateKeyFileParam, sourceText}); + config.ConnectionParams["client-cert-key-password-file"].push_back({clientCertPrivateKeyPasswordFileParam, sourceText}); + return false; + }; + // Priority 1. Explicit --client-cert-file/--client-cert-key-file options + if (ClientCertFile) { + if (getClientCertFiles("explicit --client-cert-file/--client-cert-key-file/--client-cert-key-password-file options", true, { ClientCertFile, ClientCertPrivateKeyFile, ClientCertPrivateKeyPasswordFile })) { + return; + } + } + // Priority 2. Explicit --profile option + if (TryGetParamsPackFromProfile(Profile, true, getClientCertFiles, { "client-cert-file", "client-cert-key-file", "client-cert-key-password-file" })) { + return; + } + // Priority 3. Active profile (if --profile option is not specified) + if (TryGetParamsPackFromProfile(ProfileManager->GetActiveProfile(), false, getClientCertFiles, { "client-cert-file", "client-cert-key-file", "client-cert-key-password-file" })) { + return; + } +} + void TClientCommandRootCommon::GetCaCerts(TConfig& config) { if (!config.EnableSsl && !config.CaCertsFile.empty()) { throw TMisuseException() - << "\"ca-file\" option provided for a non-ssl connection. Use grpcs:// prefix for host to connect using SSL."; + << "\"ca-file\" option is provided for a non-ssl connection. Use grpcs:// prefix for host to connect using SSL."; } if (!config.CaCertsFile.empty()) { config.CaCerts = ReadFromFile(config.CaCertsFile, "CA certificates"); } } +void TClientCommandRootCommon::GetClientCert(TConfig& config) { + if (!config.EnableSsl && !config.ClientCertFile.empty()) { + throw TMisuseException() + << "\"client-cert-file\"/\"client-cert-key-file\"/\"client-cert-key-password-file\" options are provided for a non-ssl connection. Use grpcs:// prefix for host to connect using SSL."; + } + if (!config.ClientCertFile.empty()) { + config.ClientCert = ReadFromFile(config.ClientCertFile, "Client certificate"); + } + if (!config.ClientCertPrivateKeyFile.empty()) { + config.ClientCertPrivateKey = ReadFromFile(config.ClientCertPrivateKeyFile, "Client certificate private key"); + } + if (!config.ClientCertPrivateKeyPasswordFile.empty()) { + config.ClientCertPrivateKeyPassword = ReadFromFile(config.ClientCertPrivateKeyPasswordFile, "Client certificate private key password"); + } + + // Convert certificates from PKCS#12 to PEM or encrypted private key to nonencrypted + // May ask for password + std::tie(config.ClientCert, config.ClientCertPrivateKey) = ConvertCertToPEM(config.ClientCert, config.ClientCertPrivateKey, config.ClientCertPrivateKeyPassword); +} + void TClientCommandRootCommon::ParseAddress(TConfig& config) { auto getAddress = [this, &config] (const TString& param, const TString& sourceText, bool explicitOption) { TString address; diff --git a/ydb/public/lib/ydb_cli/commands/ydb_root_common.h b/ydb/public/lib/ydb_cli/commands/ydb_root_common.h index 2e7d353fb1..d55580191d 100644 --- a/ydb/public/lib/ydb_cli/commands/ydb_root_common.h +++ b/ydb/public/lib/ydb_cli/commands/ydb_root_common.h @@ -52,12 +52,20 @@ private: void ParseDatabase(TConfig& config); void ParseIamEndpoint(TConfig& config); void ParseCaCerts(TConfig& config) override; + void ParseClientCert(TConfig& config) override; void GetAddressFromString(TConfig& config, TString* result = nullptr); bool ParseProtocolNoConfig(TString& message); void GetCaCerts(TConfig& config); - bool TryGetParamFromProfile(const TString& name, std::shared_ptr<IProfile> profile, bool explicitOption, + void GetClientCert(TConfig& config); + bool TryGetParamFromProfile(const TString& name, const std::shared_ptr<IProfile>& profile, bool explicitOption, std::function<bool(const TString&, const TString&, bool)> callback); + // Gets more than one params from one profile source. + // Returns true if at least one of the params are found in profile. + bool TryGetParamsPackFromProfile(const std::shared_ptr<IProfile>& profile, bool explicitOption, + std::function<bool(const TString& /*source*/, bool /*explicit*/, const std::vector<TString>& /*values*/)> callback, + const std::initializer_list<TString>& names); + TString Database; ui32 VerbosityLevel = 0; @@ -88,6 +96,7 @@ private: bool IsDatabaseSet = false; bool IsIamEndpointSet = false; bool IsCaCertsFileSet = false; + bool IsClientCertFileSet = false; bool IsAuthSet = false; }; diff --git a/ydb/public/lib/ydb_cli/common/cert_format_converter.cpp b/ydb/public/lib/ydb_cli/common/cert_format_converter.cpp new file mode 100644 index 0000000000..ac9186460b --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/cert_format_converter.cpp @@ -0,0 +1,207 @@ +#include "cert_format_converter.h" + +#include <ydb/public/lib/ydb_cli/common/common.h> + +#include <util/generic/strbuf.h> +#include <util/stream/output.h> + +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/pkcs12.h> +#include <openssl/pem.h> +#include <openssl/x509.h> + +namespace NYdb::NConsoleClient { + +namespace { + +class TOpenSslObjectFree { +public: + TOpenSslObjectFree() = default; + TOpenSslObjectFree(const TOpenSslObjectFree&) = default; + TOpenSslObjectFree(TOpenSslObjectFree&&) = default; + + TOpenSslObjectFree& operator=(const TOpenSslObjectFree&) = default; + TOpenSslObjectFree& operator=(TOpenSslObjectFree&&) = default; + + void operator()(X509* x509) const { + X509_free(x509); + } + + void operator()(BIO* bio) const { + BIO_free(bio); + } + + void operator()(EVP_PKEY* pkey) const { + EVP_PKEY_free(pkey); + } + + void operator()(PKCS12* p) const { + PKCS12_free(p); + } +}; + +struct TPasswordProcessor { + TString Password; + bool IsInteractiveMode; + bool PasswordIsUsed = false; + + const TString& GetPassword() { + if (PasswordIsUsed || Password || !IsInteractiveMode) { + return Password; + } + PasswordIsUsed = true; + Cerr << "Enter private key password: "; + return Password = InputPassword(); + } + + static int PasswordCallback(char* buf, int size, int rwflag, void* userdata) { + Y_UNUSED(rwflag); + const TString& password = reinterpret_cast<TPasswordProcessor*>(userdata)->GetPassword(); + size_t s = 0; + if (s = Min<size_t>(password.size(), size)) { + memcpy(buf, password.c_str(), s); + } + return static_cast<int>(s); + } +}; + +using TX509Ptr = std::unique_ptr<X509, TOpenSslObjectFree>; +using TBioPtr = std::unique_ptr<BIO, TOpenSslObjectFree>; +using TEvpPKeyPtr = std::unique_ptr<EVP_PKEY, TOpenSslObjectFree>; +using TPkcs12Ptr = std::unique_ptr<PKCS12, TOpenSslObjectFree>; + +TString ToString(const TBioPtr& bio) { + char* buf; + size_t len = BIO_get_mem_data(bio.get(), &buf); + return TString(buf, len); +} + +TBioPtr ToBio(const TStringBuf& buf) { + return TBioPtr(BIO_new_mem_buf(buf.data(), buf.size())); +} + +TBioPtr CreateMemBio() { + return TBioPtr(BIO_new(BIO_s_mem())); +} + +TString GetLastOpenSslError() { + TBioPtr bio = CreateMemBio(); + ERR_print_errors(bio.get()); + return ToString(bio); +} + +TString GetOpenSslErrorText(int errorCode) { + TStringBuilder result; + result << "Code " << errorCode; + if (TString err = GetLastOpenSslError()) { + result << ": " << err; + } + return std::move(result); +} + +TX509Ptr TryReadCertFromPEM(const TString& cert) { + auto bio = ToBio(cert); + return TX509Ptr(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); +} + +TEvpPKeyPtr TryReadPrivateKeyFromPEM(const TString& key, TPasswordProcessor& password) { + auto bio = ToBio(key); + return TEvpPKeyPtr(PEM_read_bio_PrivateKey(bio.get(), nullptr, &TPasswordProcessor::PasswordCallback, &password)); +} + +TString EncodeCertToPEM(const TX509Ptr& cert) { + TBioPtr bio = CreateMemBio(); + + if (int err = PEM_write_bio_X509(bio.get(), cert.get()); err <= 0) { + throw yexception() << "Failed to write certificate: " << GetOpenSslErrorText(err); + } + return ToString(bio); +} + +TString EncodePrivateKeyToPEM(const TEvpPKeyPtr& key) { + TBioPtr bio = CreateMemBio(); + + if (int err = PEM_write_bio_PrivateKey(bio.get(), key.get(), nullptr, nullptr, 0, nullptr, nullptr); err <= 0) { + throw yexception() << "Failed to write private key: " << GetOpenSslErrorText(err); + } + return ToString(bio); +} + +TPkcs12Ptr TryReadPkcs12(const TString& cert) { + auto bio = ToBio(cert); + TPkcs12Ptr pkcs12(d2i_PKCS12_bio(bio.get(), nullptr)); + return pkcs12; +} + +bool IsPasswordProblem() { + while (unsigned long err = ERR_get_error()) { + if (ERR_GET_REASON(err) == PKCS12_R_MAC_VERIFY_FAILURE) { + return true; + } + } + return false; +} + +void ParsePkcs12(const TPkcs12Ptr& pkcs12, TX509Ptr& cert, TEvpPKeyPtr& key, TPasswordProcessor& password) { + X509* certPtr = nullptr; + EVP_PKEY* keyPtr = nullptr; + int err = PKCS12_parse(pkcs12.get(), password.Password.c_str(), &keyPtr, &certPtr, nullptr); + if (!password.Password && IsPasswordProblem()) { // Password in not correct, try to ask password from user (if possible) + if (password.GetPassword()) { // Got new password + err = PKCS12_parse(pkcs12.get(), password.Password.c_str(), &keyPtr, &certPtr, nullptr); + } + } + if (err <= 0) { + return; + } + if (certPtr) { + cert.reset(certPtr); + } + if (keyPtr) { + key.reset(keyPtr); + } +} + +} // anonymous + +std::pair<TString, TString> ConvertCertToPEM( + const TString& certificate, + const TString& privateKey, + const TString& privateKeyPassword, + bool isInteractiveMode +) +{ + TPasswordProcessor password = { + .Password = privateKeyPassword, + .IsInteractiveMode = isInteractiveMode, + }; + + TString certResult, privateKeyResult; + TX509Ptr cert; + TEvpPKeyPtr key; + if (certificate) { + cert = TryReadCertFromPEM(certificate); + } + + if (!key && privateKey) { + key = TryReadPrivateKeyFromPEM(privateKey, password); + } + + if (!key && certificate) { // There may be a concatenated file with both cert and key + key = TryReadPrivateKeyFromPEM(certificate, password); + } + + if (!cert || !key) { + if (TPkcs12Ptr pkcs12 = TryReadPkcs12(certificate)) { + ParsePkcs12(pkcs12, cert, key, password); + } + } + + if (cert && key) { + return {EncodeCertToPEM(cert), EncodePrivateKeyToPEM(key)}; + } + throw yexception() << "Failed to parse client certificate and/or private key"; +} + +} // namespace NYdb::NConsoleClient diff --git a/ydb/public/lib/ydb_cli/common/cert_format_converter.h b/ydb/public/lib/ydb_cli/common/cert_format_converter.h new file mode 100644 index 0000000000..55babfdf0c --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/cert_format_converter.h @@ -0,0 +1,36 @@ +#pragma once +#include "interactive.h" + +#include <util/generic/string.h> + +#include <utility> + +namespace NYdb::NConsoleClient { + +// Checks current certificate/private key format. +// Supported formats are: PEM and PKCS#12. +// If certificate/private key are not encoded in PEM format, +// or if private key is encrypted with password phrase, +// tries to convert them to PEM without password protection +// (grpc lib requires client certificate to be encoded in this way). +// In interactive mode asks user to enter password if it is required and not set. +// In non-interactive mode fails in this case. +// Returns <certificate, private key>. +// +// So, supported combinations are: +// 1. Cert and key in PEM format, key is not encrypted. +// Just return cert and key as is. +// 2. Cert and key in PEM format, key is encrypted. +// Reencode private key to be suitable for grpc lib (not encrypted). Maybe ask password from user interactively. +// 3. Cert is in PKCS#12 encoding, unencrypted key is embedded into it. +// Reencode cert and key to PEM +// 4. Cert is in PKCS#12 encoding, encrypted key is embedded into it. +// Reencode cert and key to PEM without encryption. Maybe ask password from user interactively. +std::pair<TString, TString> ConvertCertToPEM( + const TString& certificate, + const TString& privateKey, + const TString& privateKeyPassword, + bool isInteractiveMode = IsStdinInteractive() +); + +} // namespace NYdb::NConsoleClient diff --git a/ydb/public/lib/ydb_cli/common/cert_format_converter_ut.cpp b/ydb/public/lib/ydb_cli/common/cert_format_converter_ut.cpp new file mode 100644 index 0000000000..d503700239 --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/cert_format_converter_ut.cpp @@ -0,0 +1,105 @@ +#include "cert_format_converter.h" + +#include <library/cpp/testing/common/env.h> +#include <library/cpp/testing/unittest/registar.h> + +#include <util/stream/file.h> + +namespace NYdb::NConsoleClient { + +namespace { + +TString GetDataPath(const TString& path) { + return ArcadiaFromCurrentLocation(__SOURCE_FILE__, "ut/test_data/" + path); +} + +TString GetDataByPath(const TString& path) { + if (path) { + return TFileInput(GetDataPath(path)).ReadAll(); + } + return {}; +} + +const TString PASSWORD = "p@ssw0rd"; + +} // anonymous + +Y_UNIT_TEST_SUITE(CertFormatConverter) { + void ValidateParse(const TString& certPath, const TString& keyPath, bool withPassword) { + TString certContent = GetDataByPath(certPath); + TString keyContent = GetDataByPath(keyPath); + TString cert, key; + if (withPassword) { + // Does not parse without password + // And also does not ask it, because we are in noninteractive mode + UNIT_ASSERT_EXCEPTION_C( + std::tie(cert, key) = ConvertCertToPEM(certContent, keyContent, "", false), + yexception, + "Cert: " << certPath << ". Key: " << keyPath); + + UNIT_ASSERT_NO_EXCEPTION_C( + std::tie(cert, key) = ConvertCertToPEM(certContent, keyContent, PASSWORD, false), + "Cert: " << certPath << ". Key: " << keyPath); + } else { + UNIT_ASSERT_NO_EXCEPTION_C( + std::tie(cert, key) = ConvertCertToPEM(certContent, keyContent, "", false), + "Cert: " << certPath << ". Key: " << keyPath); + } + + UNIT_ASSERT_STRING_CONTAINS_C(cert, "-----BEGIN CERTIFICATE-----", "Cert: " << certPath << ". Key: " << keyPath); + UNIT_ASSERT_STRING_CONTAINS_C(key, "-----BEGIN PRIVATE KEY-----", "Cert: " << certPath << ". Key: " << keyPath); + + // Check that returned cert/key can be parsed + UNIT_ASSERT_NO_EXCEPTION_C( + ConvertCertToPEM(cert, key, "", false), + "Cert: " << certPath << ". Key: " << keyPath); + } + + Y_UNIT_TEST(ParseFromPEM) { + ValidateParse("ec-cert.pem", "ec-private-key.pem", false); + ValidateParse("ec-cert.p12", "", false); + ValidateParse("ec-all.pem", "ec-all.pem", false); + ValidateParse("ec-all.pem", "", false); + ValidateParse("rsa-cert.pem", "rsa-private-key.pem", false); + ValidateParse("rsa-cert.p12", "", false); + ValidateParse("rsa-all.pem", "rsa-all.pem", false); + ValidateParse("rsa-all.pem", "", false); + + ValidateParse("ec-cert.pem", "ec-private-key-pwd.pem", true); + ValidateParse("ec-cert-pwd.p12", "", true); + ValidateParse("ec-all-pwd.pem", "ec-all-pwd.pem", true); + ValidateParse("ec-all-pwd.pem", "", true); + ValidateParse("rsa-cert.pem", "rsa-private-key-pwd.pem", true); + ValidateParse("rsa-cert-pwd.p12", "", true); + ValidateParse("rsa-all-pwd.pem", "rsa-all-pwd.pem", true); + ValidateParse("rsa-all-pwd.pem", "", true); + } + + Y_UNIT_TEST(InvalidKey) { + TString certContent = GetDataByPath("ec-cert.pem"); + TString keyContent = "invalid"; + UNIT_ASSERT_EXCEPTION( + ConvertCertToPEM(certContent, keyContent, "", false), + yexception); + } + + Y_UNIT_TEST(InvalidCert) { + TString certContent = "invalid"; + TString keyContent = GetDataByPath("rsa-private-key.pem"); + UNIT_ASSERT_EXCEPTION( + ConvertCertToPEM(certContent, keyContent, "", false), + yexception); + } + + Y_UNIT_TEST(InvalidKeyTakeFromCert) { + TString certContent = GetDataByPath("ec-all.pem"); + TString keyContent = "invalid"; + + UNIT_ASSERT_NO_EXCEPTION(ConvertCertToPEM(certContent, keyContent, "", false)); + + certContent = GetDataByPath("ec-cert-pwd.p12"); + UNIT_ASSERT_NO_EXCEPTION(ConvertCertToPEM(certContent, keyContent, PASSWORD, false)); + } +} + +} // namespace NYdb::NConsoleClient diff --git a/ydb/public/lib/ydb_cli/common/command.h b/ydb/public/lib/ydb_cli/common/command.h index 324a729c5c..d3ea848edf 100644 --- a/ydb/public/lib/ydb_cli/common/command.h +++ b/ydb/public/lib/ydb_cli/common/command.h @@ -110,6 +110,18 @@ public: TString Database; TString CaCerts; TString CaCertsFile; + TString ClientCert; + TString ClientCertPrivateKey; + TString ClientCertPrivateKeyPassword; + TString ClientCertFile; + TString ClientCertPrivateKeyFile; + TString ClientCertPrivateKeyPasswordFile; + + // Client cert initialization. + // Parses certificate from dirrefent formats. + // Can ask for password if private key is protected with password and it is not set in options. + void InitClientCert(); + TMap<TString, TVector<TConnectionParam>> ConnectionParams; bool EnableSsl = false; bool IsNetworkIntensive = false; diff --git a/ydb/public/lib/ydb_cli/common/root.cpp b/ydb/public/lib/ydb_cli/common/root.cpp index 3ebbbd2686..790967a560 100644 --- a/ydb/public/lib/ydb_cli/common/root.cpp +++ b/ydb/public/lib/ydb_cli/common/root.cpp @@ -1,4 +1,5 @@ #include "root.h" +#include "cert_format_converter.h" #include <util/folder/path.h> #include <util/folder/dirut.h> #include <util/string/strip.h> @@ -22,9 +23,19 @@ void TClientCommandRootBase::Config(TConfig& config) { opts.AddLongOption('t', "time", "Show request execution time").NoArgument().SetFlag(&TimeRequests); opts.AddLongOption('o', "progress", "Show progress of long requests").NoArgument().SetFlag(&ProgressRequests); opts.AddLongOption("ca-file", - "Path to a file containing the PEM encoding of the server root certificates for tls connections.\n" + "File containing PEM encoded root certificates for SSL/TLS connections.\n" "If this parameter is empty, the default roots will be used.") .RequiredArgument("PATH").StoreResult(&CaCertsFile); + opts.AddLongOption("client-cert-file", + "File containing client certificate for SSL/TLS connections (PKCS#12 or PEM-encoded).") + .RequiredArgument("PATH").StoreResult(&ClientCertFile); + opts.AddLongOption("client-cert-key-file", + "File containing PEM encoded client certificate private key for SSL/TLS connections.") + .RequiredArgument("PATH").StoreResult(&ClientCertPrivateKeyFile); + opts.AddLongOption("client-cert-key-password-file", + "File containing password for client certificate private key (if key is encrypted). " + "If key file is encrypted, but this option is not set, password will be asked interactively.") + .RequiredArgument("PATH").StoreResult(&ClientCertPrivateKeyPasswordFile); opts.SetCustomUsage(config.ArgV[0]); config.SetFreeArgsMin(1); @@ -92,6 +103,27 @@ void TClientCommandRootBase::ParseCaCerts(TConfig& config) { config.CaCerts = ReadFromFile(CaCertsFile, "CA certificates"); } +void TClientCommandRootBase::ParseClientCert(TConfig& config) { + if (ClientCertFile.empty()) { + return; + } + if (!config.EnableSsl) { + throw TMisuseException() + << "\"client-cert-file\" option provided for a non-ssl connection. Use grpcs:// prefix for host to connect using SSL."; + } + config.ClientCert = ReadFromFile(ClientCertFile, "Client certificate"); + if (ClientCertPrivateKeyFile) { + config.ClientCertPrivateKey = ReadFromFile(ClientCertPrivateKeyFile, "Client certificate private key"); + } + if (ClientCertPrivateKeyPasswordFile) { + config.ClientCertPrivateKeyPassword = ReadFromFile(ClientCertPrivateKeyPasswordFile, "Client certificate private key password"); + } + + // Convert certificates from PKCS#12 to PEM or encrypted private key to nonencrypted + // May ask for password + std::tie(config.ClientCert, config.ClientCertPrivateKey) = ConvertCertToPEM(config.ClientCert, config.ClientCertPrivateKey, config.ClientCertPrivateKeyPassword); +} + void TClientCommandRootBase::ParseCredentials(TConfig& config) { ParseToken(Token, TokenFile, "YDB_TOKEN", true); if (!Token.empty()) { diff --git a/ydb/public/lib/ydb_cli/common/root.h b/ydb/public/lib/ydb_cli/common/root.h index 6ccd4cce46..12dccf1348 100644 --- a/ydb/public/lib/ydb_cli/common/root.h +++ b/ydb/public/lib/ydb_cli/common/root.h @@ -17,6 +17,9 @@ public: TString Token; TString TokenFile; TString CaCertsFile; + TString ClientCertFile; + TString ClientCertPrivateKeyFile; + TString ClientCertPrivateKeyPasswordFile; virtual void Config(TConfig& config) override; virtual void Parse(TConfig& config) override; @@ -27,6 +30,7 @@ protected: void ParseToken(TString& token, TString& tokenFile, const TString& envName, bool useDefaultToken = false); bool ParseProtocol(TConfig& config, TString& message); virtual void ParseCaCerts(TConfig& config); + virtual void ParseClientCert(TConfig& config); virtual void ParseCredentials(TConfig& config); virtual void ParseAddress(TConfig& config) = 0; }; diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/ca-private-key.pem b/ydb/public/lib/ydb_cli/common/ut/test_data/ca-private-key.pem new file mode 100644 index 0000000000..f565c71800 --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/ca-private-key.pem @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDAK2UOfGT0ZDbXUT3qO351S4UiP2bqxD/YrGSS0VYB1rlK3sey7FS0G +DaF8/es/NeagBwYFK4EEACKhZANiAAT+vF2+8tuWAlb1+ByT9F4cygVYlOvfsiU/ +lW1TQwZZXV6tTWqPqCwwjJ96l11T62Dc4tfLlBSea6Sx6k1JvbjXMeYICdeIASQO +mTF/d+V3FpC7fJ7L0RhfFr9v+Dj1OaE= +-----END EC PRIVATE KEY----- diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/cert-ext.cfg b/ydb/public/lib/ydb_cli/common/ut/test_data/cert-ext.cfg new file mode 100644 index 0000000000..86b4bc9c44 --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/cert-ext.cfg @@ -0,0 +1,10 @@ +basicConstraints = CA:FALSE +nsCertType = server, client +nsComment = "Test certificate" +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer:always +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = serverAuth, clientAuth +subjectAltName = @alt_names +[alt_names] +DNS.1 = host.ydb.net diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/cert.cfg b/ydb/public/lib/ydb_cli/common/ut/test_data/cert.cfg new file mode 100644 index 0000000000..b9eb31ba74 --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/cert.cfg @@ -0,0 +1,15 @@ +[req] +default_md = sha512 +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +[req] +distinguished_name = test_host +req_extensions = req_ext +prompt = no +[test_host] +O = YDB +CN = host.ydb.net +[req_ext] +subjectAltName = @alt_names +[alt_names] +DNS.1 = host.ydb.net diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/ec-all-pwd.pem b/ydb/public/lib/ydb_cli/common/ut/test_data/ec-all-pwd.pem new file mode 100644 index 0000000000..4fea9f97fd --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/ec-all-pwd.pem @@ -0,0 +1,27 @@ +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,972C596A7496261BE01ECBC6D8A5034D + +xgqlS5qHKMwyBK/8m+xKTcPNvMK+8ieY688cr+iG28x86puF8AQNGNiSJBHSkWrK +IQhAV53UhkiX1rCjYFYrri84qcwu5FJ6DARJojvhSic3W1pBsbNN0WKmFL/3XZ8K +bMM95QxqhjQDnEvXuxmmJwA+VFa7K+tjlioVaSu1oZrUKLiUzXu3hRrPnYiAU9GJ +TZFZZSseFV6Eyn3vWntOt33f9VbcHICp/HVz/K3kUhE= +-----END EC PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIC1DCCAlqgAwIBAgITfoCHUO2Lyx7Ge7aitBhp8+57FTAKBggqhkjOPQQDBDBF +MQwwCgYDVQQKDANZREIxGTAXBgNVBAMMEFlEQiBUZXN0IFJvb3QgQ0ExGjAYBgkq +hkiG9w0BCQEWC3lkYkB5ZGIueWRiMB4XDTI1MDIyNzE1MzA0MFoXDTI2MDIyNzE1 +MzA0MFowJTEMMAoGA1UECgwDWURCMRUwEwYDVQQDDAxob3N0LnlkYi5uZXQwdjAQ +BgcqhkjOPQIBBgUrgQQAIgNiAATS8Pia9B3jaelvF3crFc5d6HSqqGwPzvEACI9T +iP4a/hnqhpZEVI9kk7RWmiV7xG2PL61VHS3WtguAF/RwlFJQJ2tscWYx0qtjcAdI +i3xui8PT6mmHG/I0CbFZ4uAa6/qjggEqMIIBJjAJBgNVHRMEAjAAMBEGCWCGSAGG ++EIBAQQEAwIGwDAfBglghkgBhvhCAQ0EEhYQVGVzdCBjZXJ0aWZpY2F0ZTAdBgNV +HQ4EFgQUT12c+6pblN4Hm2ZZk4WBOe7uaTgwgYAGA1UdIwR5MHeAFMm4UhwU9rl3 +UYH3NPzzFMl1OIOboUmkRzBFMQwwCgYDVQQKDANZREIxGTAXBgNVBAMMEFlEQiBU +ZXN0IFJvb3QgQ0ExGjAYBgkqhkiG9w0BCQEWC3lkYkB5ZGIueWRighRYDsITD3rH +t3W4BIPBfQDGprUiBTALBgNVHQ8EBAMCBeAwHQYDVR0lBBYwFAYIKwYBBQUHAwEG +CCsGAQUFBwMCMBcGA1UdEQQQMA6CDGhvc3QueWRiLm5ldDAKBggqhkjOPQQDBANo +ADBlAjEA2c9hW4aMJ1agvFZlyBVXp8kOrZwNl/ycCKBdJ6XsxBeqHZJH84+p9hmq +a1WwnE5dAjAsMGUiHFMZCPuFMt1ssOk1HrMgmv0pYXYPcFBElZZ58svFvog5gyhn +GZ5s8IGptTY= +-----END CERTIFICATE----- diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/ec-all.pem b/ydb/public/lib/ydb_cli/common/ut/test_data/ec-all.pem new file mode 100644 index 0000000000..2c405bb755 --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/ec-all.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIC1DCCAlqgAwIBAgITfoCHUO2Lyx7Ge7aitBhp8+57FTAKBggqhkjOPQQDBDBF +MQwwCgYDVQQKDANZREIxGTAXBgNVBAMMEFlEQiBUZXN0IFJvb3QgQ0ExGjAYBgkq +hkiG9w0BCQEWC3lkYkB5ZGIueWRiMB4XDTI1MDIyNzE1MzA0MFoXDTI2MDIyNzE1 +MzA0MFowJTEMMAoGA1UECgwDWURCMRUwEwYDVQQDDAxob3N0LnlkYi5uZXQwdjAQ +BgcqhkjOPQIBBgUrgQQAIgNiAATS8Pia9B3jaelvF3crFc5d6HSqqGwPzvEACI9T +iP4a/hnqhpZEVI9kk7RWmiV7xG2PL61VHS3WtguAF/RwlFJQJ2tscWYx0qtjcAdI +i3xui8PT6mmHG/I0CbFZ4uAa6/qjggEqMIIBJjAJBgNVHRMEAjAAMBEGCWCGSAGG ++EIBAQQEAwIGwDAfBglghkgBhvhCAQ0EEhYQVGVzdCBjZXJ0aWZpY2F0ZTAdBgNV +HQ4EFgQUT12c+6pblN4Hm2ZZk4WBOe7uaTgwgYAGA1UdIwR5MHeAFMm4UhwU9rl3 +UYH3NPzzFMl1OIOboUmkRzBFMQwwCgYDVQQKDANZREIxGTAXBgNVBAMMEFlEQiBU +ZXN0IFJvb3QgQ0ExGjAYBgkqhkiG9w0BCQEWC3lkYkB5ZGIueWRighRYDsITD3rH +t3W4BIPBfQDGprUiBTALBgNVHQ8EBAMCBeAwHQYDVR0lBBYwFAYIKwYBBQUHAwEG +CCsGAQUFBwMCMBcGA1UdEQQQMA6CDGhvc3QueWRiLm5ldDAKBggqhkjOPQQDBANo +ADBlAjEA2c9hW4aMJ1agvFZlyBVXp8kOrZwNl/ycCKBdJ6XsxBeqHZJH84+p9hmq +a1WwnE5dAjAsMGUiHFMZCPuFMt1ssOk1HrMgmv0pYXYPcFBElZZ58svFvog5gyhn +GZ5s8IGptTY= +-----END CERTIFICATE----- +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDBCn8VZ3bNXU7QQV1JeKmahm1sbcy2gctHUuVG9bN28Hyh1kuLiOZDr +RntgGfEa1QGgBwYFK4EEACKhZANiAATS8Pia9B3jaelvF3crFc5d6HSqqGwPzvEA +CI9TiP4a/hnqhpZEVI9kk7RWmiV7xG2PL61VHS3WtguAF/RwlFJQJ2tscWYx0qtj +cAdIi3xui8PT6mmHG/I0CbFZ4uAa6/o= +-----END EC PRIVATE KEY----- diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/ec-cert-pwd.p12 b/ydb/public/lib/ydb_cli/common/ut/test_data/ec-cert-pwd.p12 Binary files differnew file mode 100644 index 0000000000..c0c523cece --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/ec-cert-pwd.p12 diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/ec-cert.p12 b/ydb/public/lib/ydb_cli/common/ut/test_data/ec-cert.p12 Binary files differnew file mode 100644 index 0000000000..65630d09b1 --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/ec-cert.p12 diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/ec-cert.pem b/ydb/public/lib/ydb_cli/common/ut/test_data/ec-cert.pem new file mode 100644 index 0000000000..17f4b31ddd --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/ec-cert.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC1DCCAlqgAwIBAgITfoCHUO2Lyx7Ge7aitBhp8+57FTAKBggqhkjOPQQDBDBF +MQwwCgYDVQQKDANZREIxGTAXBgNVBAMMEFlEQiBUZXN0IFJvb3QgQ0ExGjAYBgkq +hkiG9w0BCQEWC3lkYkB5ZGIueWRiMB4XDTI1MDIyNzE1MzA0MFoXDTI2MDIyNzE1 +MzA0MFowJTEMMAoGA1UECgwDWURCMRUwEwYDVQQDDAxob3N0LnlkYi5uZXQwdjAQ +BgcqhkjOPQIBBgUrgQQAIgNiAATS8Pia9B3jaelvF3crFc5d6HSqqGwPzvEACI9T +iP4a/hnqhpZEVI9kk7RWmiV7xG2PL61VHS3WtguAF/RwlFJQJ2tscWYx0qtjcAdI +i3xui8PT6mmHG/I0CbFZ4uAa6/qjggEqMIIBJjAJBgNVHRMEAjAAMBEGCWCGSAGG ++EIBAQQEAwIGwDAfBglghkgBhvhCAQ0EEhYQVGVzdCBjZXJ0aWZpY2F0ZTAdBgNV +HQ4EFgQUT12c+6pblN4Hm2ZZk4WBOe7uaTgwgYAGA1UdIwR5MHeAFMm4UhwU9rl3 +UYH3NPzzFMl1OIOboUmkRzBFMQwwCgYDVQQKDANZREIxGTAXBgNVBAMMEFlEQiBU +ZXN0IFJvb3QgQ0ExGjAYBgkqhkiG9w0BCQEWC3lkYkB5ZGIueWRighRYDsITD3rH +t3W4BIPBfQDGprUiBTALBgNVHQ8EBAMCBeAwHQYDVR0lBBYwFAYIKwYBBQUHAwEG +CCsGAQUFBwMCMBcGA1UdEQQQMA6CDGhvc3QueWRiLm5ldDAKBggqhkjOPQQDBANo +ADBlAjEA2c9hW4aMJ1agvFZlyBVXp8kOrZwNl/ycCKBdJ6XsxBeqHZJH84+p9hmq +a1WwnE5dAjAsMGUiHFMZCPuFMt1ssOk1HrMgmv0pYXYPcFBElZZ58svFvog5gyhn +GZ5s8IGptTY= +-----END CERTIFICATE----- diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/ec-private-key-pkcs8.pem b/ydb/public/lib/ydb_cli/common/ut/test_data/ec-private-key-pkcs8.pem new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/ec-private-key-pkcs8.pem diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/ec-private-key-pwd.pem b/ydb/public/lib/ydb_cli/common/ut/test_data/ec-private-key-pwd.pem new file mode 100644 index 0000000000..2f2c7336b4 --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/ec-private-key-pwd.pem @@ -0,0 +1,9 @@ +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,972C596A7496261BE01ECBC6D8A5034D + +xgqlS5qHKMwyBK/8m+xKTcPNvMK+8ieY688cr+iG28x86puF8AQNGNiSJBHSkWrK +IQhAV53UhkiX1rCjYFYrri84qcwu5FJ6DARJojvhSic3W1pBsbNN0WKmFL/3XZ8K +bMM95QxqhjQDnEvXuxmmJwA+VFa7K+tjlioVaSu1oZrUKLiUzXu3hRrPnYiAU9GJ +TZFZZSseFV6Eyn3vWntOt33f9VbcHICp/HVz/K3kUhE= +-----END EC PRIVATE KEY----- diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/ec-private-key.pem b/ydb/public/lib/ydb_cli/common/ut/test_data/ec-private-key.pem new file mode 100644 index 0000000000..bb0c073564 --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/ec-private-key.pem @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDBCn8VZ3bNXU7QQV1JeKmahm1sbcy2gctHUuVG9bN28Hyh1kuLiOZDr +RntgGfEa1QGgBwYFK4EEACKhZANiAATS8Pia9B3jaelvF3crFc5d6HSqqGwPzvEA +CI9TiP4a/hnqhpZEVI9kk7RWmiV7xG2PL61VHS3WtguAF/RwlFJQJ2tscWYx0qtj +cAdIi3xui8PT6mmHG/I0CbFZ4uAa6/o= +-----END EC PRIVATE KEY----- diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/generate_certs.sh b/ydb/public/lib/ydb_cli/common/ut/test_data/generate_certs.sh new file mode 100755 index 0000000000..5082f8450c --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/generate_certs.sh @@ -0,0 +1,49 @@ +#! /usr/bin/env bash +set -e +set -x + +rm *.pem +rm *.p12 + +# Root CA +openssl ecparam -name secp384r1 -genkey -noout -out ca-private-key.pem +openssl req -x509 -new -nodes -key ca-private-key.pem -sha512 -days 1826 -subj '/O=YDB/CN=YDB Test Root CA/emailAddress=ydb@ydb.ydb' -out root-ca.pem + +# Generate EC PEM cert without password +openssl ecparam -name secp384r1 -genkey -noout -out ec-private-key.pem +openssl req -new -sha512 -nodes -key ec-private-key.pem -config cert.cfg -out ec-cert.csr +openssl x509 -req -sha512 -days 365 -in ec-cert.csr -CA root-ca.pem -CAkey ca-private-key.pem -CAcreateserial -extfile cert-ext.cfg -out ec-cert.pem + +# Generate RSA PEM cert without password +openssl genrsa -out rsa-private-key.pem 2048 +openssl req -new -sha512 -nodes -key rsa-private-key.pem -config cert.cfg -out rsa-cert.csr +openssl x509 -req -sha512 -days 365 -in rsa-cert.csr -CA root-ca.pem -CAkey ca-private-key.pem -CAcreateserial -extfile cert-ext.cfg -out rsa-cert.pem + +# Generate EC PEM key with password +openssl ec -in ec-private-key.pem -aes256 -passout 'pass:p@ssw0rd' -out ec-private-key-pwd.pem + +# Generate RSA PEM key with password +openssl rsa -in rsa-private-key.pem -aes256 -passout 'pass:p@ssw0rd' -out rsa-private-key-pwd.pem + +# Generate EC PKCS#12 cert without password +openssl pkcs12 -export -in ec-cert.pem -inkey ec-private-key.pem -name 'Test cert' -passout 'pass:' -out ec-cert.p12 + +# Generate RSA PKCS#12 cert without password +openssl pkcs12 -export -in rsa-cert.pem -inkey rsa-private-key.pem -name 'Test cert' -passout 'pass:' -out rsa-cert.p12 + +# Generate EC PKCS#12 cert with password +openssl pkcs12 -export -in ec-cert.pem -inkey ec-private-key.pem -name 'Test cert' -passout 'pass:p@ssw0rd' -out ec-cert-pwd.p12 + +# Generate RSA PKCS#12 cert with password +openssl pkcs12 -export -in rsa-cert.pem -inkey rsa-private-key.pem -name 'Test cert' -passout 'pass:p@ssw0rd' -out rsa-cert-pwd.p12 + +# Concat EC PEM cert and private key into one file +cat ec-cert.pem ec-private-key.pem > ec-all.pem +cat ec-private-key-pwd.pem ec-cert.pem > ec-all-pwd.pem + +# Concat RSA PEM cert and private key into one file +cat rsa-cert.pem rsa-private-key.pem > rsa-all.pem +cat rsa-private-key-pwd.pem rsa-cert.pem > rsa-all-pwd.pem + +# Cleanup +rm *.csr diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/root-ca.pem b/ydb/public/lib/ydb_cli/common/ut/test_data/root-ca.pem new file mode 100644 index 0000000000..be4d4a71bc --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/root-ca.pem @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICHDCCAaKgAwIBAgIUWA7CEw96x7d1uASDwX0Axqa1IgUwCgYIKoZIzj0EAwQw +RTEMMAoGA1UECgwDWURCMRkwFwYDVQQDDBBZREIgVGVzdCBSb290IENBMRowGAYJ +KoZIhvcNAQkBFgt5ZGJAeWRiLnlkYjAeFw0yNTAyMjcxNTMwNDBaFw0zMDAyMjcx +NTMwNDBaMEUxDDAKBgNVBAoMA1lEQjEZMBcGA1UEAwwQWURCIFRlc3QgUm9vdCBD +QTEaMBgGCSqGSIb3DQEJARYLeWRiQHlkYi55ZGIwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAT+vF2+8tuWAlb1+ByT9F4cygVYlOvfsiU/lW1TQwZZXV6tTWqPqCwwjJ96 +l11T62Dc4tfLlBSea6Sx6k1JvbjXMeYICdeIASQOmTF/d+V3FpC7fJ7L0RhfFr9v ++Dj1OaGjUzBRMB0GA1UdDgQWBBTJuFIcFPa5d1GB9zT88xTJdTiDmzAfBgNVHSME +GDAWgBTJuFIcFPa5d1GB9zT88xTJdTiDmzAPBgNVHRMBAf8EBTADAQH/MAoGCCqG +SM49BAMEA2gAMGUCMGKi3yp8yUAvsFEhwPzJtzRZEhmrxexO5hZY0sO0aEZ/F36n +g4MwKI77uylSp3zZ7QIxAN/b0ioIJEDMG4Z4iu6LwkMf7iM41YnH2keE+U6tt3YP +UoZPGRIcOT7pdWo0qw+zhQ== +-----END CERTIFICATE----- diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-all-pwd.pem b/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-all-pwd.pem new file mode 100644 index 0000000000..7263b191a6 --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-all-pwd.pem @@ -0,0 +1,51 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIj9j7mZP26RoCAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBCtR2G2S3gQV05HBrC2K8zJBIIE +0NHU/ROqMHHu84Td4a7FdymDQzem/fnioBuQadWLGvQMs/rbqGm8iUvMFis3M5Aq +wdHKUqO6UMKL4W+HCJtF2C/qFYkeWeAGOcddcuKbFSeOqTiiRFREpcX+MrFG9AJr +RVVmjCo3NHWBWw17plhP2fG/wUoHTnFbN9KgwV38mWF0ixe2InexQETFZu3bgIXT +VTV6+yVC++pDxhKFGmbzWu+rN3TwMAubpLy18c49BdYjmWvKdATcJ200SH9Lif4I +9FSs745hJV0qNrCjRoZCji+pc1UoW+MNoj2F+m8kJBb0i4+LhaAtEOCBtj+V4oQJ +dI38DBaPKwQS6LqczHmM4td93MyA44ubHzkkT2tMTmC5iPDIcG9599Idl5i6F0ZB +99789+pei0IPlV0Kl2Z72SxdhOadlIeE+syeO1Jon0aP3b93xiodlvo5glEHNkAJ +vmsJXaf1DReaIuDG+l8wtsKpvHQgHeIIRuTN4frned9X6XxwzvzLXWpBoL4q7M8A +HPuPPrLyCpCh2wTbp9ukdE5zVPfCExduZ6b5atk5Wgxt7P1HDNp/nwHRG7TJbge6 +Krbi6OPwyU7xY8LiOwkhec4AzNelPNgaYvF1VgQtA0XkKz75cMv1ikwF2/OVnpFI +U3QIEAvZAzxGZkbdqsGeboabtQ8vmY96QKqKBs3OIhEYq0OreA7YnQzppcCFEW3X +Nt9wA06rBZRumx2MbZQ5NzOgEYVSXBcapA/yYR8wxyLKK/LqRvZXTanw3PojbooU +zGXGxqTyYXPimSOlfgK9aRTBFsj0TGGAIbXZehTj36cNKoV0o9V912yRb7VpJIZY +bRGHAbYpvOHkjvC6pi5sRDACkyma2H53uEkvZGMOva0Xfq/iDszeUNHeFAauMDvR +OjdfXcJaUeO+PwUNyFpVZnmsQzsfU6FvfNcpiak6kQb2h2OlTXV1LQzCOTbUst6N +fVv78wY+HlF6BG+pR/yWZEJvPQ0ui51v4FY+xK0/I0+so2lk2Pj3xSq2S+eqC56X +s4CAPYzMfIZZBUhpBXeVjnMR2REjvsixoF6fghJso70aIhmbWvXGSw2LA07WRrmo +zWmhGIJ/egBSPU8hb4XG7ZiybyFgAleyUFl5laI3F94zOAixsSj6o2FoiAFeiKN9 +hZevO27Aino/Ep6+fAJAIG6oi5F/svchrLtn0Mdr0iTU/jKbY1F151Vr6incv81f +KkWheXr+NO0VaS1Qnh64lWV2rSSwQunLSZNL4SlMWxMTZykfDcAbb6B2UuNRKGnp +ryYLDfFfY2YFQjT+lG84hkYeEY5mKZ/FFO1Vu59YxHaJLMCvfAoxbwXAFUCeN4h+ +WY/gPLXJLXtqEtSg8RvCIjUmqfq72zpjuu/KxVbrEmXWLA8RE0X3UolZz+kWxpCA +JETc9S5+iexMi0ns/Fe7y8thMxHMbg5aBSU4oq50lj+u5PdYyJI+EejrWobjl07T +T5mJid9uEo4e7+HLqNz3ebMKpT87VLk/r4AoQwZ39P+4N2s7o/C6IBGpOqADHASA +ieLWLoKt/O+o4vM0Kfpme3fHg2tzv9SeX0ECf6ZF0suq/Vs8xtWhuIMEhrM/tyFt +pGLEHhTOuaRsoOy0mVeZYugqhx7v6mnzoYKWzoKMfrMS +-----END ENCRYPTED PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIDhDCCAwmgAwIBAgIUOWjP4WDFqjNgjpi9+3RyLHVgVDowCgYIKoZIzj0EAwQw +RTEMMAoGA1UECgwDWURCMRkwFwYDVQQDDBBZREIgVGVzdCBSb290IENBMRowGAYJ +KoZIhvcNAQkBFgt5ZGJAeWRiLnlkYjAeFw0yNTAyMjcxNTMwNDBaFw0yNjAyMjcx +NTMwNDBaMCUxDDAKBgNVBAoMA1lEQjEVMBMGA1UEAwwMaG9zdC55ZGIubmV0MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr0Gj5UzadnbHRckVcZI8BEFK +DZZTjdVS5KXWQ3l/oNVEW5Ay+cYDQJWeO4+rdhXqCEsLPz0c6LCmZG8mBlz0Clp5 +aPkPsXOrZW+/qnW5hHq3BMJbyBLXcJjUXNShvbO+wO/mXN+RuJktxRxq9ZFc5Dxo +23qSD2rnEsCO7+swXqzOI76x+a+euOMrWGN0vMaQBlfdkzE9FFhHNP3kUorSli5d +IjF6TS2agmQM7VyKuodl2x/f/FeobzDqrTH5bpA0d1zj9maZaKpAC7Gt1Wkl7e7/ +48t3OOYpM4yNbzWUktLuEnMhySsCWZGu3hF3zSNpKaqh2ANlSK/J7KOC6VWN+wID +AQABo4IBKjCCASYwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBsAwHwYJYIZI +AYb4QgENBBIWEFRlc3QgY2VydGlmaWNhdGUwHQYDVR0OBBYEFHmk/j4vLRcLHPYk +KGkjx141v5o9MIGABgNVHSMEeTB3gBTJuFIcFPa5d1GB9zT88xTJdTiDm6FJpEcw +RTEMMAoGA1UECgwDWURCMRkwFwYDVQQDDBBZREIgVGVzdCBSb290IENBMRowGAYJ +KoZIhvcNAQkBFgt5ZGJAeWRiLnlkYoIUWA7CEw96x7d1uASDwX0Axqa1IgUwCwYD +VR0PBAQDAgXgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAXBgNVHREE +EDAOggxob3N0LnlkYi5uZXQwCgYIKoZIzj0EAwQDaQAwZgIxAKF9+r38ij1iOdMl +be+cT4r26S4Fo8rfDMP1YlIJ9dvlJjI7lPPW72441et4QvJEogIxAPp3GldSZFvu +WFVUGSK0l2UNct5Rm5fegGKwMaunk/ZQO293JBc/AOU31FtgxcOLrg== +-----END CERTIFICATE----- diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-all.pem b/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-all.pem new file mode 100644 index 0000000000..7c346bf7ca --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-all.pem @@ -0,0 +1,49 @@ +-----BEGIN CERTIFICATE----- +MIIDhDCCAwmgAwIBAgIUOWjP4WDFqjNgjpi9+3RyLHVgVDowCgYIKoZIzj0EAwQw +RTEMMAoGA1UECgwDWURCMRkwFwYDVQQDDBBZREIgVGVzdCBSb290IENBMRowGAYJ +KoZIhvcNAQkBFgt5ZGJAeWRiLnlkYjAeFw0yNTAyMjcxNTMwNDBaFw0yNjAyMjcx +NTMwNDBaMCUxDDAKBgNVBAoMA1lEQjEVMBMGA1UEAwwMaG9zdC55ZGIubmV0MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr0Gj5UzadnbHRckVcZI8BEFK +DZZTjdVS5KXWQ3l/oNVEW5Ay+cYDQJWeO4+rdhXqCEsLPz0c6LCmZG8mBlz0Clp5 +aPkPsXOrZW+/qnW5hHq3BMJbyBLXcJjUXNShvbO+wO/mXN+RuJktxRxq9ZFc5Dxo +23qSD2rnEsCO7+swXqzOI76x+a+euOMrWGN0vMaQBlfdkzE9FFhHNP3kUorSli5d +IjF6TS2agmQM7VyKuodl2x/f/FeobzDqrTH5bpA0d1zj9maZaKpAC7Gt1Wkl7e7/ +48t3OOYpM4yNbzWUktLuEnMhySsCWZGu3hF3zSNpKaqh2ANlSK/J7KOC6VWN+wID +AQABo4IBKjCCASYwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBsAwHwYJYIZI +AYb4QgENBBIWEFRlc3QgY2VydGlmaWNhdGUwHQYDVR0OBBYEFHmk/j4vLRcLHPYk +KGkjx141v5o9MIGABgNVHSMEeTB3gBTJuFIcFPa5d1GB9zT88xTJdTiDm6FJpEcw +RTEMMAoGA1UECgwDWURCMRkwFwYDVQQDDBBZREIgVGVzdCBSb290IENBMRowGAYJ +KoZIhvcNAQkBFgt5ZGJAeWRiLnlkYoIUWA7CEw96x7d1uASDwX0Axqa1IgUwCwYD +VR0PBAQDAgXgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAXBgNVHREE +EDAOggxob3N0LnlkYi5uZXQwCgYIKoZIzj0EAwQDaQAwZgIxAKF9+r38ij1iOdMl +be+cT4r26S4Fo8rfDMP1YlIJ9dvlJjI7lPPW72441et4QvJEogIxAPp3GldSZFvu +WFVUGSK0l2UNct5Rm5fegGKwMaunk/ZQO293JBc/AOU31FtgxcOLrg== +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCvQaPlTNp2dsdF +yRVxkjwEQUoNllON1VLkpdZDeX+g1URbkDL5xgNAlZ47j6t2FeoISws/PRzosKZk +byYGXPQKWnlo+Q+xc6tlb7+qdbmEercEwlvIEtdwmNRc1KG9s77A7+Zc35G4mS3F +HGr1kVzkPGjbepIPaucSwI7v6zBerM4jvrH5r5644ytYY3S8xpAGV92TMT0UWEc0 +/eRSitKWLl0iMXpNLZqCZAztXIq6h2XbH9/8V6hvMOqtMflukDR3XOP2ZploqkAL +sa3VaSXt7v/jy3c45ikzjI1vNZSS0u4ScyHJKwJZka7eEXfNI2kpqqHYA2VIr8ns +o4LpVY37AgMBAAECggEAFN3Q4M1MuYwc3884UWRi5vLLGWELo7iBfR3NrAz8SC78 +S5aYqyqLWpY118ucU5v6WHBu72GcXowh05V0Ro+sssrg58G3v54RCyvJ+0a3BvIB +dnyZpvGvWwWnS0I527dZ/+jQWcB4vizmyTA//+sFz1rt9Svb1iAZUR9CJYiwIqVn +xyDYbL3TzLacSs5iKzcbW4i6OeLphgAYiNsY1oOu4yvRUYgZP31oCNs7Mye/muiX +rB30mF/ZSFJmXHSKEwegtmvc9yZ+HBWmV0GPySwjs+K0FEw1XexH9U8kliOvGexG +fvqMXEDWe0oPYNbAOh8NHtRrr9nzARzDS0omlHqgKQKBgQDtHDL6dRZAkM2CBjwR +ZgbMXGY+GJK6ElU9eqXK1vBud/AV286N1OVQ8wlazTHI/gEWzDtsenFqt2hhRX13 +sMIW2qmmtgOb4PdZdqe3+CMa97BoEziedbgoWFFLp+4NiYZzSli/lSF+OTMzdMsU +wMD1vb7lyytAs1a+MhvgHO4iBQKBgQC9N/MkWvUlgy2+Ak9hti2egRhggCBaGWqY +UAadYiCHhctvSgOwixe25mrvEe4aVDjMKglm/y2cCPXjPOThjfsi9UuehscDWdde +f4UnFb0KiNmDV7MsaxG6EgRwOaOLtTuJeKQRNQPQ9VcUEdZxt7snzaNHHSlprL9c +PMueQBPv/wKBgBhd8IM6qynBd80n9N5Y3NP9nug8wD9tCOODiiw5QIYpvzuP1j9P +JK3X/BsfwUEFkXkVTfoM70DnTkvIx2cYfCm7GPov9Fj9mo3QGtZWIs1vrOpVJ1lp +gZ5rzRb4UAeGHZIVjt9JZSLCoBdmpkQgtvPKJycYZP6GL6DmJ2U1s+c9AoGBAIp5 +2c0va90qJV27HxEpXDV10Ls+yW5mz2Xsmwqu95N2zS0DA7Q99vr5oiSYAKLwJCj2 +Uq837M8Wl6zXscGIQNSSo+a+SAMhysXzmSTDefets1G16wCE0xJTgUAITrI9zfaL +fbbCD6rrAfFEJKZQif1VNzsiEl6t99WvAG0uA+lNAoGBAORDn6JlCSXA5nramwuW +DgI0mjk1lOomn0yTMhv5xkFlW6Mm8oi/AIPmn0aibCluF2WmYv8tCsM+/1lzGW1W +YNgfvkBTzAIGuqSWFJkUPJYkIXq/YeA2B7m2RfKizu8Jh/u89p9zytpSggt4wEmv +8vhtPgyKb+J7Ao4ALzITvLKG +-----END PRIVATE KEY----- diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-cert-pwd.p12 b/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-cert-pwd.p12 Binary files differnew file mode 100644 index 0000000000..f6615d9a16 --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-cert-pwd.p12 diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-cert.p12 b/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-cert.p12 Binary files differnew file mode 100644 index 0000000000..a1ddb2f915 --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-cert.p12 diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-cert.pem b/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-cert.pem new file mode 100644 index 0000000000..3c9ecf3a9f --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhDCCAwmgAwIBAgIUOWjP4WDFqjNgjpi9+3RyLHVgVDowCgYIKoZIzj0EAwQw +RTEMMAoGA1UECgwDWURCMRkwFwYDVQQDDBBZREIgVGVzdCBSb290IENBMRowGAYJ +KoZIhvcNAQkBFgt5ZGJAeWRiLnlkYjAeFw0yNTAyMjcxNTMwNDBaFw0yNjAyMjcx +NTMwNDBaMCUxDDAKBgNVBAoMA1lEQjEVMBMGA1UEAwwMaG9zdC55ZGIubmV0MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr0Gj5UzadnbHRckVcZI8BEFK +DZZTjdVS5KXWQ3l/oNVEW5Ay+cYDQJWeO4+rdhXqCEsLPz0c6LCmZG8mBlz0Clp5 +aPkPsXOrZW+/qnW5hHq3BMJbyBLXcJjUXNShvbO+wO/mXN+RuJktxRxq9ZFc5Dxo +23qSD2rnEsCO7+swXqzOI76x+a+euOMrWGN0vMaQBlfdkzE9FFhHNP3kUorSli5d +IjF6TS2agmQM7VyKuodl2x/f/FeobzDqrTH5bpA0d1zj9maZaKpAC7Gt1Wkl7e7/ +48t3OOYpM4yNbzWUktLuEnMhySsCWZGu3hF3zSNpKaqh2ANlSK/J7KOC6VWN+wID +AQABo4IBKjCCASYwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBsAwHwYJYIZI +AYb4QgENBBIWEFRlc3QgY2VydGlmaWNhdGUwHQYDVR0OBBYEFHmk/j4vLRcLHPYk +KGkjx141v5o9MIGABgNVHSMEeTB3gBTJuFIcFPa5d1GB9zT88xTJdTiDm6FJpEcw +RTEMMAoGA1UECgwDWURCMRkwFwYDVQQDDBBZREIgVGVzdCBSb290IENBMRowGAYJ +KoZIhvcNAQkBFgt5ZGJAeWRiLnlkYoIUWA7CEw96x7d1uASDwX0Axqa1IgUwCwYD +VR0PBAQDAgXgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAXBgNVHREE +EDAOggxob3N0LnlkYi5uZXQwCgYIKoZIzj0EAwQDaQAwZgIxAKF9+r38ij1iOdMl +be+cT4r26S4Fo8rfDMP1YlIJ9dvlJjI7lPPW72441et4QvJEogIxAPp3GldSZFvu +WFVUGSK0l2UNct5Rm5fegGKwMaunk/ZQO293JBc/AOU31FtgxcOLrg== +-----END CERTIFICATE----- diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-private-key-pwd.pem b/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-private-key-pwd.pem new file mode 100644 index 0000000000..5b012f7b94 --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-private-key-pwd.pem @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIj9j7mZP26RoCAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBCtR2G2S3gQV05HBrC2K8zJBIIE +0NHU/ROqMHHu84Td4a7FdymDQzem/fnioBuQadWLGvQMs/rbqGm8iUvMFis3M5Aq +wdHKUqO6UMKL4W+HCJtF2C/qFYkeWeAGOcddcuKbFSeOqTiiRFREpcX+MrFG9AJr +RVVmjCo3NHWBWw17plhP2fG/wUoHTnFbN9KgwV38mWF0ixe2InexQETFZu3bgIXT +VTV6+yVC++pDxhKFGmbzWu+rN3TwMAubpLy18c49BdYjmWvKdATcJ200SH9Lif4I +9FSs745hJV0qNrCjRoZCji+pc1UoW+MNoj2F+m8kJBb0i4+LhaAtEOCBtj+V4oQJ +dI38DBaPKwQS6LqczHmM4td93MyA44ubHzkkT2tMTmC5iPDIcG9599Idl5i6F0ZB +99789+pei0IPlV0Kl2Z72SxdhOadlIeE+syeO1Jon0aP3b93xiodlvo5glEHNkAJ +vmsJXaf1DReaIuDG+l8wtsKpvHQgHeIIRuTN4frned9X6XxwzvzLXWpBoL4q7M8A +HPuPPrLyCpCh2wTbp9ukdE5zVPfCExduZ6b5atk5Wgxt7P1HDNp/nwHRG7TJbge6 +Krbi6OPwyU7xY8LiOwkhec4AzNelPNgaYvF1VgQtA0XkKz75cMv1ikwF2/OVnpFI +U3QIEAvZAzxGZkbdqsGeboabtQ8vmY96QKqKBs3OIhEYq0OreA7YnQzppcCFEW3X +Nt9wA06rBZRumx2MbZQ5NzOgEYVSXBcapA/yYR8wxyLKK/LqRvZXTanw3PojbooU +zGXGxqTyYXPimSOlfgK9aRTBFsj0TGGAIbXZehTj36cNKoV0o9V912yRb7VpJIZY +bRGHAbYpvOHkjvC6pi5sRDACkyma2H53uEkvZGMOva0Xfq/iDszeUNHeFAauMDvR +OjdfXcJaUeO+PwUNyFpVZnmsQzsfU6FvfNcpiak6kQb2h2OlTXV1LQzCOTbUst6N +fVv78wY+HlF6BG+pR/yWZEJvPQ0ui51v4FY+xK0/I0+so2lk2Pj3xSq2S+eqC56X +s4CAPYzMfIZZBUhpBXeVjnMR2REjvsixoF6fghJso70aIhmbWvXGSw2LA07WRrmo +zWmhGIJ/egBSPU8hb4XG7ZiybyFgAleyUFl5laI3F94zOAixsSj6o2FoiAFeiKN9 +hZevO27Aino/Ep6+fAJAIG6oi5F/svchrLtn0Mdr0iTU/jKbY1F151Vr6incv81f +KkWheXr+NO0VaS1Qnh64lWV2rSSwQunLSZNL4SlMWxMTZykfDcAbb6B2UuNRKGnp +ryYLDfFfY2YFQjT+lG84hkYeEY5mKZ/FFO1Vu59YxHaJLMCvfAoxbwXAFUCeN4h+ +WY/gPLXJLXtqEtSg8RvCIjUmqfq72zpjuu/KxVbrEmXWLA8RE0X3UolZz+kWxpCA +JETc9S5+iexMi0ns/Fe7y8thMxHMbg5aBSU4oq50lj+u5PdYyJI+EejrWobjl07T +T5mJid9uEo4e7+HLqNz3ebMKpT87VLk/r4AoQwZ39P+4N2s7o/C6IBGpOqADHASA +ieLWLoKt/O+o4vM0Kfpme3fHg2tzv9SeX0ECf6ZF0suq/Vs8xtWhuIMEhrM/tyFt +pGLEHhTOuaRsoOy0mVeZYugqhx7v6mnzoYKWzoKMfrMS +-----END ENCRYPTED PRIVATE KEY----- diff --git a/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-private-key.pem b/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-private-key.pem new file mode 100644 index 0000000000..99909c5804 --- /dev/null +++ b/ydb/public/lib/ydb_cli/common/ut/test_data/rsa-private-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCvQaPlTNp2dsdF +yRVxkjwEQUoNllON1VLkpdZDeX+g1URbkDL5xgNAlZ47j6t2FeoISws/PRzosKZk +byYGXPQKWnlo+Q+xc6tlb7+qdbmEercEwlvIEtdwmNRc1KG9s77A7+Zc35G4mS3F +HGr1kVzkPGjbepIPaucSwI7v6zBerM4jvrH5r5644ytYY3S8xpAGV92TMT0UWEc0 +/eRSitKWLl0iMXpNLZqCZAztXIq6h2XbH9/8V6hvMOqtMflukDR3XOP2ZploqkAL +sa3VaSXt7v/jy3c45ikzjI1vNZSS0u4ScyHJKwJZka7eEXfNI2kpqqHYA2VIr8ns +o4LpVY37AgMBAAECggEAFN3Q4M1MuYwc3884UWRi5vLLGWELo7iBfR3NrAz8SC78 +S5aYqyqLWpY118ucU5v6WHBu72GcXowh05V0Ro+sssrg58G3v54RCyvJ+0a3BvIB +dnyZpvGvWwWnS0I527dZ/+jQWcB4vizmyTA//+sFz1rt9Svb1iAZUR9CJYiwIqVn +xyDYbL3TzLacSs5iKzcbW4i6OeLphgAYiNsY1oOu4yvRUYgZP31oCNs7Mye/muiX +rB30mF/ZSFJmXHSKEwegtmvc9yZ+HBWmV0GPySwjs+K0FEw1XexH9U8kliOvGexG +fvqMXEDWe0oPYNbAOh8NHtRrr9nzARzDS0omlHqgKQKBgQDtHDL6dRZAkM2CBjwR +ZgbMXGY+GJK6ElU9eqXK1vBud/AV286N1OVQ8wlazTHI/gEWzDtsenFqt2hhRX13 +sMIW2qmmtgOb4PdZdqe3+CMa97BoEziedbgoWFFLp+4NiYZzSli/lSF+OTMzdMsU +wMD1vb7lyytAs1a+MhvgHO4iBQKBgQC9N/MkWvUlgy2+Ak9hti2egRhggCBaGWqY +UAadYiCHhctvSgOwixe25mrvEe4aVDjMKglm/y2cCPXjPOThjfsi9UuehscDWdde +f4UnFb0KiNmDV7MsaxG6EgRwOaOLtTuJeKQRNQPQ9VcUEdZxt7snzaNHHSlprL9c +PMueQBPv/wKBgBhd8IM6qynBd80n9N5Y3NP9nug8wD9tCOODiiw5QIYpvzuP1j9P +JK3X/BsfwUEFkXkVTfoM70DnTkvIx2cYfCm7GPov9Fj9mo3QGtZWIs1vrOpVJ1lp +gZ5rzRb4UAeGHZIVjt9JZSLCoBdmpkQgtvPKJycYZP6GL6DmJ2U1s+c9AoGBAIp5 +2c0va90qJV27HxEpXDV10Ls+yW5mz2Xsmwqu95N2zS0DA7Q99vr5oiSYAKLwJCj2 +Uq837M8Wl6zXscGIQNSSo+a+SAMhysXzmSTDefets1G16wCE0xJTgUAITrI9zfaL +fbbCD6rrAfFEJKZQif1VNzsiEl6t99WvAG0uA+lNAoGBAORDn6JlCSXA5nramwuW +DgI0mjk1lOomn0yTMhv5xkFlW6Mm8oi/AIPmn0aibCluF2WmYv8tCsM+/1lzGW1W +YNgfvkBTzAIGuqSWFJkUPJYkIXq/YeA2B7m2RfKizu8Jh/u89p9zytpSggt4wEmv +8vhtPgyKb+J7Ao4ALzITvLKG +-----END PRIVATE KEY----- diff --git a/ydb/public/lib/ydb_cli/common/ut/ya.make b/ydb/public/lib/ydb_cli/common/ut/ya.make index a77a975080..f73e26a026 100644 --- a/ydb/public/lib/ydb_cli/common/ut/ya.make +++ b/ydb/public/lib/ydb_cli/common/ut/ya.make @@ -1,6 +1,7 @@ UNITTEST_FOR(ydb/public/lib/ydb_cli/common) SRCS( + cert_format_converter_ut.cpp normalize_path_ut.cpp csv_parser_ut.cpp pg_dump_parser_ut.cpp diff --git a/ydb/public/lib/ydb_cli/common/ya.make b/ydb/public/lib/ydb_cli/common/ya.make index c0d94cc3e4..d8067dc57c 100644 --- a/ydb/public/lib/ydb_cli/common/ya.make +++ b/ydb/public/lib/ydb_cli/common/ya.make @@ -2,6 +2,7 @@ LIBRARY(common) SRCS( aws.cpp + cert_format_converter.cpp command.cpp command_utils.cpp common.cpp @@ -36,6 +37,7 @@ SRCS( PEERDIR( contrib/libs/aws-sdk-cpp/aws-cpp-sdk-s3 + contrib/libs/openssl library/cpp/config library/cpp/getopt library/cpp/json/writer diff --git a/ydb/public/sdk/cpp/client/ydb_driver/driver.h b/ydb/public/sdk/cpp/client/ydb_driver/driver.h index fd8bb8c95a..0cef2d1db4 100644 --- a/ydb/public/sdk/cpp/client/ydb_driver/driver.h +++ b/ydb/public/sdk/cpp/client/ydb_driver/driver.h @@ -50,7 +50,7 @@ public: //! default: 0 TDriverConfig& SetMaxClientQueueSize(size_t sz); //! Enable Ssl. - //! caCerts - The buffer containing the PEM encoding of the server root certificates. + //! caCerts - The buffer containing the PEM encoded root certificates for SSL/TLS connections. //! If this parameter is empty, the default roots will be used. TDriverConfig& UseSecureConnection(const TStringType& caCerts = TStringType()); TDriverConfig& UseClientCertificate(const TStringType& clientCert, const TStringType& clientPrivateKey); diff --git a/ydb/public/sdk/cpp/include/ydb-cpp-sdk/client/driver/driver.h b/ydb/public/sdk/cpp/include/ydb-cpp-sdk/client/driver/driver.h index 9aad0ac0a2..c934c422cb 100644 --- a/ydb/public/sdk/cpp/include/ydb-cpp-sdk/client/driver/driver.h +++ b/ydb/public/sdk/cpp/include/ydb-cpp-sdk/client/driver/driver.h @@ -47,7 +47,7 @@ public: //! default: 0 TDriverConfig& SetMaxClientQueueSize(size_t sz); //! Enable Ssl. - //! caCerts - The buffer containing the PEM encoding of the server root certificates. + //! caCerts - The buffer containing the PEM encoded root certificates for SSL/TLS connections. //! If this parameter is empty, the default roots will be used. TDriverConfig& UseSecureConnection(const std::string& caCerts = std::string()); TDriverConfig& UseClientCertificate(const std::string& clientCert, const std::string& clientPrivateKey); |