Typical situation, you managed to compiled your java app which uses BouncyCastle as native app. But when you run it, it complained about missing alogrithm. Indeed, it works when running under the JVM
Below is the jorney of how I dive through BouncyCastle source code and understand what went wrong.
You probably see similar error when running the native binary
bash-4.2# ./test
Exception in thread "main" java.security.NoSuchAlgorithmException: 1.2.840.113549.1.1.1 KeyFactory not available
at java.security.KeyFactory.<init>(KeyFactory.java:138)
at java.security.KeyFactory.getInstance(KeyFactory.java:172)
at test.main(test.java:35)
Tracing the BC source, it is easy to locate how algorithms are loaded via reflection
To load the required algorithm, add the corresponding Mappings class in relfection config.
[
{
"name" : "org.bouncycastle.jcajce.provider.asymmetric.RSA$Mappings",
"allPublicConstructors" : true,
"allPublicMethods" : true
}
]
Let's trace 1.2.840.113549.1.1.1
~/bc-java$ ack '1.2.840.113549.1.1.1 ' --java -B1 -A1
core/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
13- ASN1ObjectIdentifier pkcs_1 = new ASN1ObjectIdentifier("1.2.840.113549.1.1");
14: /** PKCS#1: 1.2.840.113549.1.1.1 */
15- ASN1ObjectIdentifier rsaEncryption = pkcs_1.branch("1");
And realize this is registered at:
~/bc-java$ ack PKCSObjectIdentifiers.rsaEncryption --java|grep register
prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/RSA.java:86: registerOid(provider, PKCSObjectIdentifiers.rsaEncryption, "RSA", keyFact);
prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/RSA.java:91: registerOidAlgorithmParameters(provider, PKCSObjectIdentifiers.rsaEncryption, "RSA");
The keyFact instance looks interesting, let's understand what it is:
~/bc-java$ ack keyFact prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/RSA.java
AsymmetricKeyInfoConverter keyFact = new KeyFactorySpi();
registerOid(provider, PKCSObjectIdentifiers.rsaEncryption, "RSA", keyFact);
registerOid(provider, X509ObjectIdentifiers.id_ea_rsa, "RSA", keyFact);
registerOid(provider, PKCSObjectIdentifiers.id_RSAES_OAEP, "RSA", keyFact);
registerOid(provider, PKCSObjectIdentifiers.id_RSASSA_PSS, "RSA", keyFact);
What if we include KeyFactorySpi to reflection config
[
{
"name" : "org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyFactorySpi",
"allPublicConstructors" : true,
"allPublicMethods" : true
},
{
"name" : "org.bouncycastle.jcajce.provider.asymmetric.RSA$Mappings",
"allPublicConstructors" : true,
"allPublicMethods" : true
}
]
bash-4.2# make native;./test
native-image -cp bcprov-jdk15on-164.jar:. -H:ReflectionConfigurationFiles=reflection-config.json test
Build on Server(pid: 243, port: 37325)
[test:243] classlist: 1,930.93 ms
[test:243] (cap): 979.95 ms
[test:243] setup: 1,390.11 ms
[test:243] (typeflow): 4,624.12 ms
[test:243] (objects): 3,530.20 ms
[test:243] (features): 152.29 ms
[test:243] analysis: 8,445.48 ms
[test:243] (clinit): 163.43 ms
[test:243] universe: 335.92 ms
[test:243] (parse): 1,058.46 ms
[test:243] (inline): 1,252.96 ms
[test:243] (compile): 4,617.22 ms
[test:243] compile: 7,276.65 ms
[test:243] image: 500.08 ms
[test:243] write: 104.66 ms
[test:243] [total]: 20,058.31 ms
finished
To conclude, a good understanding of BouncyCastle internals helps writing the reflection config as input for GraalVM's native-image
bash-4.2# ./test -|grep KeyFactory|grep RSA
Service Type: KeyFactory Algorithm RSA
Service Type: KeyFactory Algorithm RSA
Service Type: KeyFactory Algorithm RSA
java -agentlib:native-image-agent=config-output-dir=
see https://github.com/oracle/graal/blob/master/substratevm/CONFIGURE.md