Quarkus: Read/use system resources in native mode throwing NPE

Created on 15 Jun 2020  路  23Comments  路  Source: quarkusio/quarkus

Describe the bug
I'm trying to read a private key to sign JWT tokens but i get NPE when using system resources in native mode

Expected behavior
Read key and sign jwt but also i saw some related issues, assumes it's resolved when updating to Quarkus 1.5.0.Final but i actually use 1.5.1.Final and still get this in native mode

Actual behavior
Got NPE in Native Mode

To Reproduce
Steps to reproduce the behavior:

  1. mvn package -Pnative
  2. execute runner
  3. generate token

Configuration

# Add your application.properties here, if applicable.

Screenshots
(If applicable, add screenshots to help explain your problem.)

Environment (please complete the following information):

  • Output of uname -a or ver: Linux MiWiFi-R2100-srv 5.6.16-300.fc32.x86_64 #1 SMP Thu Jun 4 18:08:38 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
  • Output of java -version: openjdk version "11.0.7" 2020-04-14 OpenJDK Runtime Environment GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02) OpenJDK 64-Bit Server VM GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02, mixed mode, sharing)
  • GraalVM version (if different from Java):
  • Quarkus version or git rev: 1.5.1.Final
  • Build tool (ie. output of mvnw --version or gradlew --version): Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)

Additional context
Here are my logs

2020-06-15 10:58:18,100 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /auth/login failed, error id: 58c6cebd-2cca-4198-9589-bf95f129c9b6-1: org.jboss.resteasy.spi.UnhandledException: java.lang.RuntimeException: java.lang.NullPointerException
        at org.jboss.resteasy.core.ExceptionHandler.handleApplicationException(ExceptionHandler.java:106)
        at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:372)
        at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:216)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:515)
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:259)
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:160)
        at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:362)
        at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:163)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:245)
        at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:132)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.access$000(VertxRequestHandler.java:37)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:94)
        at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
        at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2046)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1578)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
        at java.lang.Thread.run(Thread.java:834)
        at org.jboss.threads.JBossThread.run(JBossThread.java:479)
        at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:517)
        at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:193)
Caused by: java.lang.RuntimeException: java.lang.NullPointerException
        at io.efraim.services.TokenService.generateToken(TokenService.java:38)
        at io.efraim.services.TokenService.generateUserToken(TokenService.java:16)
        at io.efraim.services.TokenService_ClientProxy.generateUserToken(TokenService_ClientProxy.zig:222)
        at io.efraim.resources.AccountResource.login(AccountResource.java:84)
        at io.efraim.resources.AccountResource_Subclass.login$$superaccessor6(AccountResource_Subclass.zig:995)
        at io.efraim.resources.AccountResource_Subclass$$function$$6.apply(AccountResource_Subclass$$function$$6.zig:33)
        at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:54)
        at io.quarkus.security.runtime.interceptor.SecurityHandler.handle(SecurityHandler.java:21)
        at io.quarkus.security.runtime.interceptor.PermitAllInterceptor.intercept(PermitAllInterceptor.java:23)
        at io.quarkus.security.runtime.interceptor.PermitAllInterceptor_Bean.intercept(PermitAllInterceptor_Bean.zig:327)
        at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
        at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:50)
        at io.quarkus.security.runtime.interceptor.SecurityHandler.handle(SecurityHandler.java:24)
        at io.quarkus.security.runtime.interceptor.RolesAllowedInterceptor.intercept(RolesAllowedInterceptor.java:23)
        at io.quarkus.security.runtime.interceptor.RolesAllowedInterceptor_Bean.intercept(RolesAllowedInterceptor_Bean.zig:335)
        at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
        at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
        at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
        at io.efraim.resources.AccountResource_Subclass.login(AccountResource_Subclass.zig:952)
        at io.efraim.resources.AccountResource_ClientProxy.login(AccountResource_ClientProxy.zig:192)
        at java.lang.reflect.Method.invoke(Method.java:566)
        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:167)
        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130)
        at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:621)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:487)
        at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:437)
        at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:362)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:439)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:400)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:374)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:67)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:488)
        ... 19 more
Caused by: java.lang.NullPointerException
        at io.efraim.utils.TokenUtils.readPrivateKey(TokenUtils.java:63)
        at io.efraim.utils.TokenUtils.generateTokenString(TokenUtils.java:27)
        at io.efraim.services.TokenService.generateToken(TokenService.java:36)
        ... 50 more

aresmallrye kinquestion

All 23 comments

@EfraimLA Can you please provide more details ?
Where is the private key located ? How to access it ? thanks

This is how my resources are organized

image

@EfraimLA How do you access it from the code or refer to it from the configuration ?

This is my reader function

    public static PrivateKey readPrivateKey(final String pemResName) throws Exception {
        InputStream contentIS = TokenUtils.class.getResourceAsStream(pemResName);
        byte[] tmp = new byte[4096];
        int length = contentIS.read(tmp);
        return decodePrivateKey(new String(tmp, 0, length, StandardCharsets.UTF_8));
    }

And this me calling with path
PrivateKey pk = readPrivateKey("/privateKey.pem");

@EfraimLA Oh, so what is TokenUtils, is it the one coming from MP JWT TCK ? Where is the smallrye-jwt code here ?

What has been fixed is the code like this:

Jwt.claims()...sign("/privateKey.pem")

or

smallrye.jwt.sign.key-location=/privateKey.pem
Jwt.claims()...sign()

I used it from a repo but don't remember which, have u some sample to use?

@EfraimLA It looks like this issue is not a bug and can be closed.
Please check:
https://quarkus.io/guides/security-jwt#generate-jwt-tokens

and

https://github.com/quarkusio/quarkus-quickstarts/blob/master/security-jwt-quickstart/src/test/java/org/acme/security/jwt/TokenUtils.java#L48
(most of this old quickstart code will be replaced but you can see there how to use the new builder API)

K, so isn't a bug with system resources?

@EfraimLA But where is the bug ? I don't see a smallrye-jwt code in your code fragment. What is that TokenUtils that you use ? Please create a reproducer showing the code which I linked to in action and failing to load the key, thanks

i just update my TokenUtils and removed all methods and keep only this functions:

    public static String generateToken(String subject, Object name, String... roles) {
        try {
            JwtClaimsBuilder claims = Jwt.claims();

            claims.issuer(ISSUER);
            claims.subject(subject);
            claims.upn(subject);
            claims.preferredUserName(String.valueOf(name));
            claims.groups(Set.of(roles));
            claims.audience(AUDIENCE);
            claims.expiresAt(currentTimeInSecs() + 600);

            return claims.sign();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @return the current time in seconds since epoch
     */
    public static int currentTimeInSecs() {
        long currentTimeMS = System.currentTimeMillis();
        return (int) (currentTimeMS / 1000);
    }

And added smallrye.jwt.sign.key-location=privateKey.pem to application.properties

it works on JVM but doesn't in native runner

@EfraimLA I'll investigate, can you check what happens with smallrye.jwt.sign.key-location=/privateKey.pem ? thanks

@EfraimLA

I have updated this quickstart locally with:

diff --git a/security-jwt-quickstart/src/main/resources/application.properties b/security-jwt-quickstart/src/main/resources/application.properties
index 02aa73d..a768be5 100644
--- a/security-jwt-quickstart/src/main/resources/application.properties
+++ b/security-jwt-quickstart/src/main/resources/application.properties
@@ -18,3 +18,5 @@ quarkus.log.file.enable=true
 #quarkus.log.category."io.quarkus.smallrye.jwt".level=TRACE
 #quarkus.log.category."io.undertow.request.security".level=TRACE
 #quarkus.log.category."io.smallrye.jwt".level=TRACE
+
+smallrye.jwt.sign.key-location=privateKey.pem
diff --git a/security-jwt-quickstart/src/test/java/org/acme/security/jwt/TokenUtils.java b/security-jwt-quickstart/src/test/java/org/acme/security/jwt/TokenUtils.java
index a03227f..430d127 100644
--- a/security-jwt-quickstart/src/test/java/org/acme/security/jwt/TokenUtils.java
+++ b/security-jwt-quickstart/src/test/java/org/acme/security/jwt/TokenUtils.java
@@ -55,7 +55,7 @@ public class TokenUtils {
         claims.claim(Claims.auth_time.name(), currentTimeInSecs);
         claims.expiresAt(exp);

-        return claims.jws().signatureKeyId(kid).sign(privateKey);
+        return claims.jws().signatureKeyId(kid).sign();
     }

     /**

and mvn clean install -Dnative works.
Please create a reproducer project

If i set smallrye.jwt.sign.key-location=/privateKey.pem get Caused by: java.lang.RuntimeException: java.lang.IllegalArgumentException: Signing key can not be loaded from: /privateKey.pem

@EfraimLA

I have updated this quickstart locally with:

diff --git a/security-jwt-quickstart/src/main/resources/application.properties b/security-jwt-quickstart/src/main/resources/application.properties
index 02aa73d..a768be5 100644
--- a/security-jwt-quickstart/src/main/resources/application.properties
+++ b/security-jwt-quickstart/src/main/resources/application.properties
@@ -18,3 +18,5 @@ quarkus.log.file.enable=true
 #quarkus.log.category."io.quarkus.smallrye.jwt".level=TRACE
 #quarkus.log.category."io.undertow.request.security".level=TRACE
 #quarkus.log.category."io.smallrye.jwt".level=TRACE
+
+smallrye.jwt.sign.key-location=privateKey.pem
diff --git a/security-jwt-quickstart/src/test/java/org/acme/security/jwt/TokenUtils.java b/security-jwt-quickstart/src/test/java/org/acme/security/jwt/TokenUtils.java
index a03227f..430d127 100644
--- a/security-jwt-quickstart/src/test/java/org/acme/security/jwt/TokenUtils.java
+++ b/security-jwt-quickstart/src/test/java/org/acme/security/jwt/TokenUtils.java
@@ -55,7 +55,7 @@ public class TokenUtils {
         claims.claim(Claims.auth_time.name(), currentTimeInSecs);
         claims.expiresAt(exp);

-        return claims.jws().signatureKeyId(kid).sign(privateKey);
+        return claims.jws().signatureKeyId(kid).sign();
     }

     /**

and mvn clean install -Dnative works.
Please create a reproducer project

Also tried this, even without getting builder with jws() method and with just smallrye.jwt.sign.key-location=privateKey.pem but got the same
Caused by: java.lang.IllegalArgumentException: Signing key can not be loaded from: privateKey.pem

Well now i'm trying with the same methods and config in JVM mode, can sign token with no problems but i get this warning, is this something relevant?

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.eclipse.yasson.internal.ReflectionUtils (jar:file:/home/efraim/.m2/repository/org/eclipse/yasson/1.0.7/yasson-1.0.7.jar!/) to constructor java.util.AbstractMap()
WARNING: Please consider reporting this to the maintainers of org.eclipse.yasson.internal.ReflectionUtils
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

@EfraimLA I was testing with Java 8, I've rebuilt the Quarkus master and the quickstart with Java 11, and it is clean and works in the native mode with the changes I've pasted above.
Please create a git hub reproducer project for us to make some progress
Thanks

https://github.com/EfraimLA/quarkus-jwt-reproducer

It's my reproducer with Java 11, in native doesn't work

@EfraimLA Thanks, I've missed it, will have a look

Ok I wait ur response

@EfraimLA, good news is that it is not a bug,
See:
https://quarkus.io/guides/writing-native-applications-tips#including-resources

I've reproduced it, so the private key should be in META-INF/resources if you'd like a given resource be loaded by default in the native image (and refer to the key as META-INF/resources/privateKey.pem, etc), or provide the resource registration file as documented in the linked docs.

I'll wait for you to confirm before closing this issue, thanks

Ohhh sure that makes sense, in fact I did try putting the publicKey.pem there and even appeared in native building log, I'll confirm

I confirm, Yes that's not a bug, thanks for help

I close issue

I had the same problem. Even after implementing everything you mentioned above the privateKey couldn't be read in native mode.
The only thing that helped me is writing the whole path here: readPrivateKey("/META-INF/resources/privateKey.pem");

Was this page helpful?
0 / 5 - 0 ratings