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

snazy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/polaris.git


The following commit(s) were added to refs/heads/main by this push:
     new 5332e4f9b Remove Bouncy Castle dependency usage from PemUtils (#1318)
5332e4f9b is described below

commit 5332e4f9baafb9d17aaad7f2dcb5eb163b0ffc96
Author: David Handermann <[email protected]>
AuthorDate: Tue May 20 09:55:21 2025 -0500

    Remove Bouncy Castle dependency usage from PemUtils (#1318)
    
    - Added PEM format parsing in PemUtils
    - Added unit test for PemUtils for empty file and multiple PEM objects
    - Removed Bouncy Castle Provider dependency from service common module
    - Removed Bouncy Castle Provider dependency from quarkus service module
---
 gradle/libs.versions.toml                          |   1 -
 quarkus/service/build.gradle.kts                   |   2 -
 service/common/build.gradle.kts                    |   2 -
 .../org/apache/polaris/service/auth/PemUtils.java  |  40 +++++-
 .../apache/polaris/service/auth/PemUtilsTest.java  | 160 +++++++++++++++++++++
 5 files changed, 195 insertions(+), 10 deletions(-)

diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 12c024e44..889747c81 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -41,7 +41,6 @@ assertj-core = { module = "org.assertj:assertj-core", version 
= "3.27.3" }
 auth0-jwt = { module = "com.auth0:java-jwt", version = "4.5.0" }
 awssdk-bom = { module = "software.amazon.awssdk:bom", version = "2.31.45" }
 azuresdk-bom = { module = "com.azure:azure-sdk-bom", version = "1.2.34" }
-bouncycastle-bcprov = { module = "org.bouncycastle:bcprov-jdk18on", version = 
"1.80" }
 caffeine = { module = "com.github.ben-manes.caffeine:caffeine", version = 
"3.2.0" }
 commons-codec1 = { module = "commons-codec:commons-codec", version = "1.18.0" }
 commons-lang3 = { module = "org.apache.commons:commons-lang3", version = 
"3.17.0" }
diff --git a/quarkus/service/build.gradle.kts b/quarkus/service/build.gradle.kts
index 0c34075bf..a7dcd14c4 100644
--- a/quarkus/service/build.gradle.kts
+++ b/quarkus/service/build.gradle.kts
@@ -70,8 +70,6 @@ dependencies {
 
   implementation(libs.auth0.jwt)
 
-  implementation(libs.bouncycastle.bcprov)
-
   compileOnly(libs.jakarta.annotation.api)
   compileOnly(libs.spotbugs.annotations)
 
diff --git a/service/common/build.gradle.kts b/service/common/build.gradle.kts
index d38838a85..2f5d95825 100644
--- a/service/common/build.gradle.kts
+++ b/service/common/build.gradle.kts
@@ -75,8 +75,6 @@ dependencies {
 
   implementation(libs.auth0.jwt)
 
-  implementation(libs.bouncycastle.bcprov)
-
   implementation(platform(libs.google.cloud.storage.bom))
   implementation("com.google.cloud:google-cloud-storage")
 
diff --git 
a/service/common/src/main/java/org/apache/polaris/service/auth/PemUtils.java 
b/service/common/src/main/java/org/apache/polaris/service/auth/PemUtils.java
index fd4a0be62..375599013 100644
--- a/service/common/src/main/java/org/apache/polaris/service/auth/PemUtils.java
+++ b/service/common/src/main/java/org/apache/polaris/service/auth/PemUtils.java
@@ -20,6 +20,7 @@ package org.apache.polaris.service.auth;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
+import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -36,8 +37,6 @@ import java.security.spec.InvalidKeySpecException;
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.security.spec.X509EncodedKeySpec;
 import java.util.Base64;
-import org.bouncycastle.util.io.pem.PemObject;
-import org.bouncycastle.util.io.pem.PemReader;
 
 public class PemUtils {
 
@@ -46,9 +45,40 @@ public class PemUtils {
       throw new FileNotFoundException(
           String.format("The file '%s' doesn't exist.", 
pemPath.toAbsolutePath()));
     }
-    try (PemReader reader = new PemReader(Files.newBufferedReader(pemPath, 
UTF_8))) {
-      PemObject pemObject = reader.readPemObject();
-      return pemObject.getContent();
+    try (BufferedReader reader = Files.newBufferedReader(pemPath, UTF_8)) {
+      final StringBuilder encodedBuilder = new StringBuilder();
+
+      boolean headerFound = false;
+      boolean footerFound = false;
+
+      String line = reader.readLine();
+      while (line != null) {
+        if (line.startsWith("-----BEGIN")) {
+          headerFound = true;
+        } else if (line.startsWith("-----END")) {
+          footerFound = true;
+          // Stop reading after finding footer
+          break;
+        } else if (!line.isBlank()) {
+          encodedBuilder.append(line);
+        }
+
+        line = reader.readLine();
+      }
+
+      final byte[] parsed;
+      if (headerFound) {
+        if (footerFound) {
+          final String encoded = encodedBuilder.toString();
+          parsed = Base64.getMimeDecoder().decode(encoded);
+        } else {
+          throw new IOException("PEM Footer not found");
+        }
+      } else {
+        throw new IOException("PEM Header not found");
+      }
+
+      return parsed;
     }
   }
 
diff --git 
a/service/common/src/test/java/org/apache/polaris/service/auth/PemUtilsTest.java
 
b/service/common/src/test/java/org/apache/polaris/service/auth/PemUtilsTest.java
new file mode 100644
index 000000000..36e62ceae
--- /dev/null
+++ 
b/service/common/src/test/java/org/apache/polaris/service/auth/PemUtilsTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.polaris.service.auth;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Base64;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+public class PemUtilsTest {
+  private static final String RSA_ALGORITHM = "RSA";
+
+  private static final String PUBLIC_KEY_HEADER = "-----BEGIN PUBLIC KEY-----";
+
+  private static final String PUBLIC_KEY_FOOTER = "-----END PUBLIC KEY-----";
+
+  private static final String PRIVATE_KEY_HEADER = "-----BEGIN PRIVATE 
KEY-----";
+
+  private static final String PRIVATE_KEY_FOOTER = "-----END PRIVATE KEY-----";
+
+  private static final String LINE_SEPARATOR = System.lineSeparator();
+
+  private static final String RSA_PUBLIC_KEY_FILE = "rsa-public-key.pem";
+
+  private static final String RSA_PRIVATE_KEY_FILE = "rsa-private-key.pem";
+
+  private static final String RSA_PUBLIC_KEY_AND_PRIVATE_KEY_FILE = 
"rsa-public-key-pair.pem";
+
+  private static final String EMPTY_FILE = "empty.pem";
+
+  private static final Base64.Encoder encoder = Base64.getMimeEncoder();
+
+  @TempDir private static Path tempDir;
+
+  private static Path rsaRublicKeyPath;
+
+  private static PublicKey rsaPublicKey;
+
+  private static Path rsaPrivateKeyPath;
+
+  private static PrivateKey rsaPrivateKey;
+
+  private static Path rsaPublicKeyAndPrivateKeyPath;
+
+  private static Path emptyFilePath;
+
+  @BeforeAll
+  public static void setKeyPair() throws NoSuchAlgorithmException, IOException 
{
+    final KeyPairGenerator keyPairGenerator = 
KeyPairGenerator.getInstance(RSA_ALGORITHM);
+    final KeyPair keyPair = keyPairGenerator.generateKeyPair();
+    rsaPublicKey = keyPair.getPublic();
+    rsaPrivateKey = keyPair.getPrivate();
+
+    final String publicKeyEncoded = getPublicKeyEncoded(rsaPublicKey);
+    final String privateKeyEncoded = getPrivateKeyEncoded(rsaPrivateKey);
+
+    rsaRublicKeyPath = tempDir.resolve(RSA_PUBLIC_KEY_FILE);
+    Files.writeString(rsaRublicKeyPath, publicKeyEncoded);
+
+    rsaPrivateKeyPath = tempDir.resolve(RSA_PRIVATE_KEY_FILE);
+    Files.writeString(rsaPrivateKeyPath, privateKeyEncoded);
+
+    rsaPublicKeyAndPrivateKeyPath = 
tempDir.resolve(RSA_PUBLIC_KEY_AND_PRIVATE_KEY_FILE);
+    final String rsaPublicKeyAndPrivateKey = publicKeyEncoded + LINE_SEPARATOR 
+ privateKeyEncoded;
+    Files.writeString(rsaPublicKeyAndPrivateKeyPath, 
rsaPublicKeyAndPrivateKey);
+
+    emptyFilePath = tempDir.resolve(EMPTY_FILE);
+    Files.write(emptyFilePath, new byte[0]);
+  }
+
+  @Test
+  public void testReadPublicKeyFromFileRSA() throws IOException {
+    final PublicKey publicKeyRead = 
PemUtils.readPublicKeyFromFile(rsaRublicKeyPath, RSA_ALGORITHM);
+
+    assertEquals(rsaPublicKey, publicKeyRead);
+  }
+
+  @Test
+  public void testReadPrivateKeyFromFileRSA() throws IOException {
+    final PrivateKey privateKeyRead =
+        PemUtils.readPrivateKeyFromFile(rsaPrivateKeyPath, RSA_ALGORITHM);
+
+    assertEquals(rsaPrivateKey, privateKeyRead);
+  }
+
+  @Test
+  public void testReadPublicKeyFromFileRSAWithPrivateKeyIgnored() throws 
IOException {
+    final PublicKey publicKeyRead =
+        PemUtils.readPublicKeyFromFile(rsaPublicKeyAndPrivateKeyPath, 
RSA_ALGORITHM);
+
+    assertEquals(rsaPublicKey, publicKeyRead);
+  }
+
+  @Test
+  public void testReadEmptyFIle() {
+    assertThrows(
+        IOException.class, () -> PemUtils.readPublicKeyFromFile(emptyFilePath, 
RSA_ALGORITHM));
+  }
+
+  private static String getPublicKeyEncoded(final PublicKey publicKey) {
+    final StringBuilder builder = new StringBuilder();
+
+    builder.append(PUBLIC_KEY_HEADER);
+    builder.append(LINE_SEPARATOR);
+
+    final byte[] publicKeyEncoded = publicKey.getEncoded();
+    final String encoded = encoder.encodeToString(publicKeyEncoded);
+    builder.append(encoded);
+    builder.append(LINE_SEPARATOR);
+
+    builder.append(PUBLIC_KEY_FOOTER);
+    builder.append(LINE_SEPARATOR);
+
+    return builder.toString();
+  }
+
+  private static String getPrivateKeyEncoded(final PrivateKey privateKey) {
+    final StringBuilder builder = new StringBuilder();
+
+    builder.append(PRIVATE_KEY_HEADER);
+    builder.append(LINE_SEPARATOR);
+
+    final byte[] privateKeyEncoded = privateKey.getEncoded();
+    final String encoded = encoder.encodeToString(privateKeyEncoded);
+    builder.append(encoded);
+    builder.append(LINE_SEPARATOR);
+
+    builder.append(PRIVATE_KEY_FOOTER);
+    builder.append(LINE_SEPARATOR);
+
+    return builder.toString();
+  }
+}

Reply via email to