Closed starksm64 closed 5 years ago
@starksm64 the Java agent created by @stuartwdouglas in https://github.com/jbossas/protean-shamrock/pull/1172 might help you with this.
Once I got some logging configuration working, at least one issue is obvious:
Caused by: org.jose4j.lang.UnresolvableKeyException: Failed to read location as any of JWK, JWKS, PEM; META-INF/resources/publicKey.pem
at io.smallrye.jwt.auth.principal.KeyLocationResolver.resolveKey(KeyLocationResolver.java:73)
at org.jose4j.jwt.consumer.JwtConsumer.processContext(JwtConsumer.java:205)
... 48 more
I thought the META-INF/resources directory would be automatically included in native images, but apparently not.
Once the resource is included, the next failure is due to:
2019-03-04 22:03:15,352 DEBUG [io.sma.jwt.aut.pri.KeyLocationResolver] (XNIO-1 task-1) Failed to read location as PEM: java.security.NoSuchAlgorithmException: RSA KeyFactory not available
at java.security.KeyFactory.<init>(KeyFactory.java:138)
at java.security.KeyFactory.getInstance(KeyFactory.java:172)
at io.smallrye.jwt.KeyUtils.decodePublicKey(KeyUtils.java:133)
at io.smallrye.jwt.auth.principal.KeyLocationResolver.tryAsPEM(KeyLocationResolver.java:81)
at io.smallrye.jwt.auth.principal.KeyLocationResolver.resolveKey(KeyLocationResolver.java:70)
at org.jose4j.jwt.consumer.JwtConsumer.processContext(JwtConsumer.java:205)
....
Which is due to: https://github.com/oracle/graal/blob/master/substratevm/JCA-SECURITY-SERVICES.md
For the smallrye-jwt extension, the ideal solution would be what they suggest at the end of the document with regard to Alternative to --enable-all-security-services:
Registering all security services doesn't come for free. The additional code increases the native image size. If your application only requires a subset of the security services you can manually register the corresponding classes for reflection and push the initialization of some seed generators to runtime. However this requires deep knowledge of the JCA architecture. We are investigating the posibility to provide a finer grain declarative configuration of security services for future releases. If you want to take on this task youreslf you can start by reading the com.oracle.svm.hosted.SecurityServicesFeature class. This is where most of the code behind the --enable-all-security-services option is implemented.
We need some smart solution for security services. I was thinking it might be good to have a Feature which transforms calls like KeyFactory.getInstance("Blah")
into direct constructor calls. That would at least cover the case where there's a fixed algorithm name (which is pretty common AFAICT).
Another possible solution would be to just @Substitute
these calls, and turn it into a fixed string-based if/else tree. Then we could introduce a Feature that allows constant-folding for string comparison (something that's been on my list for a while now). This would effectively do the same thing.
The underlying theme would be to allow zero-configuration support for adding security services, without blowing up the image size if few or none are actually used (which is a negative for registering everything in sight for reflection).
Right now I am going through each JCE factory class replacement to see what it will take to get the smallrye-jwt extension working. It was a simple replacement to get the RSA KeyFactory working, now java.security.Signature needs a replacement:
Caused by: org.jose4j.lang.InvalidKeyException: The given key (algorithm=RSA) is not valid for SHA256withRSA
at org.jose4j.jws.BaseSignatureAlgorithm.initForVerify(BaseSignatureAlgorithm.java:115)
at org.jose4j.jws.BaseSignatureAlgorithm.verifySignature(BaseSignatureAlgorithm.java:56)
at org.jose4j.jws.JsonWebSignature.verifySignature(JsonWebSignature.java:192)
at org.jose4j.jwt.consumer.JwtConsumer.processContext(JwtConsumer.java:214)
... 48 more
Caused by: java.security.InvalidKeyException: No installed provider supports this key: sun.security.rsa.RSAPublicKeyImpl
at java.security.Signature$Delegate.chooseProvider(Signature.java:1138)
at java.security.Signature$Delegate.engineInitVerify(Signature.java:1170)
at java.security.Signature.initVerify(Signature.java:460)
at org.jose4j.jws.BaseSignatureAlgorithm.initForVerify(BaseSignatureAlgorithm.java:111)
... 51 more
So these two replacement classes:
package io.quarkus.elytron.security.runtime.graal;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import sun.security.rsa.SunRsaSign;
@TargetClass(Signature.class)
public final class Target_java_security_Signature {
@Substitute
public static Signature getInstance(String algorithm) throws NoSuchAlgorithmException {
if (algorithm.endsWith("RSA")) {
SunRsaSign provider = new SunRsaSign();
return Signature.getInstance(algorithm, provider);
}
throw new NoSuchAlgorithmException(algorithm);
}
}
package io.quarkus.elytron.security.runtime.graal;
import java.security.KeyFactory;
import java.security.KeyFactorySpi;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import sun.security.rsa.RSAKeyFactory;
import sun.security.rsa.SunRsaSign;
@TargetClass(KeyFactory.class)
public final class Target_java_security_KeyFactory {
static class QuarkusKeyFactory extends KeyFactory {
QuarkusKeyFactory(KeyFactorySpi keyFacSpi, Provider provider, String algorithm) {
super(keyFacSpi, provider, algorithm);
}
}
@Substitute
public static KeyFactory getInstance(String algorithm) throws NoSuchAlgorithmException {
if (algorithm.equals("RSA")) {
SunRsaSign rsaProvider = new SunRsaSign();
KeyFactorySpi keyFactorySpi = new RSAKeyFactory();
return new QuarkusKeyFactory(keyFactorySpi, rsaProvider, algorithm);
}
return null;
}
}
and this update to the SecurityDeploymentProcessor:
@BuildStep
void services(BuildProducer<ReflectiveClassBuildItem> classes) {
String[] allClasses = {
"org.wildfly.security.password.impl.PasswordFactorySpiImpl",
"sun.security.rsa.RSASignature$MD2withRSA",
"sun.security.rsa.RSASignature$MD5withRSA",
"sun.security.rsa.RSASignature$SHA1withRSA",
"sun.security.rsa.RSASignature$SHA224withRSA",
"sun.security.rsa.RSASignature$SHA256withRSA",
"sun.security.rsa.RSASignature$SHA384withRSA",
"sun.security.rsa.RSASignature$SHA512withRSA"
};
classes.produce(new ReflectiveClassBuildItem(false, false, allClasses));
}
have the smallrye-jwt extension working in native mode.
While simple, these are hardcoded and specific to the sun providers. I'll look further into just trying to load a named java.security.Provider and register its associated classes for reflection as that is essentially what the com.oracle.svm.hosted.SecurityServicesFeature does for all providers.
@starksm64 Hi Scott ! Is there any (even dirty :) ) workaround for this right now ? I would like to demo this in 10 days at a conference and it would be nice if I could show the native image working as well. Thx !
I'll push my local changes to my quarkus fork so that you could take and build that for a demo in the interim. I'll post another comment here when it is pushed.
@sebastienblanc I have pushed my changes to a new public repo on the iss1163 branch: https://github.com/starksm64/quarkus/tree/iss1163
With that, the using-jwt-rbac quickstart integration tests run under native mode, although there are a couple of fixes in that as well. One is a bug in the testHelloDenyAll() test and a wrong annotation on the associated endpoint.
The other is that in native mode, the LottoNumbersResource is not being introspected correctly due to what looks like a bug in the resteasy extension, but can be worked around by adding the @RegisterForReflection
annotation to the class:
import io.quarkus.runtime.annotations.RegisterForReflection;
import org.eclipse.microprofile.jwt.Claim;
import org.eclipse.microprofile.jwt.Claims;
import org.eclipse.microprofile.jwt.JsonWebToken;
@RegisterForReflection
@Dependent
public class LottoNumbersResource {
@Inject
JsonWebToken jwt;
@Inject
@Claim(standard = Claims.birthdate)
Optional<JsonString> birthdate;
...
@starksm64 Thanks ! I will give it a try tomorrow !
@starksm64 Works perfectly !
Is this issue planned to be fixed in 0.13 ?
Yes, either via the workaround that I have or via the more general provider registration that I'm testing.
Testing the using-jwt-rbac under native mode produces no errors, but neither authentication or authorization are working.