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
@@ -1,6 +1,8 @@
package io.quarkus.security.runtime.graal;

import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;
import java.util.Set;
import java.util.function.BooleanSupplier;
Expand All @@ -9,13 +11,18 @@
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.RecomputeFieldValue;

import io.quarkus.security.runtime.SecurityProviderUtils;

final class BouncyCastlePackages {
static final String ORG_BOUNCYCASTLE_CRYPTO_PACKAGE = "org.bouncycastle.crypto";
static final String ORG_BOUNCYCASTLE_CRYPTO_FIPS_PACKAGE = "org.bouncycastle.crypto.fips";
static final String ORG_BOUNCYCASTLE_CRYPTO_INTERNAL_PACKAGE = "org.bouncycastle.crypto.internal";
static final String ORG_BOUNCYCASTLE_CRYPTO_GENERAL_PACKAGE = "org.bouncycastle.crypto.general";
static final String ORG_BOUNCYCASTLE_OPENSSL_PACKAGE = "org.bouncycastle.openssl";
static final Set<String> PACKAGES = Arrays.asList(Package.getPackages()).stream()
.map(p -> p.getName()).filter(p -> p.startsWith(ORG_BOUNCYCASTLE_CRYPTO_PACKAGE)).collect(Collectors.toSet());
.map(Package::getName)
.filter(p -> p.startsWith(ORG_BOUNCYCASTLE_CRYPTO_PACKAGE) || p.startsWith(ORG_BOUNCYCASTLE_OPENSSL_PACKAGE))
.collect(Collectors.toSet());
}

@com.oracle.svm.core.annotate.TargetClass(className = "org.bouncycastle.crypto.general.DSA$1", onlyWith = BouncyCastleCryptoGeneral.class)
Expand Down Expand Up @@ -104,6 +111,59 @@ final class Target_org_bouncycastle_math_ec_ECPoint {
private static SecureRandom testRandom;
}

// TODO: this should be removed when https://github.com/netty/netty/issues/14826 is addressed
// this substitution can be removed when io.quarkus.it.bouncycastle.BouncyCastleITCase#loadNettySslContext passes
@com.oracle.svm.core.annotate.TargetClass(className = "io.netty.handler.ssl.BouncyCastlePemReader", onlyWith = NettySslBountyCastleSupportRequired.class)
final class Target_io_netty_handler_ssl_BouncyCastlePemReader {
@Alias
private static volatile Provider bcProvider;
@Alias
private static volatile boolean attemptedLoading;
@Alias
private static volatile Throwable unavailabilityCause;

@com.oracle.svm.core.annotate.Substitute
public static boolean isAvailable() {
if (!attemptedLoading) {
// do what io.netty.handler.ssl.BouncyCastlePemReader.tryLoading does
// however Netty creates a new provider instance that doesn't have all the services
// while we take already created provider with all registered services
bcProvider = Security.getProvider(SecurityProviderUtils.BOUNCYCASTLE_PROVIDER_NAME);
if (bcProvider == null) {
bcProvider = Security.getProvider(SecurityProviderUtils.BOUNCYCASTLE_FIPS_PROVIDER_NAME);
}
if (bcProvider == null) {
tryLoading();
} else {
attemptedLoading = true;
}
}
return unavailabilityCause == null;
}

@Alias
private static void tryLoading() {

}
}

class NettySslBountyCastleSupportRequired implements BooleanSupplier {
@Override
public boolean getAsBoolean() {
// this package is used by the BouncyCastlePemReader and present in 'org.bouncycastle:bcpkix-jdk18on'
if (BouncyCastlePackages.PACKAGES.contains(BouncyCastlePackages.ORG_BOUNCYCASTLE_OPENSSL_PACKAGE)) {
try {
Class.forName("io.netty.handler.ssl.BouncyCastlePemReader", false,
Thread.currentThread().getContextClassLoader());
return true;
} catch (Throwable e) {
// class not available
}
}
return false;
}
}

class BouncyCastleCryptoFips implements BooleanSupplier {
@Override
public boolean getAsBoolean() {
Expand All @@ -123,4 +183,4 @@ class BouncyCastleCryptoInternal implements BooleanSupplier {
public boolean getAsBoolean() {
return BouncyCastlePackages.PACKAGES.contains(BouncyCastlePackages.ORG_BOUNCYCASTLE_CRYPTO_INTERNAL_PACKAGE);
}
}
}
6 changes: 6 additions & 0 deletions integration-tests/bouncycastle/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@
<artifactId>quarkus-security</artifactId>
</dependency>

<!-- dependency for Netty SSL context loading test -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
</dependency>

<!-- test dependencies -->
<dependency>
<groupId>org.bouncycastle</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;

import io.netty.handler.ssl.SslContextBuilder;

@Path("/jca")
public class BouncyCastleEndpoint {

Expand Down Expand Up @@ -103,4 +105,15 @@ public String readRsaPrivatePemKey() throws Exception {
return ecPrivateKey.getPrivateExponent() != null ? "success" : "failure";
}
}

@GET
@Path("loadNettySslContext")
public String loadNettySslContext() throws Exception {
var classLoader = Thread.currentThread().getContextClassLoader();
try (var privateKey = classLoader.getResourceAsStream("pkcs1-key.pem");
var certificate = classLoader.getResourceAsStream("certificate.pem")) {
var sslcontext = SslContextBuilder.forClient().keyManager(certificate, privateKey).build();
return Arrays.toString(sslcontext.cipherSuites().toArray());
}
}
}
15 changes: 15 additions & 0 deletions integration-tests/bouncycastle/src/main/resources/certificate.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICbDCCAdWgAwIBAgIJALeUXoWyGYBYMA0GCSqGSIb3DQEBBQUAMCoxGzAZBgNV
BAMMEmh4NTA5IFRlc3QgUm9vdCBDQTELMAkGA1UEBhMCU0UwHhcNMDcxMTE1MDY1
ODU2WhcNMTcxMTEyMDY1ODU2WjAqMRswGQYDVQQDDBJoeDUwOSBUZXN0IFJvb3Qg
Q0ExCzAJBgNVBAYTAlNFMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHcvJb
yJXPhM9HHq1hU6d2Cu1fW9o1CvObirn1SNZg+pTnQgO9Lv4VjQQfltNK0aovyLJa
UdbAbsRCfH+79YY2tU76x8aXpUri0DfUv5PGscIZzW7WULaaXxBgHo1owzmhc1Qj
F9JDEurJXGFEZaDsPcEwY40RjrKDL8SXzEoEwwIDAQABo4GZMIGWMB0GA1UdDgQW
BBSM5w21xd5phXUsCKHeUxUwnKHoADBaBgNVHSMEUzBRgBSM5w21xd5phXUsCKHe
UxUwnKHoAKEupCwwKjEbMBkGA1UEAwwSaHg1MDkgVGVzdCBSb290IENBMQswCQYD
VQQGEwJTRYIJALeUXoWyGYBYMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgHmMA0G
CSqGSIb3DQEBBQUAA4GBAIBa6mq1aytlbhixD6q4PROg7P1OGX6nr5CkC96CC+Xp
5UTLZEVIddkrBswNAAS0p5eEorO8xD9eT5ztZ0oYITymsO1sEIfDLks+LhdBoyF7
TX24INRwjlqsC8UlbRFoClxIMNhrMwcC3oZ4oLddV2OmA0IOG6yHXvEOQq0sTotr
-----END CERTIFICATE-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIBdVHnnzZmJm+Z1HAYYOZlvnB8Dj8kVx9XBH+6UCWlGUoAoGCCqGSM49
AwEHoUQDQgAEThPp/xgEov0mKg2s0GII76VkZAcCc//3quAqzg+PuFKXgruaF7Kn
3tuQVWHBlyZX56oOstUYQh3418Z3Gb1+yw==
-----END EC PRIVATE KEY-----
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.hamcrest.Matchers.equalTo;

import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;
Expand Down Expand Up @@ -89,4 +90,18 @@ public void readRsaPrivatePemKey() {
.statusCode(200)
.body(equalTo("success"));
}

@Test
public void loadNettySslContext() {
// this tests that io.netty.handler.ssl.BouncyCastlePemReader used by Netty SSL context
// works in native; it is used when 'org.bouncycastle:bcpkix-jdk18on' dependency is present
// even for other standards, not just PKCS1 used by this test, however for these the test could pass
// because of Netty SSL context has other strategies, not just BC
RestAssured.given()
.when()
.get("/jca/loadNettySslContext")
.then()
.statusCode(200)
.body(Matchers.notNullValue());
}
}
Loading