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

cnauroth pushed a commit to branch branch-3.4
in repository https://gitbox.apache.org/repos/asf/hadoop.git


The following commit(s) were added to refs/heads/branch-3.4 by this push:
     new b3ae9d1eb68 HADOOP-19152. Do not hard code security providers. (#6739) 
(#7439)
b3ae9d1eb68 is described below

commit b3ae9d1eb6841a6c9f9928a917cc50818a319f71
Author: Cheng Pan <cheng...@apache.org>
AuthorDate: Fri Mar 7 05:51:05 2025 +0800

    HADOOP-19152. Do not hard code security providers. (#6739) (#7439)
    
    Co-authored-by: Tsz-Wo Nicholas Sze <szets...@apache.org>
    Signed-off-by: Chris Nauroth <cnaur...@apache.org>
---
 .../java/org/apache/hadoop/crypto/CryptoUtils.java | 77 +++++++++++++++++++
 .../apache/hadoop/crypto/JceCtrCryptoCodec.java    | 15 +---
 .../org/apache/hadoop/crypto/key/KeyProvider.java  |  9 +--
 .../org/apache/hadoop/crypto/package-info.java     | 20 +++++
 .../hadoop/fs/CommonConfigurationKeysPublic.java   |  3 +
 .../src/main/resources/core-default.xml            | 15 ++++
 .../org/apache/hadoop/crypto/TestCryptoUtils.java  | 89 ++++++++++++++++++++++
 7 files changed, 209 insertions(+), 19 deletions(-)

diff --git 
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/CryptoUtils.java
 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/CryptoUtils.java
new file mode 100644
index 00000000000..3d118457df7
--- /dev/null
+++ 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/CryptoUtils.java
@@ -0,0 +1,77 @@
+/*
+ * 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.crypto;
+
+import java.security.Provider;
+import java.security.Security;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
+import org.apache.hadoop.fs.store.LogExactlyOnce;
+
+/** Utility methods for the crypto related features. */
+@InterfaceAudience.Private
+public final class CryptoUtils {
+  static final Logger LOG = LoggerFactory.getLogger(CryptoUtils.class);
+  private static final LogExactlyOnce LOG_FAILED_TO_LOAD_CLASS = new 
LogExactlyOnce(LOG);
+  private static final LogExactlyOnce LOG_FAILED_TO_ADD_PROVIDER = new 
LogExactlyOnce(LOG);
+
+  private static final String BOUNCY_CASTLE_PROVIDER_CLASS
+      = "org.bouncycastle.jce.provider.BouncyCastleProvider";
+  static final String BOUNCY_CASTLE_PROVIDER_NAME = "BC";
+
+  /**
+   * Get the security provider value specified in
+   * {@link 
CommonConfigurationKeysPublic#HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_KEY}
+   * from the given conf.
+   *
+   * @param conf the configuration
+   * @return the configured provider, if there is any; otherwise, return an 
empty string.
+   */
+  public static String getJceProvider(Configuration conf) {
+    final String provider = conf.getTrimmed(
+        CommonConfigurationKeysPublic.HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_KEY, 
"");
+    final boolean autoAdd = conf.getBoolean(
+        
CommonConfigurationKeysPublic.HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_AUTO_ADD_KEY,
+        
CommonConfigurationKeysPublic.HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_AUTO_ADD_DEFAULT);
+
+    // For backward compatible, auto-add BOUNCY_CASTLE_PROVIDER_CLASS when the 
provider is "BC".
+    if (autoAdd && BOUNCY_CASTLE_PROVIDER_NAME.equals(provider)) {
+      try {
+        // Use reflection in order to avoid statically loading the class.
+        final Class<?> clazz = Class.forName(BOUNCY_CASTLE_PROVIDER_CLASS);
+        Security.addProvider((Provider) clazz.getConstructor().newInstance());
+        LOG.debug("Successfully added security provider {}", provider);
+        if (LOG.isTraceEnabled()) {
+          LOG.trace("Trace", new Throwable());
+        }
+      } catch (ClassNotFoundException e) {
+        LOG_FAILED_TO_LOAD_CLASS.warn("Failed to load " + 
BOUNCY_CASTLE_PROVIDER_CLASS, e);
+      } catch (Exception e) {
+        LOG_FAILED_TO_ADD_PROVIDER.warn("Failed to add security provider for 
{}", provider, e);
+      }
+    }
+    return provider;
+  }
+
+  private CryptoUtils() { }
+}
diff --git 
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/JceCtrCryptoCodec.java
 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/JceCtrCryptoCodec.java
index 7aae65d47cc..8d15a8262e9 100644
--- 
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/JceCtrCryptoCodec.java
+++ 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/JceCtrCryptoCodec.java
@@ -17,7 +17,6 @@
  */
 package org.apache.hadoop.crypto;
 
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.apache.hadoop.util.Preconditions;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceStability;
@@ -27,13 +26,11 @@
 import java.nio.ByteBuffer;
 import java.security.GeneralSecurityException;
 import java.security.SecureRandom;
-import java.security.Security;
 import javax.crypto.Cipher;
 import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 import org.slf4j.Logger;
 
-import static 
org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_KEY;
 import static 
org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_JAVA_SECURE_RANDOM_ALGORITHM_KEY;
 import static 
org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_JAVA_SECURE_RANDOM_ALGORITHM_DEFAULT;
 
@@ -48,10 +45,6 @@ public String getProvider() {
     return provider;
   }
 
-  public void setProvider(String provider) {
-    this.provider = provider;
-  }
-
   public void calculateIV(byte[] initIV, long counter,
                             byte[] iv, int blockSize) {
     Preconditions.checkArgument(initIV.length == blockSize);
@@ -82,17 +75,15 @@ public Configuration getConf() {
 
   public void setConf(Configuration conf) {
     this.conf = conf;
-    setProvider(conf.get(HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_KEY));
-    if (BouncyCastleProvider.PROVIDER_NAME.equals(provider)) {
-      Security.addProvider(new BouncyCastleProvider());
-    }
+    this.provider = CryptoUtils.getJceProvider(conf);
+
     final String secureRandomAlg =
           conf.get(
               HADOOP_SECURITY_JAVA_SECURE_RANDOM_ALGORITHM_KEY,
               HADOOP_SECURITY_JAVA_SECURE_RANDOM_ALGORITHM_DEFAULT);
 
     try {
-      random = (provider != null)
+      random = (provider != null && !provider.isEmpty())
             ? SecureRandom.getInstance(secureRandomAlg, provider)
             : SecureRandom.getInstance(secureRandomAlg);
     } catch(GeneralSecurityException e) {
diff --git 
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProvider.java
 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProvider.java
index 5e207251805..5cdd2f8d53f 100644
--- 
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProvider.java
+++ 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProvider.java
@@ -26,7 +26,6 @@
 import java.io.OutputStreamWriter;
 import java.nio.charset.StandardCharsets;
 import java.security.NoSuchAlgorithmException;
-import java.security.Security;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
@@ -35,17 +34,16 @@
 import java.util.Map;
 import java.util.Objects;
 
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import com.google.gson.stream.JsonReader;
 import com.google.gson.stream.JsonWriter;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceStability;
 import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.crypto.CryptoUtils;
 import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
 
 import javax.crypto.KeyGenerator;
 
-import static 
org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_KEY;
 import static 
org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_CRYPTO_JCEKS_KEY_SERIALFILTER;
 
 /**
@@ -410,10 +408,7 @@ public KeyProvider(Configuration conf) {
                       JCEKS_KEY_SERIALFILTER_DEFAULT);
       System.setProperty(JCEKS_KEY_SERIAL_FILTER, serialFilter);
     }
-    String jceProvider = conf.get(HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_KEY);
-    if (BouncyCastleProvider.PROVIDER_NAME.equals(jceProvider)) {
-      Security.addProvider(new BouncyCastleProvider());
-    }
+    CryptoUtils.getJceProvider(conf);
   }
 
   /**
diff --git 
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/package-info.java
 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/package-info.java
new file mode 100644
index 00000000000..fe947dc2630
--- /dev/null
+++ 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/** Crypto related classes. */
+package org.apache.hadoop.crypto;
diff --git 
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/CommonConfigurationKeysPublic.java
 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/CommonConfigurationKeysPublic.java
index c22c48f1ffe..0b36aec318d 100644
--- 
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/CommonConfigurationKeysPublic.java
+++ 
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/CommonConfigurationKeysPublic.java
@@ -773,6 +773,9 @@ public class CommonConfigurationKeysPublic {
    */
   public static final String HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_KEY =
     "hadoop.security.crypto.jce.provider";
+  public static final String HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_AUTO_ADD_KEY =
+      "hadoop.security.crypto.jce.provider.auto-add";
+  public static final boolean 
HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_AUTO_ADD_DEFAULT = true;
   /**
    * @see
    * <a 
href="{@docRoot}/../hadoop-project-dist/hadoop-common/core-default.xml">
diff --git 
a/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml 
b/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml
index c31843e8c4a..4104e304314 100644
--- a/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml
+++ b/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml
@@ -3633,6 +3633,21 @@ The switch to turn S3A auditing on or off.
   <value></value>
   <description>
     The JCE provider name used in CryptoCodec.
+    If this value is set, the corresponding provider must be added to the 
provider list.
+    The provider may be added statically in the java.security file, or
+    dynamically by calling the java.security.Security.addProvider(..) method, 
or
+    automatically (only for org.bouncycastle.jce.provider.BouncyCastleProvider)
+    by setting "hadoop.security.crypto.jce.provider.auto-add" to true
+  </description>
+</property>
+
+<property>
+  <name>hadoop.security.crypto.jce.provider.auto-add</name>
+  <value>true</value>
+  <description>
+    Automatically add the org.bouncycastle.jce.provider.BouncyCastleProvider
+    when the value in "hadoop.security.crypto.jce.provider" is set
+    to BouncyCastleProvider.PROVIDER_NAME.
   </description>
 </property>
 
diff --git 
a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/TestCryptoUtils.java
 
b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/TestCryptoUtils.java
new file mode 100644
index 00000000000..be369547240
--- /dev/null
+++ 
b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/TestCryptoUtils.java
@@ -0,0 +1,89 @@
+/*
+ * 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.crypto;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.test.GenericTestUtils;
+import org.assertj.core.api.Assertions;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.Assert;
+import org.junit.Test;
+import org.slf4j.event.Level;
+
+import java.security.Provider;
+import java.security.Security;
+
+import static 
org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_AUTO_ADD_DEFAULT;
+import static 
org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_AUTO_ADD_KEY;
+import static 
org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_KEY;
+
+/** Test {@link CryptoUtils}. */
+public class TestCryptoUtils {
+  static {
+    GenericTestUtils.setLogLevel(CryptoUtils.LOG, Level.TRACE);
+  }
+
+  @Test(timeout = 1_000)
+  public void testProviderName() {
+    Assert.assertEquals(CryptoUtils.BOUNCY_CASTLE_PROVIDER_NAME, 
BouncyCastleProvider.PROVIDER_NAME);
+  }
+
+  static void assertRemoveProvider() {
+    Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
+    
Assert.assertNull(Security.getProvider(BouncyCastleProvider.PROVIDER_NAME));
+  }
+
+  static void assertSetProvider(Configuration conf) {
+    conf.set(HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_KEY, 
CryptoUtils.BOUNCY_CASTLE_PROVIDER_NAME);
+    final String providerFromConf = CryptoUtils.getJceProvider(conf);
+    Assert.assertEquals(CryptoUtils.BOUNCY_CASTLE_PROVIDER_NAME, 
providerFromConf);
+  }
+
+  @Test(timeout = 5_000)
+  public void testAutoAddDisabled() {
+    assertRemoveProvider();
+
+    final Configuration conf = new Configuration();
+    conf.setBoolean(HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_AUTO_ADD_KEY, false);
+
+    assertSetProvider(conf);
+
+    
Assert.assertNull(Security.getProvider(BouncyCastleProvider.PROVIDER_NAME));
+  }
+
+  @Test(timeout = 5_000)
+  public void testAutoAddEnabled() {
+    assertRemoveProvider();
+
+    final Configuration conf = new Configuration();
+    
Assertions.assertThat(conf.get(HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_AUTO_ADD_KEY))
+        .describedAs("conf: " + 
HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_AUTO_ADD_KEY)
+        .isEqualToIgnoringCase("true");
+    Assert.assertTrue(HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_AUTO_ADD_DEFAULT);
+
+    conf.set(HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_KEY, 
CryptoUtils.BOUNCY_CASTLE_PROVIDER_NAME);
+    final String providerFromConf = CryptoUtils.getJceProvider(conf);
+    Assert.assertEquals(CryptoUtils.BOUNCY_CASTLE_PROVIDER_NAME, 
providerFromConf);
+
+    final Provider provider = 
Security.getProvider(BouncyCastleProvider.PROVIDER_NAME);
+    Assertions.assertThat(provider)
+        .isInstanceOf(BouncyCastleProvider.class);
+
+    assertRemoveProvider();
+  }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscr...@hadoop.apache.org
For additional commands, e-mail: common-commits-h...@hadoop.apache.org

Reply via email to