diff --git a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliCommand.kt b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliCommand.kt index c34611d90..fb587f3e3 100644 --- a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliCommand.kt +++ b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliCommand.kt @@ -219,7 +219,7 @@ abstract class CliCommand(protected val cliOptions: CliBaseOptions) { var certsAdded = false if (Files.isDirectory(caCertsDir)) { Files.list(caCertsDir) - .filter { it.isRegularFile() } + .filter { it.isRegularFile() && !it.fileName.toString().startsWith(".") } .forEach { cert -> certsAdded = true addCertificates(cert) diff --git a/pkl-core/src/main/java/org/pkl/core/http/JdkHttpClient.java b/pkl-core/src/main/java/org/pkl/core/http/JdkHttpClient.java index 28b49e401..5445ba4d8 100644 --- a/pkl-core/src/main/java/org/pkl/core/http/JdkHttpClient.java +++ b/pkl-core/src/main/java/org/pkl/core/http/JdkHttpClient.java @@ -19,6 +19,7 @@ import com.google.errorprone.annotations.ThreadSafe; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.PushbackInputStream; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -37,11 +38,10 @@ import java.security.SecureRandom; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; import java.time.Duration; import java.util.ArrayList; -import java.util.Collection; import java.util.List; +import java.util.Objects; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; @@ -177,17 +177,40 @@ final class JdkHttpClient implements HttpClient { CertificateFactory factory, InputStream stream, Object source) { - Collection certificates; + var input = new PushbackInputStream(stream); + try { - //noinspection unchecked - certificates = (Collection) factory.generateCertificates(stream); - } catch (CertificateException e) { + var peekByte = input.read(); + if (peekByte == -1) { + throw new HttpClientException(ErrorMessages.create("emptyCertFile", source)); + } else { + input.unread(peekByte); + } + } catch (IOException e) { throw new HttpClientException( ErrorMessages.create("cannotParseCertFile", source, Exceptions.getRootReason(e))); } - if (certificates.isEmpty()) { - throw new HttpClientException(ErrorMessages.create("emptyCertFile", source)); + + var first = true; + while (true) { + try { + anchors.add(factory.generateCertificate(input)); + } catch (CertificateException e) { + if (e.getCause() instanceof IOException ioExc) { + if (Objects.equals(ioExc.getMessage(), "Empty input")) { + if (first) { + throw new HttpClientException( + ErrorMessages.create("cannotParseCertFile", source, "No certificate data found")); + } + break; + } + if (Objects.equals(ioExc.getMessage(), "Duplicate extensions not allowed")) continue; + } + throw new HttpClientException( + ErrorMessages.create("cannotParseCertFile", source, Exceptions.getRootReason(e))); + } finally { + first = false; + } } - anchors.addAll(certificates); } }