Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ public Producer createProducer() throws Exception {
return new PQCProducer(this);
}

/**
* Registers the BouncyCastle providers if they are not installed yet.
* <p>
* The providers are deliberately never removed again. {@link Security} is JVM-global and shared with every other
* endpoint, every other {@code CamelContext} and all non-Camel code in the same JVM, and nothing records which
* component installed a given provider. Removing one on stop therefore tears it out from under unrelated users -
* including the sibling {@code pqc} endpoint of the same route and the statically initialised
* {@code PQCDefault*Material} classes of this very component, which also register BouncyCastle and never remove it.
*/
@Override
public void doStart() throws Exception {
super.doStart();
Expand All @@ -71,17 +80,6 @@ public void doStart() throws Exception {
}
}

@Override
public void doStop() throws Exception {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) != null) {
Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
}
if (Security.getProvider(BouncyCastlePQCProvider.PROVIDER_NAME) != null) {
Security.removeProvider(BouncyCastlePQCProvider.PROVIDER_NAME);
}
super.doStop();
}

public PQCConfiguration getConfiguration() {
return configuration;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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.camel.component.pqc;

import org.apache.camel.CamelContext;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
import org.junit.jupiter.api.Test;

import static java.security.Security.getProvider;
import static org.junit.jupiter.api.Assertions.assertNotNull;

/**
* The BouncyCastle providers live in the JVM-global {@link java.security.Security} registry, so a PQC endpoint must
* never remove them on stop - other endpoints, other CamelContexts and unrelated code in the same JVM rely on them.
*/
public class PQCEndpointProviderLifecycleTest {

@Test
void stoppingTheContextKeepsTheProvidersRegistered() throws Exception {
try (CamelContext context = new DefaultCamelContext()) {
context.addRoutes(new RouteBuilder() {
@Override
public void configure() {
from("direct:sign").to("pqc:sign?operation=sign&signatureAlgorithm=MLDSA");
}
});
context.start();

assertNotNull(getProvider(BouncyCastleProvider.PROVIDER_NAME), "BC must be registered while running");
assertNotNull(getProvider(BouncyCastlePQCProvider.PROVIDER_NAME), "BCPQC must be registered while running");

context.stop();

assertNotNull(getProvider(BouncyCastleProvider.PROVIDER_NAME),
"stopping a PQC endpoint must not de-register the JVM-global BC provider");
assertNotNull(getProvider(BouncyCastlePQCProvider.PROVIDER_NAME),
"stopping a PQC endpoint must not de-register the JVM-global BCPQC provider");
}
}

@Test
void stoppingOneEndpointLeavesTheProviderForTheOther() throws Exception {
try (CamelContext context = new DefaultCamelContext()) {
context.addRoutes(new RouteBuilder() {
@Override
public void configure() {
from("direct:a").routeId("a").to("pqc:a?operation=sign&signatureAlgorithm=MLDSA");
from("direct:b").routeId("b").to("pqc:b?operation=sign&signatureAlgorithm=MLDSA");
}
});
context.start();

// stopping the first route disposes its endpoint. The second route keeps its own Signature either way,
// so what this pins is the provider: on main the stop de-registers it for the whole JVM.
context.getRouteController().stopRoute("a");
context.removeEndpoints("pqc:a*");

context.createProducerTemplate().sendBody("direct:b", "Hello");

assertNotNull(getProvider(BouncyCastleProvider.PROVIDER_NAME),
"a sibling PQC endpoint must still see the BC provider");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,18 @@ values such as `AES`, which are pure ASCII. A deployment running on a JVM whose
name identically to UTF-8 would, however, derive a different key than the previous version, and could not decrypt
material produced by it.

=== camel-pqc - the BouncyCastle providers stay registered when an endpoint stops

Stopping a `pqc` endpoint no longer removes the `BC` and `BCPQC` providers from `java.security.Security`.

`java.security.Security` is JVM-global, and the endpoint only added a provider when it was absent, so stopping one
endpoint could remove a provider it had not installed - breaking the other `pqc` endpoint of the same route, other
`CamelContext` instances, components such as camel-crypto, camel-ssh and camel-xmlsecurity, and any non-Camel code in
the same JVM.

An application that relied on the providers being de-registered on shutdown must now remove them itself with
`Security.removeProvider("BC")` and `Security.removeProvider("BCPQC")`.

=== camel-jbang - camel validate normalize writes one file per input file to an output directory

`camel validate normalize <files> --output=<dir>` now writes one normalized file per input file into the
Expand Down
Loading