Skip to content

Commit 5332e4f

Browse files
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
1 parent b422f39 commit 5332e4f

File tree

5 files changed

+195
-10
lines changed

5 files changed

+195
-10
lines changed

gradle/libs.versions.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ assertj-core = { module = "org.assertj:assertj-core", version = "3.27.3" }
4141
auth0-jwt = { module = "com.auth0:java-jwt", version = "4.5.0" }
4242
awssdk-bom = { module = "software.amazon.awssdk:bom", version = "2.31.45" }
4343
azuresdk-bom = { module = "com.azure:azure-sdk-bom", version = "1.2.34" }
44-
bouncycastle-bcprov = { module = "org.bouncycastle:bcprov-jdk18on", version = "1.80" }
4544
caffeine = { module = "com.github.ben-manes.caffeine:caffeine", version = "3.2.0" }
4645
commons-codec1 = { module = "commons-codec:commons-codec", version = "1.18.0" }
4746
commons-lang3 = { module = "org.apache.commons:commons-lang3", version = "3.17.0" }

quarkus/service/build.gradle.kts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,6 @@ dependencies {
7070

7171
implementation(libs.auth0.jwt)
7272

73-
implementation(libs.bouncycastle.bcprov)
74-
7573
compileOnly(libs.jakarta.annotation.api)
7674
compileOnly(libs.spotbugs.annotations)
7775

service/common/build.gradle.kts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,6 @@ dependencies {
7575

7676
implementation(libs.auth0.jwt)
7777

78-
implementation(libs.bouncycastle.bcprov)
79-
8078
implementation(platform(libs.google.cloud.storage.bom))
8179
implementation("com.google.cloud:google-cloud-storage")
8280

service/common/src/main/java/org/apache/polaris/service/auth/PemUtils.java

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import static java.nio.charset.StandardCharsets.UTF_8;
2222

23+
import java.io.BufferedReader;
2324
import java.io.BufferedWriter;
2425
import java.io.FileNotFoundException;
2526
import java.io.IOException;
@@ -36,8 +37,6 @@
3637
import java.security.spec.PKCS8EncodedKeySpec;
3738
import java.security.spec.X509EncodedKeySpec;
3839
import java.util.Base64;
39-
import org.bouncycastle.util.io.pem.PemObject;
40-
import org.bouncycastle.util.io.pem.PemReader;
4140

4241
public class PemUtils {
4342

@@ -46,9 +45,40 @@ private static byte[] parsePEMFile(Path pemPath) throws IOException {
4645
throw new FileNotFoundException(
4746
String.format("The file '%s' doesn't exist.", pemPath.toAbsolutePath()));
4847
}
49-
try (PemReader reader = new PemReader(Files.newBufferedReader(pemPath, UTF_8))) {
50-
PemObject pemObject = reader.readPemObject();
51-
return pemObject.getContent();
48+
try (BufferedReader reader = Files.newBufferedReader(pemPath, UTF_8)) {
49+
final StringBuilder encodedBuilder = new StringBuilder();
50+
51+
boolean headerFound = false;
52+
boolean footerFound = false;
53+
54+
String line = reader.readLine();
55+
while (line != null) {
56+
if (line.startsWith("-----BEGIN")) {
57+
headerFound = true;
58+
} else if (line.startsWith("-----END")) {
59+
footerFound = true;
60+
// Stop reading after finding footer
61+
break;
62+
} else if (!line.isBlank()) {
63+
encodedBuilder.append(line);
64+
}
65+
66+
line = reader.readLine();
67+
}
68+
69+
final byte[] parsed;
70+
if (headerFound) {
71+
if (footerFound) {
72+
final String encoded = encodedBuilder.toString();
73+
parsed = Base64.getMimeDecoder().decode(encoded);
74+
} else {
75+
throw new IOException("PEM Footer not found");
76+
}
77+
} else {
78+
throw new IOException("PEM Header not found");
79+
}
80+
81+
return parsed;
5282
}
5383
}
5484

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.polaris.service.auth;
20+
21+
import static org.junit.jupiter.api.Assertions.assertEquals;
22+
import static org.junit.jupiter.api.Assertions.assertThrows;
23+
24+
import java.io.IOException;
25+
import java.nio.file.Files;
26+
import java.nio.file.Path;
27+
import java.security.KeyPair;
28+
import java.security.KeyPairGenerator;
29+
import java.security.NoSuchAlgorithmException;
30+
import java.security.PrivateKey;
31+
import java.security.PublicKey;
32+
import java.util.Base64;
33+
import org.junit.jupiter.api.BeforeAll;
34+
import org.junit.jupiter.api.Test;
35+
import org.junit.jupiter.api.io.TempDir;
36+
37+
public class PemUtilsTest {
38+
private static final String RSA_ALGORITHM = "RSA";
39+
40+
private static final String PUBLIC_KEY_HEADER = "-----BEGIN PUBLIC KEY-----";
41+
42+
private static final String PUBLIC_KEY_FOOTER = "-----END PUBLIC KEY-----";
43+
44+
private static final String PRIVATE_KEY_HEADER = "-----BEGIN PRIVATE KEY-----";
45+
46+
private static final String PRIVATE_KEY_FOOTER = "-----END PRIVATE KEY-----";
47+
48+
private static final String LINE_SEPARATOR = System.lineSeparator();
49+
50+
private static final String RSA_PUBLIC_KEY_FILE = "rsa-public-key.pem";
51+
52+
private static final String RSA_PRIVATE_KEY_FILE = "rsa-private-key.pem";
53+
54+
private static final String RSA_PUBLIC_KEY_AND_PRIVATE_KEY_FILE = "rsa-public-key-pair.pem";
55+
56+
private static final String EMPTY_FILE = "empty.pem";
57+
58+
private static final Base64.Encoder encoder = Base64.getMimeEncoder();
59+
60+
@TempDir private static Path tempDir;
61+
62+
private static Path rsaRublicKeyPath;
63+
64+
private static PublicKey rsaPublicKey;
65+
66+
private static Path rsaPrivateKeyPath;
67+
68+
private static PrivateKey rsaPrivateKey;
69+
70+
private static Path rsaPublicKeyAndPrivateKeyPath;
71+
72+
private static Path emptyFilePath;
73+
74+
@BeforeAll
75+
public static void setKeyPair() throws NoSuchAlgorithmException, IOException {
76+
final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
77+
final KeyPair keyPair = keyPairGenerator.generateKeyPair();
78+
rsaPublicKey = keyPair.getPublic();
79+
rsaPrivateKey = keyPair.getPrivate();
80+
81+
final String publicKeyEncoded = getPublicKeyEncoded(rsaPublicKey);
82+
final String privateKeyEncoded = getPrivateKeyEncoded(rsaPrivateKey);
83+
84+
rsaRublicKeyPath = tempDir.resolve(RSA_PUBLIC_KEY_FILE);
85+
Files.writeString(rsaRublicKeyPath, publicKeyEncoded);
86+
87+
rsaPrivateKeyPath = tempDir.resolve(RSA_PRIVATE_KEY_FILE);
88+
Files.writeString(rsaPrivateKeyPath, privateKeyEncoded);
89+
90+
rsaPublicKeyAndPrivateKeyPath = tempDir.resolve(RSA_PUBLIC_KEY_AND_PRIVATE_KEY_FILE);
91+
final String rsaPublicKeyAndPrivateKey = publicKeyEncoded + LINE_SEPARATOR + privateKeyEncoded;
92+
Files.writeString(rsaPublicKeyAndPrivateKeyPath, rsaPublicKeyAndPrivateKey);
93+
94+
emptyFilePath = tempDir.resolve(EMPTY_FILE);
95+
Files.write(emptyFilePath, new byte[0]);
96+
}
97+
98+
@Test
99+
public void testReadPublicKeyFromFileRSA() throws IOException {
100+
final PublicKey publicKeyRead = PemUtils.readPublicKeyFromFile(rsaRublicKeyPath, RSA_ALGORITHM);
101+
102+
assertEquals(rsaPublicKey, publicKeyRead);
103+
}
104+
105+
@Test
106+
public void testReadPrivateKeyFromFileRSA() throws IOException {
107+
final PrivateKey privateKeyRead =
108+
PemUtils.readPrivateKeyFromFile(rsaPrivateKeyPath, RSA_ALGORITHM);
109+
110+
assertEquals(rsaPrivateKey, privateKeyRead);
111+
}
112+
113+
@Test
114+
public void testReadPublicKeyFromFileRSAWithPrivateKeyIgnored() throws IOException {
115+
final PublicKey publicKeyRead =
116+
PemUtils.readPublicKeyFromFile(rsaPublicKeyAndPrivateKeyPath, RSA_ALGORITHM);
117+
118+
assertEquals(rsaPublicKey, publicKeyRead);
119+
}
120+
121+
@Test
122+
public void testReadEmptyFIle() {
123+
assertThrows(
124+
IOException.class, () -> PemUtils.readPublicKeyFromFile(emptyFilePath, RSA_ALGORITHM));
125+
}
126+
127+
private static String getPublicKeyEncoded(final PublicKey publicKey) {
128+
final StringBuilder builder = new StringBuilder();
129+
130+
builder.append(PUBLIC_KEY_HEADER);
131+
builder.append(LINE_SEPARATOR);
132+
133+
final byte[] publicKeyEncoded = publicKey.getEncoded();
134+
final String encoded = encoder.encodeToString(publicKeyEncoded);
135+
builder.append(encoded);
136+
builder.append(LINE_SEPARATOR);
137+
138+
builder.append(PUBLIC_KEY_FOOTER);
139+
builder.append(LINE_SEPARATOR);
140+
141+
return builder.toString();
142+
}
143+
144+
private static String getPrivateKeyEncoded(final PrivateKey privateKey) {
145+
final StringBuilder builder = new StringBuilder();
146+
147+
builder.append(PRIVATE_KEY_HEADER);
148+
builder.append(LINE_SEPARATOR);
149+
150+
final byte[] privateKeyEncoded = privateKey.getEncoded();
151+
final String encoded = encoder.encodeToString(privateKeyEncoded);
152+
builder.append(encoded);
153+
builder.append(LINE_SEPARATOR);
154+
155+
builder.append(PRIVATE_KEY_FOOTER);
156+
builder.append(LINE_SEPARATOR);
157+
158+
return builder.toString();
159+
}
160+
}

0 commit comments

Comments
 (0)