diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/BCFKSFileLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/BCFKSFileLoader.java
new file mode 100644
index 00000000000..cefa4135c90
--- /dev/null
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/BCFKSFileLoader.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+package org.apache.hadoop.hbase.io.crypto.tls;
+
+/**
+ * Implementation of {@link FileKeyStoreLoader} that loads from BCKFS files.
+ *
+ * This file has been copied from the Apache ZooKeeper project.
+ * @see Base
+ * revision
+ */
+final class BCFKSFileLoader extends StandardTypeFileKeyStoreLoader {
+ private BCFKSFileLoader(String keyStorePath, String trustStorePath, char[] keyStorePassword,
+ char[] trustStorePassword) {
+ super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword,
+ SupportedStandardKeyFormat.BCFKS);
+ }
+
+ static class Builder extends FileKeyStoreLoader.Builder {
+ @Override
+ BCFKSFileLoader build() {
+ return new BCFKSFileLoader(keyStorePath, trustStorePath, keyStorePassword,
+ trustStorePassword);
+ }
+ }
+}
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoader.java
new file mode 100644
index 00000000000..3a1740b4faf
--- /dev/null
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoader.java
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+package org.apache.hadoop.hbase.io.crypto.tls;
+
+import java.util.Objects;
+
+/**
+ * Base class for instances of {@link KeyStoreLoader} which load the key/trust stores from files on
+ * a filesystem.
+ *
+ * This file has been copied from the Apache ZooKeeper project.
+ * @see Base
+ * revision
+ */
+abstract class FileKeyStoreLoader implements KeyStoreLoader {
+ final String keyStorePath;
+ final String trustStorePath;
+ final char[] keyStorePassword;
+ final char[] trustStorePassword;
+
+ FileKeyStoreLoader(String keyStorePath, String trustStorePath, char[] keyStorePassword,
+ char[] trustStorePassword) {
+ this.keyStorePath = keyStorePath;
+ this.trustStorePath = trustStorePath;
+ this.keyStorePassword = keyStorePassword;
+ this.trustStorePassword = trustStorePassword;
+ }
+
+ /**
+ * Base class for builder pattern used by subclasses.
+ * @param the subtype of FileKeyStoreLoader created by the Builder.
+ */
+ static abstract class Builder {
+ String keyStorePath;
+ String trustStorePath;
+ char[] keyStorePassword;
+ char[] trustStorePassword;
+
+ Builder() {
+ }
+
+ Builder setKeyStorePath(String keyStorePath) {
+ this.keyStorePath = Objects.requireNonNull(keyStorePath);
+ return this;
+ }
+
+ Builder setTrustStorePath(String trustStorePath) {
+ this.trustStorePath = Objects.requireNonNull(trustStorePath);
+ return this;
+ }
+
+ Builder setKeyStorePassword(char[] keyStorePassword) {
+ this.keyStorePassword = Objects.requireNonNull(keyStorePassword);
+ return this;
+ }
+
+ Builder setTrustStorePassword(char[] trustStorePassword) {
+ this.trustStorePassword = Objects.requireNonNull(trustStorePassword);
+ return this;
+ }
+
+ abstract T build();
+ }
+}
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java
new file mode 100644
index 00000000000..432c8a06d11
--- /dev/null
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/FileKeyStoreLoaderBuilderProvider.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+package org.apache.hadoop.hbase.io.crypto.tls;
+
+import java.util.Objects;
+
+/**
+ * This file has been copied from the Apache ZooKeeper project.
+ * @see Base
+ * revision
+ */
+final class FileKeyStoreLoaderBuilderProvider {
+ /**
+ * Returns a {@link FileKeyStoreLoader.Builder} that can build a loader which loads keys and certs
+ * from files of the given {@link KeyStoreFileType}.
+ * @param type the file type to load keys/certs from.
+ * @return a new Builder.
+ */
+ static FileKeyStoreLoader.Builder extends FileKeyStoreLoader>
+ getBuilderForKeyStoreFileType(KeyStoreFileType type) {
+ switch (Objects.requireNonNull(type)) {
+ case JKS:
+ return new JKSFileLoader.Builder();
+ case PEM:
+ return new PEMFileLoader.Builder();
+ case PKCS12:
+ return new PKCS12FileLoader.Builder();
+ case BCFKS:
+ return new BCFKSFileLoader.Builder();
+ default:
+ throw new AssertionError("Unexpected StoreFileType: " + type.name());
+ }
+ }
+
+ private FileKeyStoreLoaderBuilderProvider() {
+ // disabled
+ }
+}
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/JKSFileLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/JKSFileLoader.java
new file mode 100644
index 00000000000..36e9643cbfe
--- /dev/null
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/JKSFileLoader.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+package org.apache.hadoop.hbase.io.crypto.tls;
+
+/**
+ * Implementation of {@link FileKeyStoreLoader} that loads from JKS files.
+ *
+ * This file has been copied from the Apache ZooKeeper project.
+ * @see Base
+ * revision
+ */
+final class JKSFileLoader extends StandardTypeFileKeyStoreLoader {
+ private JKSFileLoader(String keyStorePath, String trustStorePath, char[] keyStorePassword,
+ char[] trustStorePassword) {
+ super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword,
+ SupportedStandardKeyFormat.JKS);
+ }
+
+ static class Builder extends FileKeyStoreLoader.Builder {
+ @Override
+ JKSFileLoader build() {
+ return new JKSFileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword);
+ }
+ }
+}
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/KeyStoreLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/KeyStoreLoader.java
new file mode 100644
index 00000000000..928b3b9d046
--- /dev/null
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/KeyStoreLoader.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+package org.apache.hadoop.hbase.io.crypto.tls;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+
+/**
+ * An interface for an object that can load key stores or trust stores.
+ *
+ * This file has been copied from the Apache ZooKeeper project.
+ * @see Base
+ * revision
+ */
+interface KeyStoreLoader {
+ /**
+ * Loads a KeyStore which contains at least one private key and the associated X509 cert chain.
+ * @return a new KeyStore
+ * @throws IOException if loading the key store fails due to an IO error, such as
+ * "file not found".
+ * @throws GeneralSecurityException if loading the key store fails due to a security error, such
+ * as "unsupported crypto algorithm".
+ */
+ KeyStore loadKeyStore() throws IOException, GeneralSecurityException;
+
+ /**
+ * Loads a KeyStore which contains at least one X509 cert chain for a trusted Certificate
+ * Authority (CA).
+ * @return a new KeyStore
+ * @throws IOException if loading the trust store fails due to an IO error, such as
+ * "file not found".
+ * @throws GeneralSecurityException if loading the trust store fails due to a security error, such
+ * as "unsupported crypto algorithm".
+ */
+ KeyStore loadTrustStore() throws IOException, GeneralSecurityException;
+}
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PEMFileLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PEMFileLoader.java
new file mode 100644
index 00000000000..06d264b67da
--- /dev/null
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PEMFileLoader.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+package org.apache.hadoop.hbase.io.crypto.tls;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+
+/**
+ * Implementation of {@link FileKeyStoreLoader} that loads from PEM files.
+ *
+ * This file has been copied from the Apache ZooKeeper project.
+ * @see Base
+ * revision
+ */
+final class PEMFileLoader extends FileKeyStoreLoader {
+ private PEMFileLoader(String keyStorePath, String trustStorePath, char[] keyStorePassword,
+ char[] trustStorePassword) {
+ super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword);
+ }
+
+ @Override
+ public KeyStore loadKeyStore() throws IOException, GeneralSecurityException {
+ File file = new File(keyStorePath);
+ return PemReader.loadKeyStore(file, file, keyStorePassword);
+ }
+
+ @Override
+ public KeyStore loadTrustStore() throws IOException, GeneralSecurityException {
+ return PemReader.loadTrustStore(new File(trustStorePath));
+ }
+
+ static class Builder extends FileKeyStoreLoader.Builder {
+ @Override
+ PEMFileLoader build() {
+ return new PEMFileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword);
+ }
+ }
+}
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PKCS12FileLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PKCS12FileLoader.java
new file mode 100644
index 00000000000..ab5a532787e
--- /dev/null
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PKCS12FileLoader.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+package org.apache.hadoop.hbase.io.crypto.tls;
+
+/**
+ * Implementation of {@link FileKeyStoreLoader} that loads from PKCS12 files.
+ *
+ * This file has been copied from the Apache ZooKeeper project.
+ * @see Base
+ * revision
+ */
+final class PKCS12FileLoader extends StandardTypeFileKeyStoreLoader {
+ private PKCS12FileLoader(String keyStorePath, String trustStorePath, char[] keyStorePassword,
+ char[] trustStorePassword) {
+ super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword,
+ SupportedStandardKeyFormat.PKCS12);
+ }
+
+ static class Builder extends FileKeyStoreLoader.Builder {
+ @Override
+ PKCS12FileLoader build() {
+ return new PKCS12FileLoader(keyStorePath, trustStorePath, keyStorePassword,
+ trustStorePassword);
+ }
+ }
+}
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PemReader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PemReader.java
new file mode 100644
index 00000000000..b4f7aa5565a
--- /dev/null
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/PemReader.java
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ */
+package org.apache.hadoop.hbase.io.crypto.tls;
+
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static java.util.Base64.getMimeDecoder;
+import static java.util.regex.Pattern.CASE_INSENSITIVE;
+import static javax.crypto.Cipher.DECRYPT_MODE;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.crypto.Cipher;
+import javax.crypto.EncryptedPrivateKeyInfo;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * This file has been copied from the Apache ZooKeeper project.
+ * @see Base
+ * revision
+ */
+final class PemReader {
+ private static final Pattern CERT_PATTERN =
+ Pattern.compile("-+BEGIN\\s+.*CERTIFICATE[^-]*-+(?:\\s|\\r|\\n)+" + // Header
+ "([a-z0-9+/=\\r\\n]+)" + // Base64 text
+ "-+END\\s+.*CERTIFICATE[^-]*-+", // Footer
+ CASE_INSENSITIVE);
+
+ private static final Pattern PRIVATE_KEY_PATTERN =
+ Pattern.compile("-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" + // Header
+ "([a-z0-9+/=\\r\\n]+)" + // Base64 text
+ "-+END\\s+.*PRIVATE\\s+KEY[^-]*-+", // Footer
+ CASE_INSENSITIVE);
+
+ private static final Pattern PUBLIC_KEY_PATTERN =
+ Pattern.compile("-+BEGIN\\s+.*PUBLIC\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" + // Header
+ "([a-z0-9+/=\\r\\n]+)" + // Base64 text
+ "-+END\\s+.*PUBLIC\\s+KEY[^-]*-+", // Footer
+ CASE_INSENSITIVE);
+
+ private PemReader() {
+ }
+
+ public static KeyStore loadTrustStore(File certificateChainFile)
+ throws IOException, GeneralSecurityException {
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(null, null);
+
+ List certificateChain = readCertificateChain(certificateChainFile);
+ for (X509Certificate certificate : certificateChain) {
+ X500Principal principal = certificate.getSubjectX500Principal();
+ keyStore.setCertificateEntry(principal.getName("RFC2253"), certificate);
+ }
+ return keyStore;
+ }
+
+ public static KeyStore loadKeyStore(File certificateChainFile, File privateKeyFile,
+ char[] keyPassword) throws IOException, GeneralSecurityException {
+ PrivateKey key = loadPrivateKey(privateKeyFile, keyPassword);
+
+ List certificateChain = readCertificateChain(certificateChainFile);
+ if (certificateChain.isEmpty()) {
+ throw new CertificateException(
+ "Certificate file does not contain any certificates: " + certificateChainFile);
+ }
+
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(null, null);
+ keyStore.setKeyEntry("key", key, keyPassword, certificateChain.toArray(new Certificate[0]));
+ return keyStore;
+ }
+
+ public static List readCertificateChain(File certificateChainFile)
+ throws IOException, GeneralSecurityException {
+ String contents = new String(Files.readAllBytes(certificateChainFile.toPath()), US_ASCII);
+ return readCertificateChain(contents);
+ }
+
+ public static List readCertificateChain(String certificateChain)
+ throws CertificateException {
+ Matcher matcher = CERT_PATTERN.matcher(certificateChain);
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+ List certificates = new ArrayList<>();
+
+ int start = 0;
+ while (matcher.find(start)) {
+ byte[] buffer = base64Decode(matcher.group(1));
+ certificates.add(
+ (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(buffer)));
+ start = matcher.end();
+ }
+
+ return certificates;
+ }
+
+ public static PrivateKey loadPrivateKey(File privateKeyFile, char[] keyPassword)
+ throws IOException, GeneralSecurityException {
+ String privateKey = new String(Files.readAllBytes(privateKeyFile.toPath()), US_ASCII);
+ return loadPrivateKey(privateKey, keyPassword);
+ }
+
+ public static PrivateKey loadPrivateKey(String privateKey, char[] keyPassword)
+ throws IOException, GeneralSecurityException {
+ Matcher matcher = PRIVATE_KEY_PATTERN.matcher(privateKey);
+ if (!matcher.find()) {
+ throw new KeyStoreException("did not find a private key");
+ }
+ byte[] encodedKey = base64Decode(matcher.group(1));
+
+ PKCS8EncodedKeySpec encodedKeySpec;
+ if (keyPassword != null && keyPassword.length > 0) {
+ EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(encodedKey);
+ SecretKeyFactory keyFactory =
+ SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
+ SecretKey secretKey = keyFactory.generateSecret(new PBEKeySpec(keyPassword));
+
+ Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
+ cipher.init(DECRYPT_MODE, secretKey, encryptedPrivateKeyInfo.getAlgParameters());
+
+ encodedKeySpec = encryptedPrivateKeyInfo.getKeySpec(cipher);
+ } else {
+ encodedKeySpec = new PKCS8EncodedKeySpec(encodedKey);
+ }
+
+ // this code requires a key in PKCS8 format which is not the default openssl format
+ // to convert to the PKCS8 format you use : openssl pkcs8 -topk8 ...
+ try {
+ KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+ return keyFactory.generatePrivate(encodedKeySpec);
+ } catch (InvalidKeySpecException ignore) {
+ // ignore
+ }
+
+ try {
+ KeyFactory keyFactory = KeyFactory.getInstance("EC");
+ return keyFactory.generatePrivate(encodedKeySpec);
+ } catch (InvalidKeySpecException ignore) {
+ // ignore
+ }
+
+ KeyFactory keyFactory = KeyFactory.getInstance("DSA");
+ return keyFactory.generatePrivate(encodedKeySpec);
+ }
+
+ public static PublicKey loadPublicKey(File publicKeyFile)
+ throws IOException, GeneralSecurityException {
+ String publicKey = new String(Files.readAllBytes(publicKeyFile.toPath()), US_ASCII);
+ return loadPublicKey(publicKey);
+ }
+
+ public static PublicKey loadPublicKey(String publicKey) throws GeneralSecurityException {
+ Matcher matcher = PUBLIC_KEY_PATTERN.matcher(publicKey);
+ if (!matcher.find()) {
+ throw new KeyStoreException("did not find a public key");
+ }
+ String data = matcher.group(1);
+ byte[] encodedKey = base64Decode(data);
+
+ X509EncodedKeySpec encodedKeySpec = new X509EncodedKeySpec(encodedKey);
+ try {
+ KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+ return keyFactory.generatePublic(encodedKeySpec);
+ } catch (InvalidKeySpecException ignore) {
+ // ignore
+ }
+
+ try {
+ KeyFactory keyFactory = KeyFactory.getInstance("EC");
+ return keyFactory.generatePublic(encodedKeySpec);
+ } catch (InvalidKeySpecException ignore) {
+ // ignore
+ }
+
+ KeyFactory keyFactory = KeyFactory.getInstance("DSA");
+ return keyFactory.generatePublic(encodedKeySpec);
+ }
+
+ private static byte[] base64Decode(String base64) {
+ return getMimeDecoder().decode(base64.getBytes(US_ASCII));
+ }
+}
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/StandardTypeFileKeyStoreLoader.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/StandardTypeFileKeyStoreLoader.java
new file mode 100644
index 00000000000..67aebdd6c7b
--- /dev/null
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/StandardTypeFileKeyStoreLoader.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+package org.apache.hadoop.hbase.io.crypto.tls;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+
+/**
+ * Base class for instances of {@link KeyStoreLoader} which load the key/trust stores from files on
+ * a filesystem using standard {@link KeyStore} types like JKS or PKCS12.
+ *
+ * This file has been copied from the Apache ZooKeeper project.
+ * @see Base
+ * revision
+ */
+abstract class StandardTypeFileKeyStoreLoader extends FileKeyStoreLoader {
+ private static final char[] EMPTY_CHAR_ARRAY = new char[0];
+
+ protected final SupportedStandardKeyFormat format;
+
+ protected enum SupportedStandardKeyFormat {
+ JKS,
+ PKCS12,
+ BCFKS
+ }
+
+ StandardTypeFileKeyStoreLoader(String keyStorePath, String trustStorePath,
+ char[] keyStorePassword, char[] trustStorePassword, SupportedStandardKeyFormat format) {
+ super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword);
+ this.format = format;
+ }
+
+ @Override
+ public KeyStore loadKeyStore() throws IOException, GeneralSecurityException {
+ try (InputStream inputStream = Files.newInputStream(new File(keyStorePath).toPath())) {
+ KeyStore ks = keyStoreInstance();
+ ks.load(inputStream, passwordStringToCharArray(keyStorePassword));
+ return ks;
+ }
+ }
+
+ @Override
+ public KeyStore loadTrustStore() throws IOException, GeneralSecurityException {
+ try (InputStream inputStream = Files.newInputStream(new File(trustStorePath).toPath())) {
+ KeyStore ts = keyStoreInstance();
+ ts.load(inputStream, passwordStringToCharArray(trustStorePassword));
+ return ts;
+ }
+ }
+
+ private KeyStore keyStoreInstance() throws KeyStoreException {
+ return KeyStore.getInstance(format.name());
+ }
+
+ private static char[] passwordStringToCharArray(char[] password) {
+ return password == null ? EMPTY_CHAR_ARRAY : password;
+ }
+}
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/X509Util.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/X509Util.java
index 76b7fad4c59..471ad41d06f 100644
--- a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/X509Util.java
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/X509Util.java
@@ -17,10 +17,7 @@
*/
package org.apache.hadoop.hbase.io.crypto.tls;
-import java.io.File;
import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.Security;
@@ -227,19 +224,16 @@ public final class X509Util {
static X509KeyManager createKeyManager(String keyStoreLocation, char[] keyStorePassword,
String keyStoreType) throws KeyManagerException {
- if (keyStoreType == null) {
- keyStoreType = "jks";
- }
-
if (keyStorePassword == null) {
keyStorePassword = EMPTY_CHAR_ARRAY;
}
try {
- KeyStore ks = KeyStore.getInstance(keyStoreType);
- try (InputStream inputStream = Files.newInputStream(new File(keyStoreLocation).toPath())) {
- ks.load(inputStream, keyStorePassword);
- }
+ KeyStoreFileType storeFileType =
+ KeyStoreFileType.fromPropertyValueOrFileName(keyStoreType, keyStoreLocation);
+ KeyStore ks = FileKeyStoreLoaderBuilderProvider.getBuilderForKeyStoreFileType(storeFileType)
+ .setKeyStorePath(keyStoreLocation).setKeyStorePassword(keyStorePassword).build()
+ .loadKeyStore();
KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
kmf.init(ks, keyStorePassword);
@@ -272,19 +266,16 @@ public final class X509Util {
static X509TrustManager createTrustManager(String trustStoreLocation, char[] trustStorePassword,
String trustStoreType, boolean crlEnabled, boolean ocspEnabled) throws TrustManagerException {
- if (trustStoreType == null) {
- trustStoreType = "jks";
- }
-
if (trustStorePassword == null) {
trustStorePassword = EMPTY_CHAR_ARRAY;
}
try {
- KeyStore ts = KeyStore.getInstance(trustStoreType);
- try (InputStream inputStream = Files.newInputStream(new File(trustStoreLocation).toPath())) {
- ts.load(inputStream, trustStorePassword);
- }
+ KeyStoreFileType storeFileType =
+ KeyStoreFileType.fromPropertyValueOrFileName(trustStoreType, trustStoreLocation);
+ KeyStore ts = FileKeyStoreLoaderBuilderProvider.getBuilderForKeyStoreFileType(storeFileType)
+ .setTrustStorePath(trustStoreLocation).setTrustStorePassword(trustStorePassword).build()
+ .loadTrustStore();
PKIXBuilderParameters pbParams = new PKIXBuilderParameters(ts, new X509CertSelector());
if (crlEnabled || ocspEnabled) {
diff --git a/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/AbstractTestX509Parameterized.java b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/AbstractTestX509Parameterized.java
new file mode 100644
index 00000000000..821a6854135
--- /dev/null
+++ b/hbase-common/src/test/java/org/apache/hadoop/hbase/io/crypto/tls/AbstractTestX509Parameterized.java
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+package org.apache.hadoop.hbase.io.crypto.tls;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.Security;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.apache.commons.io.FileUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseCommonTestingUtil;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.runners.Parameterized;
+
+/**
+ * Base class for parameterized unit tests that use X509TestContext for testing different X509
+ * parameter combinations (CA key type, cert key type, with/without a password, with/without
+ * hostname verification, etc).
+ *
+ * This base class takes care of setting up / cleaning up the test environment, and caching the
+ * X509TestContext objects used by the tests.
+ *
+ * This file has been copied from the Apache ZooKeeper project.
+ * @see Base
+ * revision
+ */
+public abstract class AbstractTestX509Parameterized {
+
+ private static final HBaseCommonTestingUtil UTIL = new HBaseCommonTestingUtil();
+ private static X509TestContextProvider PROVIDER;
+
+ @Parameterized.Parameter()
+ public X509KeyType caKeyType;
+
+ @Parameterized.Parameter(value = 1)
+ public X509KeyType certKeyType;
+
+ @Parameterized.Parameter(value = 2)
+ public char[] keyPassword;
+
+ @Parameterized.Parameter(value = 3)
+ public Integer paramIndex;
+
+ /**
+ * Default parameters suitable for most subclasses. See example usage in {@link TestX509Util}.
+ * @return an array of parameter combinations to test with.
+ */
+ @Parameterized.Parameters(
+ name = "{index}: caKeyType={0}, certKeyType={1}, keyPassword={2}, paramIndex={3}")
+ public static Collection