You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by pe...@apache.org on 2022/08/05 09:22:44 UTC

[pulsar] branch branch-2.11 updated (146484ed8c2 -> d82d629f326)

This is an automated email from the ASF dual-hosted git repository.

penghui pushed a change to branch branch-2.11
in repository https://gitbox.apache.org/repos/asf/pulsar.git


    from 146484ed8c2 [improve][proxy] Add TLS transport encryption for broker client (#16833)
     new 024a5e59447 [improve][cli] Add a separate TLS transport encryption configuration (#16930)
     new d82d629f326 [improve][cpp-client] Add basic authentication (#15822)

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 conf/client.conf                                   |  15 +++
 pulsar-client-cpp/include/pulsar/Authentication.h  |  46 +++++++
 .../include/pulsar/c/authentication.h              |   3 +
 pulsar-client-cpp/lib/Authentication.cc            |   7 ++
 pulsar-client-cpp/lib/auth/AuthBasic.cc            | 110 ++++++++++++++++
 .../lib/auth/{AuthToken.h => AuthBasic.h}          |  14 +--
 pulsar-client-cpp/pulsar-test-service-start.sh     |   4 +
 pulsar-client-cpp/test-conf/.htpasswd              |   1 +
 pulsar-client-cpp/test-conf/standalone-ssl.conf    |   2 +-
 pulsar-client-cpp/tests/AuthBasicTest.cc           | 140 +++++++++++++++++++++
 .../apache/pulsar/admin/cli/PulsarAdminTool.java   |  13 +-
 .../apache/pulsar/client/cli/PulsarClientTool.java |  21 +++-
 12 files changed, 365 insertions(+), 11 deletions(-)
 create mode 100644 pulsar-client-cpp/lib/auth/AuthBasic.cc
 copy pulsar-client-cpp/lib/auth/{AuthToken.h => AuthBasic.h} (74%)
 create mode 100644 pulsar-client-cpp/test-conf/.htpasswd
 create mode 100644 pulsar-client-cpp/tests/AuthBasicTest.cc


[pulsar] 02/02: [improve][cpp-client] Add basic authentication (#15822)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

penghui pushed a commit to branch branch-2.11
in repository https://gitbox.apache.org/repos/asf/pulsar.git

commit d82d629f326074427da5c680b144c6e011616a1c
Author: Zixuan Liu <no...@gmail.com>
AuthorDate: Fri Aug 5 12:08:42 2022 +0800

    [improve][cpp-client] Add basic authentication (#15822)
    
    * [improve][cpp-client] Add basic authentication
    
    Signed-off-by: Zixuan Liu <no...@gmail.com>
    
    * Fix test
    
    Signed-off-by: Zixuan Liu <no...@gmail.com>
    (cherry picked from commit 4840a98e247a1c75b27dd8ac12162c974bfbd777)
---
 pulsar-client-cpp/include/pulsar/Authentication.h  |  46 +++++++
 .../include/pulsar/c/authentication.h              |   3 +
 pulsar-client-cpp/lib/Authentication.cc            |   7 ++
 pulsar-client-cpp/lib/auth/AuthBasic.cc            | 110 ++++++++++++++++
 pulsar-client-cpp/lib/auth/AuthBasic.h             |  46 +++++++
 pulsar-client-cpp/pulsar-test-service-start.sh     |   4 +
 pulsar-client-cpp/test-conf/.htpasswd              |   1 +
 pulsar-client-cpp/test-conf/standalone-ssl.conf    |   2 +-
 pulsar-client-cpp/tests/AuthBasicTest.cc           | 140 +++++++++++++++++++++
 9 files changed, 358 insertions(+), 1 deletion(-)

diff --git a/pulsar-client-cpp/include/pulsar/Authentication.h b/pulsar-client-cpp/include/pulsar/Authentication.h
index 185ac335c80..7ab1e65a621 100644
--- a/pulsar-client-cpp/include/pulsar/Authentication.h
+++ b/pulsar-client-cpp/include/pulsar/Authentication.h
@@ -293,6 +293,52 @@ class PULSAR_PUBLIC AuthToken : public Authentication {
     AuthenticationDataPtr authDataToken_;
 };
 
+/**
+ * Basic based implementation of Pulsar client authentication
+ */
+class PULSAR_PUBLIC AuthBasic : public Authentication {
+   public:
+    explicit AuthBasic(AuthenticationDataPtr&);
+    ~AuthBasic() override;
+
+    /**
+     * Create an AuthBasic with a ParamMap
+     *
+     * It is equal to create(params[“username”], params[“password”])
+     * @see create(const std::string&, const std::string&)
+     */
+    static AuthenticationPtr create(ParamMap& params);
+
+    /**
+     * Create an AuthBasic with an authentication parameter string
+     *
+     * @param authParamsString the JSON format string: {"username": "admin", "password": "123456"}
+     */
+    static AuthenticationPtr create(const std::string& authParamsString);
+
+    /**
+     * Create an AuthBasic with the required parameters
+     */
+    static AuthenticationPtr create(const std::string& username, const std::string& password);
+
+    /**
+     * @return “basic”
+     */
+    const std::string getAuthMethodName() const override;
+
+    /**
+     * Get AuthenticationData from the current instance
+     *
+     * @param[out] authDataBasic the shared pointer of AuthenticationData. The content of AuthenticationData
+     * is changed to the internal data of the current instance.
+     * @return ResultOk
+     */
+    Result getAuthData(AuthenticationDataPtr& authDataBasic) override;
+
+   private:
+    AuthenticationDataPtr authDataBasic_;
+};
+
 /**
  * Athenz implementation of Pulsar client authentication
  */
diff --git a/pulsar-client-cpp/include/pulsar/c/authentication.h b/pulsar-client-cpp/include/pulsar/c/authentication.h
index 20247263031..9712e7158a6 100644
--- a/pulsar-client-cpp/include/pulsar/c/authentication.h
+++ b/pulsar-client-cpp/include/pulsar/c/authentication.h
@@ -39,6 +39,9 @@ PULSAR_PUBLIC pulsar_authentication_t *pulsar_authentication_token_create(const
 PULSAR_PUBLIC pulsar_authentication_t *pulsar_authentication_token_create_with_supplier(
     token_supplier tokenSupplier, void *ctx);
 
+PULSAR_PUBLIC pulsar_authentication_t *pulsar_authentication_basic_create(const char *username,
+                                                                          const char *password);
+
 PULSAR_PUBLIC pulsar_authentication_t *pulsar_authentication_athenz_create(const char *authParamsString);
 
 PULSAR_PUBLIC pulsar_authentication_t *pulsar_authentication_oauth2_create(const char *authParamsString);
diff --git a/pulsar-client-cpp/lib/Authentication.cc b/pulsar-client-cpp/lib/Authentication.cc
index 105d1c37d2b..8fc007dba0b 100644
--- a/pulsar-client-cpp/lib/Authentication.cc
+++ b/pulsar-client-cpp/lib/Authentication.cc
@@ -23,6 +23,7 @@
 #include "auth/AuthAthenz.h"
 #include "auth/AuthToken.h"
 #include "auth/AuthOauth2.h"
+#include "auth/AuthBasic.h"
 #include <lib/LogUtils.h>
 
 #include <string>
@@ -129,6 +130,9 @@ AuthenticationPtr tryCreateBuiltinAuth(const std::string& pluginName, ParamMap&
     } else if (boost::iequals(pluginName, OAUTH2_TOKEN_PLUGIN_NAME) ||
                boost::iequals(pluginName, OAUTH2_TOKEN_JAVA_PLUGIN_NAME)) {
         return AuthOauth2::create(paramMap);
+    } else if (boost::iequals(pluginName, BASIC_PLUGIN_NAME) ||
+               boost::iequals(pluginName, BASIC_JAVA_PLUGIN_NAME)) {
+        return AuthBasic::create(paramMap);
     } else {
         return AuthenticationPtr();
     }
@@ -146,6 +150,9 @@ AuthenticationPtr tryCreateBuiltinAuth(const std::string& pluginName, const std:
     } else if (boost::iequals(pluginName, OAUTH2_TOKEN_PLUGIN_NAME) ||
                boost::iequals(pluginName, OAUTH2_TOKEN_JAVA_PLUGIN_NAME)) {
         return AuthOauth2::create(authParamsString);
+    } else if (boost::iequals(pluginName, BASIC_PLUGIN_NAME) ||
+               boost::iequals(pluginName, BASIC_JAVA_PLUGIN_NAME)) {
+        return AuthBasic::create(authParamsString);
     } else {
         return AuthenticationPtr();
     }
diff --git a/pulsar-client-cpp/lib/auth/AuthBasic.cc b/pulsar-client-cpp/lib/auth/AuthBasic.cc
new file mode 100644
index 00000000000..463e1474ce9
--- /dev/null
+++ b/pulsar-client-cpp/lib/auth/AuthBasic.cc
@@ -0,0 +1,110 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "AuthBasic.h"
+
+#include <stdexcept>
+#include <boost/archive/iterators/base64_from_binary.hpp>
+#include <boost/archive/iterators/transform_width.hpp>
+#include <boost/property_tree/json_parser.hpp>
+#include <boost/property_tree/ptree.hpp>
+namespace ptree = boost::property_tree;
+
+#include <sstream>
+#include <functional>
+
+namespace pulsar {
+
+std::string base64_encode(const std::string& s) {
+    using namespace boost::archive::iterators;
+    using It = base64_from_binary<transform_width<std::string::const_iterator, 6, 8>>;
+    auto data = std::string(It(std::begin(s)), It(std::end(s)));
+    return data.append((3 - s.size() % 3) % 3, '=');
+}
+
+AuthDataBasic::AuthDataBasic(const std::string& username, const std::string& password) {
+    commandAuthToken_ = username + ":" + password;
+    httpAuthToken_ = base64_encode(commandAuthToken_);
+}
+
+AuthDataBasic::~AuthDataBasic() {}
+
+bool AuthDataBasic::hasDataForHttp() { return true; }
+
+std::string AuthDataBasic::getHttpHeaders() { return "Authorization: Basic " + httpAuthToken_; }
+
+bool AuthDataBasic::hasDataFromCommand() { return true; }
+
+std::string AuthDataBasic::getCommandData() { return commandAuthToken_; }
+
+// AuthBasic
+
+AuthBasic::AuthBasic(AuthenticationDataPtr& authDataBasic) { authDataBasic_ = authDataBasic; }
+
+AuthBasic::~AuthBasic() = default;
+
+AuthenticationPtr AuthBasic::create(const std::string& username, const std::string& password) {
+    AuthenticationDataPtr authDataBasic = AuthenticationDataPtr(new AuthDataBasic(username, password));
+    return AuthenticationPtr(new AuthBasic(authDataBasic));
+}
+
+ParamMap parseBasicAuthParamsString(const std::string& authParamsString) {
+    ParamMap params;
+    if (!authParamsString.empty()) {
+        ptree::ptree root;
+        std::stringstream stream;
+        stream << authParamsString;
+        try {
+            ptree::read_json(stream, root);
+            for (const auto& item : root) {
+                params[item.first] = item.second.get_value<std::string>();
+            }
+        } catch (ptree::json_parser_error& e) {
+            throw std::runtime_error(e.message());
+        }
+    }
+    return params;
+}
+
+AuthenticationPtr AuthBasic::create(const std::string& authParamsString) {
+    ParamMap paramMap = parseBasicAuthParamsString(authParamsString);
+    return create(paramMap);
+}
+
+AuthenticationPtr AuthBasic::create(ParamMap& params) {
+    auto usernameIt = params.find("username");
+    if (usernameIt == params.end()) {
+        throw std::runtime_error("No username provided for basic provider");
+    }
+    auto passwordIt = params.find("password");
+    if (passwordIt == params.end()) {
+        throw std::runtime_error("No password provided for basic provider");
+    }
+
+    return create(usernameIt->second, passwordIt->second);
+}
+
+const std::string AuthBasic::getAuthMethodName() const { return "basic"; }
+
+Result AuthBasic::getAuthData(AuthenticationDataPtr& authDataBasic) {
+    authDataBasic = authDataBasic_;
+    return ResultOk;
+}
+
+}  // namespace pulsar
diff --git a/pulsar-client-cpp/lib/auth/AuthBasic.h b/pulsar-client-cpp/lib/auth/AuthBasic.h
new file mode 100644
index 00000000000..89b995afa81
--- /dev/null
+++ b/pulsar-client-cpp/lib/auth/AuthBasic.h
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include <pulsar/Authentication.h>
+#include <string>
+#include <boost/function.hpp>
+
+namespace pulsar {
+
+const std::string BASIC_PLUGIN_NAME = "basic";
+const std::string BASIC_JAVA_PLUGIN_NAME = "org.apache.pulsar.client.impl.auth.AuthenticationBasic";
+
+class AuthDataBasic : public AuthenticationDataProvider {
+   public:
+    AuthDataBasic(const std::string& username, const std::string& password);
+    ~AuthDataBasic();
+
+    bool hasDataForHttp();
+    std::string getHttpHeaders();
+    bool hasDataFromCommand();
+    std::string getCommandData();
+
+   private:
+    std::string commandAuthToken_;
+    std::string httpAuthToken_;
+};
+
+}  // namespace pulsar
diff --git a/pulsar-client-cpp/pulsar-test-service-start.sh b/pulsar-client-cpp/pulsar-test-service-start.sh
index 48edd6ed9ae..63915cfc311 100755
--- a/pulsar-client-cpp/pulsar-test-service-start.sh
+++ b/pulsar-client-cpp/pulsar-test-service-start.sh
@@ -40,6 +40,10 @@ DATA_DIR=/tmp/pulsar-test-data
 rm -rf $DATA_DIR
 mkdir -p $DATA_DIR
 
+# Set up basic authentication
+cp $SRC_DIR/pulsar-client-cpp/test-conf/.htpasswd $DATA_DIR/.htpasswd
+export PULSAR_EXTRA_OPTS=-Dpulsar.auth.basic.conf=$DATA_DIR/.htpasswd
+
 # Copy TLS test certificates
 mkdir -p $DATA_DIR/certs
 cp $SRC_DIR/pulsar-broker/src/test/resources/authentication/tls/*.pem $DATA_DIR/certs
diff --git a/pulsar-client-cpp/test-conf/.htpasswd b/pulsar-client-cpp/test-conf/.htpasswd
new file mode 100644
index 00000000000..2aa3a4772ab
--- /dev/null
+++ b/pulsar-client-cpp/test-conf/.htpasswd
@@ -0,0 +1 @@
+admin:$apr1$FG4AO6aX$KGYPuMoLUou3i6vUkPUUf.
diff --git a/pulsar-client-cpp/test-conf/standalone-ssl.conf b/pulsar-client-cpp/test-conf/standalone-ssl.conf
index 7c7eeb4a74c..9d0da557493 100644
--- a/pulsar-client-cpp/test-conf/standalone-ssl.conf
+++ b/pulsar-client-cpp/test-conf/standalone-ssl.conf
@@ -97,7 +97,7 @@ anonymousUserRole=anonymous
 authenticationEnabled=true
 
 # Authentication provider name list, which is comma separated list of class names
-authenticationProviders=org.apache.pulsar.broker.authentication.AuthenticationProviderTls,org.apache.pulsar.broker.authentication.AuthenticationProviderToken
+authenticationProviders=org.apache.pulsar.broker.authentication.AuthenticationProviderTls,org.apache.pulsar.broker.authentication.AuthenticationProviderToken,org.apache.pulsar.broker.authentication.AuthenticationProviderBasic
 
 # Enforce authorization
 authorizationEnabled=true
diff --git a/pulsar-client-cpp/tests/AuthBasicTest.cc b/pulsar-client-cpp/tests/AuthBasicTest.cc
new file mode 100644
index 00000000000..296eff3cd57
--- /dev/null
+++ b/pulsar-client-cpp/tests/AuthBasicTest.cc
@@ -0,0 +1,140 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <pulsar/Authentication.h>
+
+#include <gtest/gtest.h>
+#include <pulsar/Client.h>
+
+#include <string>
+
+using namespace pulsar;
+
+static const std::string serviceUrl = "pulsar://localhost:6650";
+static const std::string serviceUrlHttp = "http://localhost:8080";
+
+TEST(AuthPluginBasic, testBasic) {
+    ClientConfiguration config = ClientConfiguration();
+    AuthenticationPtr auth = pulsar::AuthBasic::create("admin", "123456");
+
+    ASSERT_TRUE(auth != NULL);
+    ASSERT_EQ(auth->getAuthMethodName(), "basic");
+
+    pulsar::AuthenticationDataPtr data;
+    ASSERT_EQ(auth->getAuthData(data), pulsar::ResultOk);
+    ASSERT_EQ(data->hasDataFromCommand(), true);
+    ASSERT_EQ(data->getCommandData(), "admin:123456");
+    ASSERT_EQ(data->hasDataForTls(), false);
+    ASSERT_EQ(data->hasDataForHttp(), true);
+    ASSERT_EQ(auth.use_count(), 1);
+
+    config.setAuth(auth);
+    Client client(serviceUrl, config);
+
+    std::string topicName = "persistent://private/auth/test-basic";
+    std::string subName = "subscription-name";
+
+    Producer producer;
+    Result result = client.createProducer(topicName, producer);
+    ASSERT_EQ(ResultOk, result);
+    producer.close();
+}
+
+TEST(AuthPluginBasic, testBasicWithHttp) {
+    ClientConfiguration config = ClientConfiguration();
+    AuthenticationPtr auth = pulsar::AuthBasic::create("admin", "123456");
+
+    ASSERT_TRUE(auth != NULL);
+    ASSERT_EQ(auth->getAuthMethodName(), "basic");
+
+    pulsar::AuthenticationDataPtr data;
+    ASSERT_EQ(auth->getAuthData(data), pulsar::ResultOk);
+    ASSERT_EQ(data->hasDataFromCommand(), true);
+    ASSERT_EQ(data->getCommandData(), "admin:123456");
+    ASSERT_EQ(data->hasDataForTls(), false);
+    ASSERT_EQ(data->hasDataForHttp(), true);
+
+    config.setAuth(auth);
+    Client client(serviceUrlHttp, config);
+
+    std::string topicName = "persistent://private/auth/test-basic";
+    std::string subName = "subscription-name";
+
+    Producer producer;
+    Result result = client.createProducer(topicName, producer);
+    ASSERT_EQ(ResultOk, result);
+    producer.close();
+}
+
+TEST(AuthPluginBasic, testNoAuth) {
+    ClientConfiguration config = ClientConfiguration();
+    Client client(serviceUrl, config);
+
+    std::string topicName = "persistent://private/auth/test-basic";
+    std::string subName = "subscription-name";
+
+    Producer producer;
+    Result result = client.createProducer(topicName, producer);
+    ASSERT_EQ(ResultAuthorizationError, result);
+}
+
+TEST(AuthPluginBasic, testNoAuthWithHttp) {
+    ClientConfiguration config = ClientConfiguration();
+    Client client(serviceUrlHttp, config);
+
+    std::string topicName = "persistent://private/auth/test-basic";
+    std::string subName = "subscription-name";
+
+    Producer producer;
+    Result result = client.createProducer(topicName, producer);
+    ASSERT_EQ(ResultConnectError, result);
+}
+
+TEST(AuthPluginBasic, testLoadAuth) {
+    AuthenticationPtr auth = pulsar::AuthBasic::create("admin", "123456");
+    ASSERT_TRUE(auth != NULL);
+    ASSERT_EQ(auth->getAuthMethodName(), "basic");
+    pulsar::AuthenticationDataPtr data;
+    ASSERT_EQ(auth->getAuthData(data), pulsar::ResultOk);
+    ASSERT_EQ(data->hasDataFromCommand(), true);
+    ASSERT_EQ(data->getCommandData(), "admin:123456");
+    ASSERT_EQ(data->hasDataForTls(), false);
+    ASSERT_EQ(data->hasDataForHttp(), true);
+
+    auth = pulsar::AuthBasic::create("{\"username\":\"super-user\",\"password\":\"123789\"}");
+    ASSERT_TRUE(auth != NULL);
+    ASSERT_EQ(auth->getAuthMethodName(), "basic");
+    ASSERT_EQ(auth->getAuthData(data), pulsar::ResultOk);
+    ASSERT_EQ(data->hasDataFromCommand(), true);
+    ASSERT_EQ(data->getCommandData(), "super-user:123789");
+    ASSERT_EQ(data->hasDataForTls(), false);
+    ASSERT_EQ(data->hasDataForHttp(), true);
+
+    ParamMap p = ParamMap();
+    p["username"] = "super-user-2";
+    p["password"] = "456789";
+    auth = pulsar::AuthBasic::create(p);
+    ASSERT_TRUE(auth != NULL);
+    ASSERT_EQ(auth->getAuthMethodName(), "basic");
+    ASSERT_EQ(auth->getAuthData(data), pulsar::ResultOk);
+    ASSERT_EQ(data->hasDataFromCommand(), true);
+    ASSERT_EQ(data->getCommandData(), "super-user-2:456789");
+    ASSERT_EQ(data->hasDataForTls(), false);
+    ASSERT_EQ(data->hasDataForHttp(), true);
+}


[pulsar] 01/02: [improve][cli] Add a separate TLS transport encryption configuration (#16930)

Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

penghui pushed a commit to branch branch-2.11
in repository https://gitbox.apache.org/repos/asf/pulsar.git

commit 024a5e594471bf8a71f3cdc15e391f120fb07112
Author: Zixuan Liu <no...@gmail.com>
AuthorDate: Fri Aug 5 11:57:06 2022 +0800

    [improve][cli] Add a separate TLS transport encryption configuration (#16930)
    
    Signed-off-by: Zixuan Liu <no...@gmail.com>
    (cherry picked from commit 1b097982f9c2bb1546823f8502305fcb220b584b)
---
 conf/client.conf                                    | 15 +++++++++++++++
 .../apache/pulsar/admin/cli/PulsarAdminTool.java    | 13 ++++++++++++-
 .../apache/pulsar/client/cli/PulsarClientTool.java  | 21 +++++++++++++++++++--
 3 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/conf/client.conf b/conf/client.conf
index b2b071adb81..50d9bf374c1 100644
--- a/conf/client.conf
+++ b/conf/client.conf
@@ -56,6 +56,12 @@ tlsEnableHostnameVerification=false
 # fails, then the cert is untrusted and the connection is dropped.
 tlsTrustCertsFilePath=
 
+# Path for the TLS certificate file
+tlsCertificateFilePath=
+
+# Path for the TLS private key file
+tlsKeyFilePath=
+
 # Enable TLS with KeyStore type configuration in broker.
 useKeyStoreTls=false
 
@@ -68,6 +74,15 @@ tlsTrustStorePath=
 # TLS TrustStore password
 tlsTrustStorePassword=
 
+# TLS KeyStore type configuration: JKS, PKCS12
+tlsKeyStoreType=JKS
+
+# TLS TrustStore path
+tlsKeyStorePath=
+
+# TLS TrustStore password
+tlsKeyStorePassword=
+
 # Set up TLS provider for web service
 # When TLS authentication with CACert is used, the valid value is either OPENSSL or JDK.
 # When TLS authentication with KeyStore is used, available options can be SunJSSE, Conscrypt and so on.
diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java
index 5c65ef052e6..b4a0e04439f 100644
--- a/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java
+++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/admin/cli/PulsarAdminTool.java
@@ -108,6 +108,12 @@ public class PulsarAdminTool {
         String tlsTrustStoreType = properties.getProperty("tlsTrustStoreType", "JKS");
         String tlsTrustStorePath = properties.getProperty("tlsTrustStorePath");
         String tlsTrustStorePassword = properties.getProperty("tlsTrustStorePassword");
+        String tlsKeyStoreType = properties.getProperty("tlsKeyStoreType", "JKS");
+        String tlsKeyStorePath = properties.getProperty("tlsKeyStorePath");
+        String tlsKeyStorePassword = properties.getProperty("tlsKeyStorePassword");
+        String tlsKeyFilePath = properties.getProperty("tlsKeyFilePath");
+        String tlsCertificateFilePath = properties.getProperty("tlsCertificateFilePath");
+
         boolean tlsAllowInsecureConnection = this.rootParams.tlsAllowInsecureConnection != null
                 ? this.rootParams.tlsAllowInsecureConnection
                 : Boolean.parseBoolean(properties.getProperty("tlsAllowInsecureConnection", "false"));
@@ -125,7 +131,12 @@ public class PulsarAdminTool {
                 .useKeyStoreTls(useKeyStoreTls)
                 .tlsTrustStoreType(tlsTrustStoreType)
                 .tlsTrustStorePath(tlsTrustStorePath)
-                .tlsTrustStorePassword(tlsTrustStorePassword);
+                .tlsTrustStorePassword(tlsTrustStorePassword)
+                .tlsKeyStoreType(tlsKeyStoreType)
+                .tlsKeyStorePath(tlsKeyStorePath)
+                .tlsKeyStorePassword(tlsKeyStorePassword)
+                .tlsKeyFilePath(tlsKeyFilePath)
+                .tlsCertificateFilePath(tlsCertificateFilePath);
     }
 
     protected void initRootParamsFromProperties(Properties properties) {
diff --git a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarClientTool.java b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarClientTool.java
index dd4f4b69f9a..32770f3bd07 100644
--- a/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarClientTool.java
+++ b/pulsar-client-tools/src/main/java/org/apache/pulsar/client/cli/PulsarClientTool.java
@@ -78,12 +78,17 @@ public class PulsarClientTool {
     boolean tlsAllowInsecureConnection;
     boolean tlsEnableHostnameVerification;
     String tlsTrustCertsFilePath;
+    String tlsKeyFilePath;
+    String tlsCertificateFilePath;
 
     // for tls with keystore type config
     boolean useKeyStoreTls;
     String tlsTrustStoreType;
     String tlsTrustStorePath;
     String tlsTrustStorePassword;
+    String tlsKeyStoreType;
+    String tlsKeyStorePath;
+    String tlsKeyStorePassword;
 
     protected JCommander jcommander;
     IUsageFormatter usageFormatter;
@@ -106,6 +111,12 @@ public class PulsarClientTool {
         this.tlsTrustStorePath = properties.getProperty("tlsTrustStorePath");
         this.tlsTrustStorePassword = properties.getProperty("tlsTrustStorePassword");
 
+        this.tlsKeyStoreType = properties.getProperty("tlsKeyStoreType", "JKS");
+        this.tlsKeyStorePath = properties.getProperty("tlsKeyStorePath");
+        this.tlsKeyStorePassword = properties.getProperty("tlsKeyStorePassword");
+        this.tlsKeyFilePath = properties.getProperty("tlsKeyFilePath");
+        this.tlsCertificateFilePath = properties.getProperty("tlsCertificateFilePath");
+
         initJCommander();
     }
 
@@ -146,14 +157,20 @@ public class PulsarClientTool {
             clientBuilder.listenerName(this.rootParams.listenerName);
         }
         clientBuilder.allowTlsInsecureConnection(this.tlsAllowInsecureConnection);
-        clientBuilder.tlsTrustCertsFilePath(this.tlsTrustCertsFilePath);
         clientBuilder.enableTlsHostnameVerification(this.tlsEnableHostnameVerification);
         clientBuilder.serviceUrl(rootParams.serviceURL);
 
+        clientBuilder.tlsTrustCertsFilePath(this.tlsTrustCertsFilePath)
+                .tlsKeyFilePath(tlsKeyFilePath)
+                .tlsCertificateFilePath(tlsCertificateFilePath);
+
         clientBuilder.useKeyStoreTls(useKeyStoreTls)
                 .tlsTrustStoreType(tlsTrustStoreType)
                 .tlsTrustStorePath(tlsTrustStorePath)
-                .tlsTrustStorePassword(tlsTrustStorePassword);
+                .tlsTrustStorePassword(tlsTrustStorePassword)
+                .tlsKeyStoreType(tlsKeyStoreType)
+                .tlsKeyStorePath(tlsKeyStorePath)
+                .tlsKeyStorePassword(tlsKeyStorePassword);
 
         if (isNotBlank(rootParams.proxyServiceURL)) {
             if (rootParams.proxyProtocol == null) {