|
| 1 | +# Bouncy Castle Java API How To |
| 2 | +## Using Bouncy Castle with GraalVM Native Image |
| 3 | +### Problem: Provider Not Registered at Build Time with `UnsupportedFeatureError` Exception |
| 4 | +#### Error message |
| 5 | +```text |
| 6 | +Trying to verify a provider that was not registered at build time: BC version... |
| 7 | +``` |
| 8 | +#### Cause: |
| 9 | +Bouncy Castle security provider isn't properly registered during GraalVM native image build process. |
| 10 | + |
| 11 | +### Solution 1: Static Initializer Approach (No GraalVM SDK) |
| 12 | +#### Step 1. Create Initializer Class |
| 13 | +```java |
| 14 | +package com.yourpackage.crypto; // ← Replace with your actual package |
| 15 | + |
| 16 | +import org.bouncycastle.jce.provider.BouncyCastleProvider; |
| 17 | +import java.security.Security; |
| 18 | + |
| 19 | +public class BCInitializer { |
| 20 | + static { |
| 21 | + // Force provider registration during image build |
| 22 | + Security.addProvider(new BouncyCastleProvider()); |
| 23 | + } |
| 24 | +} |
| 25 | +``` |
| 26 | + |
| 27 | +#### Step 2. And then in the native-image build configuration |
| 28 | +For Maven (`pom.xml`) |
| 29 | +```xml |
| 30 | +<plugin> |
| 31 | + <groupId>org.graalvm.buildtools</groupId> |
| 32 | + <artifactId>native-maven-plugin</artifactId> |
| 33 | + <version>0.9.28</version> |
| 34 | + <configuration> |
| 35 | + <buildArgs> |
| 36 | + <!-- Initialize Bouncy Castle and our initializer --> |
| 37 | + <arg>--initialize-at-build-time=org.bouncycastle,com.yourpackage.crypto.BCInitializer</arg> |
| 38 | + <!-- Required for SecureRandom components --> |
| 39 | + <arg>--initialize-at-run-time=org.bouncycastle.jcajce.provider.drbg.DRBG$Default,org.bouncycastle.jcajce.provider.drbg.DRBG$NonceAndIV</arg> |
| 40 | + </buildArgs> |
| 41 | + </configuration> |
| 42 | +</plugin> |
| 43 | +``` |
| 44 | + |
| 45 | +For Gradle (`build.gradle`), |
| 46 | +```gradle |
| 47 | + buildArgs.add('--initialize-at-build-time=com.yourpackage.crypto.BCInitializer') |
| 48 | + buildArgs.add("--initialize-at-run-time=org.bouncycastle.jcajce.provider.drbg.DRBG\$Default,org.bouncycastle.jcajce.provider.drbg.DRBG\$NonceAndIV") |
| 49 | +``` |
| 50 | +# Key Configuration |
| 51 | + |
| 52 | +| Argument | Purpose | |
| 53 | +| ------------------------------- |-----------------------------------------------------------------| |
| 54 | +| `--initialize-at-build-time` | Forces inclusion of BC classes and triggers static initializer. | |
| 55 | +| `--initialize-at-run-time` | Solves stateful SecureRandom initialization issues. | |
| 56 | +|`--enable-all-security-services` | (optional) Enables JCE security infrastructure | |
| 57 | + |
| 58 | + |
| 59 | +### Solution 2: GraalVM Feature Approach (With SDK) |
| 60 | + |
| 61 | +#### Step 1: Create a Native Image Feature |
| 62 | +```java |
| 63 | +package com.yourpackage.crypto; // ← Replace with your actual package |
| 64 | + |
| 65 | +import org.bouncycastle.jce.provider.BouncyCastleProvider; |
| 66 | +import org.graalvm.nativeimage.hosted.Feature; |
| 67 | + |
| 68 | +import java.security.Security; |
| 69 | + |
| 70 | +/** |
| 71 | + * A GraalVM Feature that registers the Bouncy Castle provider. |
| 72 | + * This is required so that native image builds verify and include the provider. |
| 73 | + */ |
| 74 | +public class BouncyCastleFeature implements Feature { |
| 75 | + |
| 76 | + @Override |
| 77 | + public void afterRegistration(AfterRegistrationAccess access) { |
| 78 | + // Register the Bouncy Castle provider |
| 79 | + Security.addProvider(new BouncyCastleProvider()); |
| 80 | + } |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +#### Step 2: Configure Dependencies and Build |
| 85 | +##### 2.1 add dependency |
| 86 | +```xml |
| 87 | +<dependency> |
| 88 | + <groupId>org.graalvm.sdk</groupId> |
| 89 | + <artifactId>graal-sdk</artifactId> |
| 90 | + <version>21.0.0</version> <!-- Match your GraalVM version --> |
| 91 | + <scope>provided</scope> |
| 92 | +</dependency> |
| 93 | +``` |
| 94 | +##### 2.2 add plugin |
| 95 | +```xml |
| 96 | +<plugin> |
| 97 | + <groupId>org.graalvm.buildtools</groupId> |
| 98 | + <artifactId>native-maven-plugin</artifactId> |
| 99 | + <version>0.9.28</version> |
| 100 | + <configuration> |
| 101 | + <buildArgs> |
| 102 | + <arg>--features=com.yourpackage.crypto.BouncyCastleFeature</arg> <!-- replace with correct package path --> |
| 103 | + <arg>--initialize-at-build-time=org.bouncycastle</arg> |
| 104 | + <arg>--initialize-at-run-time=org.bouncycastle.jcajce.provider.drbg.DRBG$Default,org.bouncycastle.jcajce.provider.drbg.DRBG$NonceAndIV</arg> |
| 105 | + </buildArgs> |
| 106 | + </configuration> |
| 107 | +</plugin> |
| 108 | +``` |
| 109 | +Key Configuration Explanations: |
| 110 | +`--features=...` |
| 111 | +- Registers custom feature class that adds BouncyCastle provider at build time |
| 112 | +- Required for JCE security provider verification |
| 113 | + |
| 114 | +### Troubleshooting |
| 115 | +#### Common Issues |
| 116 | +##### Classpath Conflicts: |
| 117 | + |
| 118 | +```text |
| 119 | +Error: Class-path entry contains class from image builder |
| 120 | +``` |
| 121 | +Fix: Add `-H:+AllowDeprecatedBuilderClassesOnImageClasspath` (temporary) or ensure graal-sdk has provided scope |
| 122 | + |
| 123 | +##### Missing Algorithms: |
| 124 | +Example of the error message: |
| 125 | +```text |
| 126 | +No such algorithm: AES/CBC/PKCS5Padding |
| 127 | +``` |
| 128 | + |
| 129 | +Fix: Verify `--initialize-at-build-time` includes `org.bouncycastle` |
0 commit comments