zoltantarcsay / oath-node

An auth tree node implementation of the ForgeRock Authenticator (OATH) auth module
3 stars 2 forks source link

Registration generate key fails #3

Open barramandi opened 3 years ago

barramandi commented 3 years ago

When user have no device registered and entered the regisration node, it seems the key generation failed.

Caused by: org.forgerock.json.jose.exceptions.JweEncryptionException: java.security.InvalidKeyException at org.forgerock.json.jose.jwe.handlers.encryption.AESCBCHMACSHA2ContentEncryptionHandler.encrypt(AESCBCHMACSHA2ContentEncryptionHandler.java:85) at org.forgerock.json.jose.jwe.handlers.encryption.DirectEncryptionHandler.encryptPlaintext(DirectEncryptionHandler.java:58) at org.forgerock.json.jose.jwe.EncryptedJwt.build(EncryptedJwt.java:162) at com.forgerock.backstage.ssoextensions.auth.oath.OathHelper.encrypt(Unknown Source) at com.forgerock.backstage.ssoextensions.auth.oath.OathHelper.encryptOathDeviceSettings(Unknown Source) at com.forgerock.backstage.ssoextensions.auth.oath.registration.OathRegistrationNode.createDeviceProfileAndFinishWithCallbacks(Unknown Source) at com.forgerock.backstage.ssoextensions.auth.oath.registration.OathRegistrationNode.process(Unknown Source) at org.forgerock.openam.auth.trees.engine.AuthTreeExecutor.process(AuthTreeExecutor.java:143) ... 122 common frames omitted Caused by: java.security.InvalidKeyException: null at org.forgerock.json.jose.jwe.handlers.encryption.AESCBCHMACSHA2ContentEncryptionHandler.validateKey(AESCBCHMACSHA2ContentEncryptionHandler.java:180) at org.forgerock.json.jose.jwe.handlers.encryption.AESCBCHMACSHA2ContentEncryptionHandler.encrypt(AESCBCHMACSHA2ContentEncryptionHandler.java:63) ... 129 common frames omitted

kullwindergit commented 3 years ago

Do we have any updated code for this issue.

jasarmcj commented 2 years ago

@zoltantarcsay Any suggestion on this issue would be helpful

zoltantarcsay commented 2 years ago

@jasarmcj could you send the full stack trace?

jasarmcj commented 2 years ago

Hi @zoltantarcsay here is the complete stack trace

org.forgerock.json.jose.exceptions.JweEncryptionException: java.security.InvalidKeyException at org.forgerock.json.jose.jwe.handlers.encryption.AESCBCHMACSHA2ContentEncryptionHandler.encrypt(AESCBCHMACSHA2ContentEncryptionHandler.java:81) at org.forgerock.json.jose.jwe.handlers.encryption.DirectEncryptionHandler.encryptPlaintext(DirectEncryptionHandler.java:58) at org.forgerock.json.jose.jwe.EncryptedJwt.build(EncryptedJwt.java:162) at com.forgerock.backstage.ssoextensions.auth.oath.OathHelper.encrypt(OathHelper.java:115) at com.forgerock.backstage.ssoextensions.auth.oath.OathHelper.encryptOathDeviceSettings(OathHelper.java:85) at com.forgerock.backstage.ssoextensions.auth.oath.registration.OathRegistrationNode.createDeviceProfileAndFinishWithCallbacks(OathRegistrationNode.java:97) at com.forgerock.backstage.ssoextensions.auth.oath.registration.OathRegistrationNode.process(OathRegistrationNode.java:86) at org.forgerock.openam.auth.trees.engine.AuthTreeExecutor.process(AuthTreeExecutor.java:107) at org.forgerock.openam.auth.trees.engine.AuthTreeExecutor.process(AuthTreeExecutor.java:154) at org.forgerock.openam.auth.trees.engine.AuthTreeExecutor.process(AuthTreeExecutor.java:154) at org.forgerock.openam.auth.trees.engine.AuthTreeExecutor.process(AuthTreeExecutor.java:154) at org.forgerock.openam.core.rest.authn.trees.AuthTrees.processTree(AuthTrees.java:423) at org.forgerock.openam.core.rest.authn.trees.AuthTrees.evaluateTreeAndProcessResult(AuthTrees.java:263) at org.forgerock.openam.core.rest.authn.trees.AuthTrees.invokeTree(AuthTrees.java:255) at org.forgerock.openam.core.rest.authn.RestAuthenticationHandler.authenticate(RestAuthenticationHandler.java:230) at org.forgerock.openam.core.rest.authn.http.AuthenticationServiceV1.authenticate(AuthenticationServiceV1.java:162) 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:498) at org.forgerock.openam.http.annotations.AnnotatedMethod.invoke(AnnotatedMethod.java:76) at org.forgerock.openam.http.annotations.Endpoints$1.handle(Endpoints.java:64) at org.forgerock.http.routing.Router.handle(Router.java:100) at org.forgerock.openam.audit.AbstractHttpAccessAuditFilter.filter(AbstractHttpAccessAuditFilter.java:59) at org.forgerock.http.handler.Handlers$1.handle(Handlers.java:53) at org.forgerock.http.routing.Router.handle(Router.java:100) at org.forgerock.openam.rest.RealmContextFilter.filter(RealmContextFilter.java:87) at org.forgerock.http.handler.Handlers$1.handle(Handlers.java:53) at org.forgerock.http.routing.Router.handle(Router.java:100) at org.forgerock.http.routing.Router.handle(Router.java:100) at org.forgerock.openam.rest.CsrfFilter.filter(CsrfFilter.java:95) at org.forgerock.http.handler.Handlers$1.handle(Handlers.java:53) at org.forgerock.http.routing.ResourceApiVersionRoutingFilter.filter(ResourceApiVersionRoutingFilter.java:59) at org.forgerock.http.handler.Handlers$1.handle(Handlers.java:53) at org.forgerock.caf.authentication.framework.AuthenticationFramework.grantAccess(AuthenticationFramework.java:188) at org.forgerock.caf.authentication.framework.AuthenticationFramework.lambda$onValidateRequestSuccess$1(AuthenticationFramework.java:181) at org.forgerock.util.promise.Promises$CompletedPromise.thenAsync(Promises.java:260) at org.forgerock.util.promise.Promises$CompletedPromise.thenAsync(Promises.java:249) at org.forgerock.caf.authentication.framework.AuthenticationFramework.validateRequest(AuthenticationFramework.java:144) at org.forgerock.caf.authentication.framework.AuthenticationFramework.processMessage(AuthenticationFramework.java:134) at org.forgerock.caf.authentication.framework.AuthenticationFilter.filter(AuthenticationFilter.java:84) at org.forgerock.http.handler.Handlers$1.handle(Handlers.java:53) at org.forgerock.openam.http.GuiceHandler.handle(GuiceHandler.java:51) at org.forgerock.openam.http.HttpRoute$6.handle(HttpRoute.java:206) at org.forgerock.http.routing.Router.handle(Router.java:100) at org.forgerock.openam.dpro.session.ProofOfPossessionTokenFilter.filter(ProofOfPossessionTokenFilter.java:87) at org.forgerock.http.handler.Handlers$1.handle(Handlers.java:53) at org.forgerock.http.swagger.OpenApiRequestFilter.filter(OpenApiRequestFilter.java:63) at org.forgerock.http.handler.Handlers$1.handle(Handlers.java:53) at org.forgerock.openam.http.ApiDescriptorFilter.filter(ApiDescriptorFilter.java:139) at org.forgerock.http.handler.Handlers$1.handle(Handlers.java:53) at org.forgerock.openam.http.OpenAMHttpApplication$1.filter(OpenAMHttpApplication.java:74) at org.forgerock.http.handler.Handlers$1.handle(Handlers.java:53) at org.forgerock.http.filter.TransactionIdInboundFilter.filter(TransactionIdInboundFilter.java:86) at org.forgerock.http.handler.Handlers$1.handle(Handlers.java:53) at org.forgerock.http.servlet.HttpFrameworkServlet.service(HttpFrameworkServlet.java:260) at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:228) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) at org.forgerock.openam.services.datastore.DataStoreConsistencyFilter.lambda$doFilter$0(DataStoreConsistencyFilter.java:46) at org.forgerock.openam.service.datastore.ReentrantVolatileActionConsistencyController.safeExecute(ReentrantVolatileActionConsistencyController.java:37) at org.forgerock.openam.services.datastore.DataStoreConsistencyFilter.doFilter(DataStoreConsistencyFilter.java:46) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) at org.forgerock.openam.rest.ProtocolVersionFilter.doFilter(ProtocolVersionFilter.java:62) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) at org.forgerock.openam.headers.DisableSameSiteCookiesFilter.doFilter(DisableSameSiteCookiesFilter.java:105) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) at org.forgerock.openam.validation.ResponseValidationFilter.doFilter(ResponseValidationFilter.java:59) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) at org.forgerock.openam.headers.SetHeadersFilter.doFilter(SetHeadersFilter.java:80) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) at org.forgerock.openam.headers.SetHeadersFilter.doFilter(SetHeadersFilter.java:80) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) at com.sun.identity.setup.AMSetupFilter.doFilter(AMSetupFilter.java:115) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) at org.forgerock.openam.audit.context.AuditContextFilter.doFilter(AuditContextFilter.java:46) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:687) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1723) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:748) Caused by: java.security.InvalidKeyException at org.forgerock.json.jose.jwe.handlers.encryption.AESCBCHMACSHA2ContentEncryptionHandler.validateKey(AESCBCHMACSHA2ContentEncryptionHandler.java:176) at org.forgerock.json.jose.jwe.handlers.encryption.AESCBCHMACSHA2ContentEncryptionHandler.encrypt(AESCBCHMACSHA2ContentEncryptionHandler.java:59) ... 104 more

zoltantarcsay commented 2 years ago

The code wants to use EncryptionMethod.A128CBC_HS256, so make sure that the key being used supports that. The key being used is the one referenced as "am.services.oauth2.stateless.token.encryption". You can look up the alias of this key in the keystore on the secret store config page in AM (e.g. #configure/secretStores/KeyStoreSecretStore/edit/default-keystore, or if you have a different keystore defined for your realm, then there). If this key doesn't exist or is not the right length or type, you should generate a new key and add it to your keystore.

zoltantarcsay commented 2 years ago

here's what EncryptionMethod.A128CBC_HS256 looks like:

/**
 * AES encryption in CBC mode with PKCS5 Padding and a 128 bit length, AES encryption for CEK, HMA
 * hash algorithm for authentication tag.
 */
A128CBC_HS256("AES_128_CBC_HMAC_SHA_256", "AES/CBC/PKCS5Padding", "HMACSHA256", "AES", 16, 256),
aksonkar commented 2 years ago

The am.services.oauth2.stateless.token.encryption provides the alias name of key to be used for encryption. The default value is directenctest in the keystore provided with AM installation (I am using AM 6.5.3) The encrypt method uses this key and checks for predefined allowed algorithms.
When this alias is read as key its algorithm is "RAW" and not AES which FR framework expects in allowed algorithms. Hence it is required to generate new key with algo AES.

keytool gensekey with AES algo should have helped, but key generated with keytool when loaded with java code, shows algorithm RAW.. So here is what needs to be done:

  1. Generate AES key using java and store in a temp keystore. (sample program provided below)
  2. Import the AES key from temp keystore to keystore used by openam.
  3. Deploy the updated keystore and am installtion
  4. Restart Openam
  5. Run you login flow with custom node for oath.

Here is the program to save the AES key in keystore: import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.security.Key; import java.security.KeyStore; import java.security.cert.Certificate;

import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey;

import org.forgerock.json.jose.jwe.EncryptionMethod;

public class StoreAESKey {

/**
 * This class creates a Symmetric key with AES algo with size 256 bits and stores it in a keystore.
 * 
 * The stored key can be imported to another keystore using keytool command.
 * Sample keytool command:
 * 
 * keytool -importkeystore -srckeystore D:\\temp.jceks -
 * srcstorepass pass -srckeypass pass -srcalias directenctest 
 * -destalias directenctest -destkeystore D:\\keystore.jceks 
 * -srcstoretype JCEKS -deststoretype JCEKS -deststorepass pass 
 * -destkeypass pass
 * 
 * @param args
 * @throws Exception
 */
public static void main(String[] args) throws Exception {
    String keystorePass = "pass";
    String alias  = "directenctest";
    String keyPass= "pass";
    String keystoreLocation = "D:\\temp.jceks";
    FileOutputStream fos = new FileOutputStream(keystoreLocation);      

    KeyStore keystore = KeyStore.getInstance("JCEKS"); 
    keystore.load(null, null);

    KeyGenerator instance = KeyGenerator.getInstance("AES");
    instance.init(256);

    SecretKey generateKey = instance.generateKey();

    Certificate[] cert = {};
    keystore.setKeyEntry(alias, generateKey, keyPass.toCharArray(), cert);
    keystore.store(fos, keystorePass.toCharArray());
    fos.close();

// read the key and check its algorithm. InputStream keystoreStream = new FileInputStream(keystoreLocation);

    keystore = KeyStore.getInstance("JCEKS"); 
    keystore.load(keystoreStream, keystorePass.toCharArray()); 
    keystoreStream.close();
    if (!keystore.containsAlias(alias)) { 
     throw new RuntimeException("Alias for key not found"); 
    } 
    Key key = keystore.getKey(alias, keyPass.toCharArray());

    //check the key algorithm
    System.out.println("key algo=" + key.getAlgorithm() + " key format=" + key.getFormat() + " encoded key=" + key.getEncoded() );

}

}


Here is the program to inspect the key from keystore:

import java.io.FileInputStream; import java.io.InputStream; import java.security.Key; import java.security.KeyStore;

import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec;

import org.forgerock.json.jose.jwe.EncryptionMethod;

public class GetAESKey {

public static void main(String[] args) throws Exception {
    String keystorePass = "pass";
    String alias  = "directenctest";
    String keyPass= "pass";
    String keystoreLocation = "D:\\\\keystore.jceks";

    InputStream keystoreStream = new FileInputStream(keystoreLocation); 
    KeyStore keystore = KeyStore.getInstance("JCEKS"); 
    keystore.load(keystoreStream, keystorePass.toCharArray()); 
    if (!keystore.containsAlias(alias)) { 
     throw new RuntimeException("Alias for key not found"); 
    } 
    Key key = keystore.getKey(alias, keyPass.toCharArray());

    System.out.println("key algo=" + key.getAlgorithm() + " key format=" + key.getFormat() + " encoded key=" + key.getEncoded() );

    System.out.println(EncryptionMethod.A128CBC_HS256.getEncryptionAlgorithm());
    System.out.println(EncryptionMethod.A128CBC_HS256.getMacAlgorithm());   
    System.out.println(EncryptionMethod.A128CBC_HS256.getKeySize());
}

}