Project:
Converting JWT to a signed encrypted JWE
Environment:
- Windows 10
- JDK1.8
- Apache Maven 3.5.4
- VSCode 1.25.1 with all the required Java extensions
- Adobe Coldfusion 11 Application Server with JRE1.8
Dependencies:
- nimbus-jose-jwt-6.0
- json-smart-2.3
- asm-1.0.2
Issue:
Firstly, please understand that I am new to Java, but I have a good understanding of Coldfusion [CFML, like PHP].
When I run my program from within VSCode, I get the expected result of a serialised JWT string.
When I try & access the method from within a 'jar' file, using my server side language Coldfusion [similar to PHP], I get an error, coming from the very last line.
The constructor is initialised successfully, and the majority of the method call executes.
I have carefully tested every line, and the vast majority of the code works when externally calling the 'Encrypt()' method of the 'JwtSignEncrypt' class, but the last line fails with the following error.
The JWE object must be in an encrypted or decrypted state
Important Part of Stack Trace:
java.lang.IllegalStateException: The JWE object must be in an encrypted or decrypted state
at com.nimbusds.jose.JWEObject.ensureEncryptedOrDecryptedState(JWEObject.java:320)
at com.nimbusds.jose.JWEObject.serialize(JWEObject.java:456)
at com.chamika.jwt.JwtSignEncrypt.Encrypt(JwtSignEncrypt.java:153)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at coldfusion.runtime.StructBean.invoke(StructBean.java:508)
at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:2553)
at cftest412ecfm1275900201.runPage(C:\ColdFusion11\cfusion\wwwroot\establishmindfulness\unit-test\test41.cfm:129)
at coldfusion.runtime.CfJspPage.invoke(CfJspPage.java:246)
at coldfusion.tagext.lang.IncludeTag.handlePageInvoke(IncludeTag.java:736)
at coldfusion.tagext.lang.IncludeTag.doStartTag(IncludeTag.java:572)
at coldfusion.filter.CfincludeFilter.invoke(CfincludeFilter.java:65)
at coldfusion.filter.IpFilter.invoke(IpFilter.java:45)
at coldfusion.filter.ApplicationFilter.invoke(ApplicationFilter.java:466)
at coldfusion.filter.RequestMonitorFilter.invoke(RequestMonitorFilter.java:42)
at coldfusion.filter.MonitoringFilter.invoke(MonitoringFilter.java:40)
at coldfusion.filter.PathFilter.invoke(PathFilter.java:142)
at coldfusion.filter.LicenseFilter.invoke(LicenseFilter.java:30)
at coldfusion.filter.ExceptionFilter.invoke(ExceptionFilter.java:94)
at coldfusion.filter.ClientScopePersistenceFilter.invoke(ClientScopePersistenceFilter.java:28)
at coldfusion.filter.BrowserFilter.invoke(BrowserFilter.java:38)
at coldfusion.filter.NoCacheFilter.invoke(NoCacheFilter.java:58)
at coldfusion.filter.GlobalsFilter.invoke(GlobalsFilter.java:38)
at coldfusion.filter.DatasourceFilter.invoke(DatasourceFilter.java:22)
at coldfusion.filter.CachingFilter.invoke(CachingFilter.java:62)
at coldfusion.CfmServlet.service(CfmServlet.java:219)
at coldfusion.bootstrap.BootstrapServlet.service(BootstrapServlet.java:89)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at coldfusion.monitor.event.MonitoringServletFilter.doFilter(MonitoringServletFilter.java:42)
at coldfusion.bootstrap.BootstrapFilter.doFilter(BootstrapFilter.java:46)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at coldfusion.inspect.weinre.MobileDeviceDomInspectionFilter.doFilter(MobileDeviceDomInspectionFilter.java:121)
at coldfusion.bootstrap.BootstrapFilter.doFilter(BootstrapFilter.java:46)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:422)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
JwtSignEncrypt.java
package com.chamika.jwt;
import java.util.*;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;
import com.nimbusds.jwt.*;
public class JwtSignEncrypt
{
String issuer;
String subject;
List<String> audience;
Date expirationTime;
Date notBeforeTime;
Date issueTime;
String jwtID;
Map<String, Object> claim;
public JwtSignEncrypt(final String iss,
final String sub,
final String aud,
final Date exp,
final Date nbf,
final Date iat,
final String jti,
Map<String, Object> cla) {
if(iss != null) {
this.issuer = iss;
}
if(sub != null) {
this.subject = sub;
}
if(aud != null) {
List<String> items = Arrays.asList(aud.split("\\s*,\\s*"));
this.audience = items;
}
if(exp != null) {
this.expirationTime = exp;
}
if(nbf != null) {
this.notBeforeTime = nbf;
}
if(iat != null) {
this.issueTime = iat;
}
if(jti != null) {
this.jwtID = jti;
}
if(cla != null) {
this.claim = cla;
}
}
public String Encrypt(byte[] secretKeyEncoded) {
String key = null;
Object value = null;
for (Map.Entry<String, Object> entry : claim.entrySet()) {
key = entry.getKey();
value = entry.getValue();
}
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().issuer(this.issuer).subject(this.subject).audience(this.audience).expirationTime(this.expirationTime).notBeforeTime(this.notBeforeTime).issueTime(this.issueTime).jwtID(this.jwtID).claim(key,value).build();
String jweobject = "";
JWSSigner signer;
try {
signer = new MACSigner(secretKeyEncoded);
SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claimsSet);
try {
signedJWT.sign(signer);
} catch (JOSEException e) {
e.printStackTrace();
}
JWEObject jweObject = new JWEObject(
new JWEHeader.Builder(JWEAlgorithm.DIR, EncryptionMethod.A256GCM)
.contentType("JWT")
.build(),
new Payload(signedJWT));
try {
jweObject.encrypt(new DirectEncrypter(secretKeyEncoded));
} catch (KeyLengthException e) {
e.printStackTrace();
} catch (JOSEException e) {
e.printStackTrace();
}
jweobject = jweObject.serialize();
} catch (KeyLengthException e) {
e.printStackTrace();
}
return jweobject;
}
}
Error occurs at the following line in the java file:
jweObject.encrypt(new DirectEncrypter(secretKeyEncoded));
Java Loader in Coldfusion onRequestStart method:
<cfset request.lckchamikajwtlibinit = true />
<cfif NOT StructKeyExists(APPLICATION,"chamikajwtlib") OR request.appreload>
<cftry>
<cflock name="chamikajwtlib" type="exclusive" timeout="#request.writelocktimeout#">
<cfset local.jbClasschamikajwt = "#request.filepathasset#\lib\chamika-jwt-sign-encrypt\chamika-jwt-sign-encrypt-1.0.2.jar" />
<cfset local.javaloader = createObject('component','com.javaloader.JavaLoader') />
<cfset application.chamikajwtlib = local.javaloader.init([local.jbClasschamikajwt]) />
</cflock>
<cfcatch>
<cfset request.lckchamikajwtlibinit = false />
</cfcatch>
</cftry>
</cfif>
<cfif request.lckchamikajwtlibinit>
<cflock NAME="chamikajwtliblck" TIMEOUT="#request.readlocktimeout#" TYPE="READONLY">
<cfset request.chamikaJwtSignEncryptJar= application.chamikajwtlib />
</cflock>
<cfelse>
<cfset request.chamikaJwtSignEncryptJar= "" />
</cfif>
test.cfm
<cfscript>
local = {};
local.loader = request.chamikaJwtSignEncryptJar;
local.issuer = JavaCast("string","https://openid.net");
local.subject = JavaCast("string","Charles Robertson");
local.audience = "https://app-one.com,https://app-two.com";
local.expirationTime = createObject("java","java.util.Date").init().getTime() + 60 * 1000;
local.expirationTime = createObject("java","java.util.Date").init(local.expirationTime);
local.currentDateTime = createObject("java","java.util.Date").init();
local.notBeforeTime = local.currentDateTime;
local.issueTime = local.currentDateTime;
local.jwtID = JavaCast("string",CreateUUID());
local.claim = createObject("java", "java.util.LinkedHashMap").init();
local.json = {forename="Charles",surname='Robertson'};
local.claim['json'] = SerializeJson(local.json);
local.JwtSignEncrypt = local.loader.create("com.chamika.jwt.JwtSignEncrypt").init(local.issuer,local.subject,local.audience,local.expirationTime,local.notBeforeTime,local.issueTime,local.jwtID,local.claim);
local.keyGen = local.loader.create("javax.crypto.KeyGenerator").getInstance("AES");
local.keyGen.init(256);
local.secretKeyEncoded = local.keyGen.generateKey().getEncoded();
local.jweString = local.JwtSignEncrypt.Encrypt(local.secretKeyEncoded);
writeDump(var=local.jweString);
</cfscript>
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.chamika.jwt</groupId>
<artifactId>chamika-jwt-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>chamika-jwt-app</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>6.0</version>
</dependency>
<dependency>
<groupId>net.minidev</groupId>
<artifactId>json-smart</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>net.minidev</groupId>
<artifactId>asm</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies>
</project>
I have correctly included all the requisite libraries inside my 'jar'. Is there some other step I need to take, when I package the 'jar'. I am using a 'pom.xml' file, with 3 dependencies. For some reason, when I run:
nvm package
The dependencies do not get included. So, I have resorted to using 'jarsplice' to bundle the dependencies instead. All of the dependencies can be accessed correctly, independently, externally.
Questions:
- Why I am getting the error, when I try to generate the signed encrytped JWT, when using Coldfusion to call a method in the '.jar' file?
- Why are my dependencies not being included in the packaged '.jar' file?
UPDATE:
Here is my git repo:
https://bitbucket.org/charlesrobertson/chamika-jwt-app/src/master/
My java class is based on an official documentation snippet:
https://connect2id.com/products/nimbus-jose-jwt/examples/signed-and-encrypted-jwt