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();
+ }
+}