Hey, any JWT experts here? I'm trying to connect ...
# adobe
b
Hey, any JWT experts here? I'm trying to connect to NetSuite with a signed JWT. It works fine with JavaScript (jsrsasign), but I'm hoping to solve this using ColdFusion 2023 to keep it synchronous. It requires ES256, a header, payload, and a PrivateKey.pem. The jwt-cfml comes close, but it throws a No EC error using this algorithm... even after encoding the PEM as PKCS#8 format as described. Using the CF CreateSignedJWT function does not help at all. Just curious if this is even possible. Thanks.
b
It might help to take a look at Ben Nadel's JWT example.
b
Thanks for that BK BK
👍 1
j
What version of Java are you testing with? And what exactly was the "No EC error"? When I tested jwt-cfml with Adobe 2023, if I used OpenJDK 21, I would get reflection errors for the EC algorithms ("module jdk.crypto.ec does not export sun.security.ec"), and I had to add an "--add-opens jdk.crypto.ec/sun.security.ec=ALL-UNNAMED" to my JVM args to get it to allow reflective access. It is not clear to me though whether that is the error you are facing?
b
Hey jcberquist, thank you for reaching out! This is a client's machine on Windows 2019 running Java 25.242-b07 (1.8.0_242) which I believe is Java 8. I also remember the error happened on line 175 of your JWT component if that helps. I'm glad to revisit that code to provide more details if you like. Outside of that, I really like what you have done!
j
Right, that looks to be Java 8. My tests pass locally with ACF2023 and Java 8, so it would be helpful to know what error is being thrown exactly.
b
Sure, no problem. I'll rerun the test and show a screen capture in a bit. Thanks!
Not an EC key: RSA 172 : var jssInstance = variables.jss.getInstance( algorithmMap[ algorithm ] ); 173 : jssInstance.initSign( key ); 174 : jssInstance.update( charsetDecode( message, 'utf-8' ) ); 175 : var sig = jssInstance.sign(); Resources: Check the ColdFusion documentation to verify that you are using the correct syntax. Search the Knowledge Base to find a solution to your problem. java.security.InvalidKeyException: Not an EC key: RSA at jdk.crypto.ec/sun.security.ec.ECKeyFactory.engineTranslateKey(ECKeyFactory.java:139) at java.base/java.security.KeyFactory.translateKey(KeyFactory.java:469) at jdk.crypto.ec/sun.security.ec.ECKeyFactory.toECKey(ECKeyFactory.java:99) at jdk.crypto.ec/sun.security.ec.ECDSASignature.engineInitSign(ECDSASignature.java:372) at jdk.crypto.ec/sun.security.ec.ECDSASignature.engineInitSign(ECDSASignature.java:365) at java.base/java.security.Signature$Delegate.tryOperation(Signature.java:1327) at java.base/java.security.Signature$Delegate.chooseProvider(Signature.java:1276) at java.base/java.security.Signature$Delegate.engineInitSign(Signature.java:1373) at java.base/java.security.Signature.initSign(Signature.java:635) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at coldfusion.runtime.StructBean.invoke(StructBean.java:509) at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:4248) at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:4100) at cfjwt2ecfc1613502659$funcSIGN.runFunction(D\virtualwww\site effects\clients\mis splash\CXMLConnect\gateway\Tayor\v3\no app\netsuite\models\jwt.cfc173) at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:629) at coldfusion.runtime.UDFMethod$ArgumentCollectionFilter.invoke(UDFMethod.java:522) at coldfusion.filter.FunctionAccessFilter.invoke(FunctionAccessFilter.java:95) at coldfusion.runtime.UDFMethod.runFilterChain(UDFMethod.java:469) at coldfusion.runtime.UDFMethod.runFilterChain(UDFMethod.java:444) at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:316) at coldfusion.runtime.CfJspPage._invokeUDF(CfJspPage.java:4894) at coldfusion.runtime.CfJspPage._invokeUDF(CfJspPage.java:4874) at cfjwt2ecfc1613502659$funcENCODE.runFunction(D\virtualwww\site effects\clients\mis splash\CXMLConnect\gateway\Tayor\v3\no app\netsuite\models\jwt.cfc68) at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:629) at coldfusion.runtime.UDFMethod$ReturnTypeFilter.invoke(UDFMethod.java:559) at coldfusion.runtime.UDFMethod$ArgumentCollectionFilter.invoke(UDFMethod.java:522) at coldfusion.filter.FunctionAccessFilter.invoke(FunctionAccessFilter.java:95) at coldfusion.runtime.UDFMethod.runFilterChain(UDFMethod.java:469) at coldfusion.runtime.UDFMethod.runFilterChain(UDFMethod.java:444) at coldfusion.runtime.UDFMethod.invoke(UDFMethod.java:316) at coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:975) at coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:696) at coldfusion.runtime.TemplateProxy.invoke(TemplateProxy.java:503) at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:4137) at coldfusion.runtime.CfJspPage._invoke(CfJspPage.java:4100) at cftest22ecfm441991199.runPage(D\virtualwww\site effects\clients\mis splash\CXMLConnect\gateway\Tayor\v3\no app\netsuite\test2.cfm68) at coldfusion.runtime.CfJspPage.invoke(CfJspPage.java:251) at coldfusion.tagext.lang.IncludeTag.handlePageInvoke(IncludeTag.java:749) at coldfusion.tagext.lang.IncludeTag.doStartTag(IncludeTag.java:578) at coldfusion.filter.CfincludeFilter.invoke(CfincludeFilter.java:65) at coldfusion.filter.ApplicationFilter.invoke(ApplicationFilter.java:613) at coldfusion.filter.MonitoringFilter.invoke(MonitoringFilter.java:40) at coldfusion.filter.PathFilter.invoke(PathFilter.java:162) at coldfusion.filter.IpFilter.invoke(IpFilter.java:45) at coldfusion.filter.ExceptionFilter.invoke(ExceptionFilter.java:97) at coldfusion.filter.BrowserDebugFilter.invoke(BrowserDebugFilter.java:81) at coldfusion.filter.ClientScopePersistenceFilter.invoke(ClientScopePersistenceFilter.java:28) at coldfusion.filter.BrowserFilter.invoke(BrowserFilter.java:38) at coldfusion.filter.NoCacheFilter.invoke(NoCacheFilter.java:60) 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:231) at coldfusion.bootstrap.BootstrapServlet.service(BootstrapServlet.java:311) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:199) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144) at coldfusion.monitor.event.MonitoringServletFilter.doFilter(MonitoringServletFilter.java:46) at coldfusion.bootstrap.BootstrapFilter.doFilter(BootstrapFilter.java:47) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:168) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144) at coldfusion.inspect.weinre.MobileDeviceDomInspectionFilter.doFilter(MobileDeviceDomInspectionFilter.java:57) at coldfusion.bootstrap.BootstrapFilter.doFilter(BootstrapFilter.java:47) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:168) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:144) at jdk.internal.reflect.GeneratedMethodAccessor131.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at com.intergral.fusionreactor.j2ee.filterchain.WrappedFilterChain.doFilter(WrappedFilterChain.java:134) at com.intergral.fusionreactor.j2ee.filter.FusionReactorRequestHandler.doNext(FusionReactorRequestHandler.java:698) at com.intergral.fusionreactor.j2ee.filter.FusionReactorRequestHandler.doHttpServletRequest(FusionReactorRequestHandler.java:256) at com.intergral.fusionreactor.j2ee.filter.FusionReactorRequestHandler.doFusionRequest(FusionReactorRequestHandler.java:119) at com.intergral.fusionreactor.j2ee.filter.FusionReactorRequestHandler.handle(FusionReactorRequestHandler.java:736) at com.intergral.fusionreactor.j2ee.filter.FusionReactorCoreFilter.doFilter(FusionReactorCoreFilter.java:36) at jdk.internal.reflect.GeneratedMethodAccessor128.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at com.intergral.fusionreactor.j2ee.filterchain.WrappedFilterChain.doFilter(WrappedFilterChain.java:71) at jdk.internal.reflect.GeneratedMethodAccessor127.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at com.intergral.fusionreactor.agent.filter.FusionReactorStaticFilter.doFilter(FusionReactorStaticFilter.java:54) at com.intergral.fusionreactor.agent.pointcuts.NewFilterChainPointCut$1.invoke(NewFilterChainPointCut.java:50) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:168) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) at org.apache.coyote.ajp.AjpProcessor.service(AjpProcessor.java:448) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:936) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1791) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190) at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) at java.base/java.lang.Thread.run(Thread.java:842)
j
Interesting that you are able to make this work via JavaScript - what the error seems to saying is that you passed in an RSA key to the jwt.encode() function, and then told it the algorithm you were using was an EC algorithm (ES256 I presume based on your earlier messages). If you are sure you are passing in an ECDSA key, then it is possible there is invalid parsing going on in the jwt encoding utils. What I actually do (if you pass in a base64 encoded key) is try to parse it as an RSA key first, and then if that fails, try again as an EC key. Given that the actual signing attempt fails with invalid key, I would be somewhat surprised if the parsing as RSA succeeds, but it could be possible. If you can confirm that you do in fact have an ECDSA key, you can try pre-parsing it yourself, to see if this is in fact what is happening, e.g.:
Copy code
// given PEMKey as the base 64 text encoded key in PKCS8
binaryKey = binaryDecode(
    trim( pemKey ).reReplace( '-----[A-Z\s]+-----', '', 'all' ).reReplace( '[\r\n]', '', 'all' ),
    'base64'
);
privateKeySpec = createObject( 'java', 'java.security.spec.PKCS8EncodedKeySpec' ).init( binaryKey );
key = createObject( 'java', 'java.security.KeyFactory' ).getInstance( 'EC' ).generatePrivate( privateKeySpec );
token = jwt.encode( payload, key, 'ES256' );
writeDump(token);
b
Okay thank you, I will give that a go. The algorithm used in my Javascript solution is PS256 which I don't think is supported in your wrapper. However NetSuite apparently supports ES256, that's why I'm using it. One off the wall question is about PKCS8. Depending on how it is created, it can be in a readable format or not. When I used the openssl command you published, it didn't change the KEY at all except for the file extension. Why is that? In either case, I'm not reading a file, I'm just including the KEY in my test code and converting to a variable.
j
Well, here I am not an expert at all, but PEM encoding is a broader use encoding while PKCS8 is specifically for private keys. If you generate a key in DER format from open SSL the output is binary. The Java cryptographic libraries should work with that fine, I just chose to work with PKCS8 in base64. But you can always create the Java private key class yourself (as in the example above), and then pass that class instance in instead of the base64 encoded key. Depending on how you created your key pair, the private key might already be in PKCS8 format - in which case the contents of the file wouldn't change.
b
Thanks again for your help jcberquist, much appreciated! The problem was the algorithm. In the NetSuite docs, they demo using PS256 with RSA keys, not EC keys. Since JWT-CFML does not support PS256, I just used EC256 instead hoping it would work. What I realize now is EC256 requires EC keys, not RSA keys. That is what caused the issues, not JWT-CFML. So... since you provided some code, I went and experimented using EC keys instead. First, I created an EC private key with openssl, then converted it to PKCS8 format using this command... openssl pkcs8 -topk8 -nocrypt -in private.ec.key -out p8file.pem Then once the key was converted, I finally created a token using the Java code you provided. No EC errors! But will this work on NetSuite? Maybe... but I would have to map a new certificate in their admin, etc, etc. I'll see if I'm up to that later.
Update. Big shout out to @jcberquist! Because of your help today, I was able to make a successful connection using JWT-CFML. Definitely a learning curve, but it paid off! Thanks again!
j
Hey, I am glad to hear you were able to make things work! Looking back at my last post, that was a stream of consciousness - sorry about that, but you got it working anyways 😄
❤️ 1