Skip to main content
dwaynea8754223
Known Participant
September 14, 2023
Answered

Coldfusion 2023 seems to be missing some JAVA

  • September 14, 2023
  • 2 replies
  • 4496 views

var keySpec = createObject("java", "javax.crypto.spec.PBEKeySpec").init(arguments.password.toCharArray(), salt, 128, 80);

 

results in error:

java.lang.reflect.InaccessibleObjectException: Unable to make public boolean com.sun.crypto.provider.PBKDF2KeyImpl.equals(java.lang.Object) accessible: module java.base does not "opens com.sun.crypto.provider" to unnamed module @db99785

 

this code works on previous versions of CF/Java. Is there something I should add to the JVM arguments?

Correct answer xfreeman89x

I’m aware of this function, which is in this repository (https://github.com/marcins/cf-google-authenticator/blob/master/authenticator/GoogleAuthenticator.cfc), and as far as I’m concerned, I don’t feel comfortable having to add exceptions to the server that might increase the attack surface by using the configuration

--add-opens=java.base/some.package.name=ALL-UNNAMED


Instead, I changed the function by using ColdFusion’s native functions that do the same thing. Specifically, the GeneratePBKDFKey function is what we need (https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-functions/functions-e-g/generatepbkdfkey.html).

Here’s the code with the changes:

public string function generateKey(required string password, string salt = "", numeric iterationCount = 128, numeric keySizeBits = 80) 
{
    // Se salt non fornito, generiamo 16 byte casuali
    if (Len(arguments.salt) == 0) {
        arguments.salt = generateRandomString();
    }
    else if (Len(salt) != 16) {
        throw(message="Salt must be 16 bytes", errorCode="GoogleAuthenticator.BadSalt");
    }

    var derivedKey = GeneratePBKDFKey("PBKDF2WithHmacSHA1" , arguments.password, arguments.salt, arguments.iterationCount, arguments.keySizeBits);

    return Base32encode(binaryDecode(derivedKey, "base64"));
}


Hope this helps.

Best regards,
Salvatore

2 replies

BKBK
Community Expert
Community Expert
September 14, 2023

ColdFusion 2023 is not missing the Java class, javax.crypto.spec.PBEKeySpec. It is right there. You can confirm this by running the test code:

 

<cfscript>
    testObject= createObject("java","javax.crypto.spec.PBEKeySpec");
    writedump(testObject);
</cfscript>

 

Though ColdFusion is weakly-typed, newer versions get progressively more accurate with regard to type. So CF2023 might not tolerate code which CF2016 accepted. My guess is that that is the issue you now face.

 

Here and there, your code uses Java types as if they are native CFML types. For example, the constructor, createObject("java", "javax.crypto.spec.PBEKeySpec").init(arguments.password.toCharArray(), salt, 128, 80); requires that the argument salt be a Java byte-array. However, your code is passing it as a ColdFusion array. In fact, the code is mixing up byte-array and CFML-array as if they are interchangeable. Thus, salt is initialized as a ColdFusion array, []. Yet the same variable is used to store buffer.array(), which is a Java byte-array. That might be the cause of the issue.

 

Now, on to a possible solution. I shall assume that the argument salt is coming into the function as a ColdFusion array, not as a Java byte array.

 

Then:

  1. Replace 
    remote string function generateKey (required string password, array salt = [])
    {
        if (arrayLen(salt) == 0)​
     with
    remote string function generateKey (required string password, array salt = [])
    {
        // Convert from CFML array to Java byte-array 
        var saltByteArray=javacast("byte[]",arguments.salt);
        if (arrayLen(arguments.salt) == 0)​
  2.  Replace

 

arguments.salt = buffer.array();
secureRandom.nextBytes(arguments.salt);

 

with

 

saltByteArray=buffer.array();
secureRandom.nextBytes(saltByteArray);

 

3. Replace

 

var keySpec = createObject("java", "javax.crypto.spec.PBEKeySpec").init(arguments.password.toCharArray(), salt, 128, 80);

 

with

 

var keySpec = createObject("java", "javax.crypto.spec.PBEKeySpec").init(arguments.password.toCharArray(), saltByteArray, 128, 80);

 

 



BKBK
Community Expert
Community Expert
September 14, 2023

A demo to illustrate:

<cfscript>
cfArray=[];
byteArray=javacast("byte[]",cfArray);

writeOutput("Type of CF array [] is: <strong>#cfArray.getClass().getName()#</strong>");
writeOutput("<p>");
writeOutput("Type of Java Byte array obtained from [] using Javacast is: <strong>#byteArray.getClass().getSimpleName()#</strong>");
</cfscript>

 

Output of the demo:

 

Charlie Arehart
Community Expert
Community Expert
September 14, 2023

Before presuming that's the issue (or asking for your values passed into the two args, so we can try to recreate it), can you first just edit the file in any way to force its recompilation?

 

Second, in what version did it work? If that is still around, perhaps someone dropped in a class or jar that was being leveraged, such that putting it into cf2023's same folder could fix things. 

/Charlie (troubleshooter, carehart. org)
dwaynea8754223
Known Participant
September 14, 2023

thanks for the quick response.

this is a function in a cfc. not sure I understand the path to force recompilation. I don't believe there are any custom classes or jar file. This was working on CF2016. I believe all Java is native. That said, I read a little about modules, or lack there of, and through maybe the JVM arguments may be required to load the crypto class.

 

 

    /**
    * Generates a Base32 encoded secret key for use with the token functions
    *
    * @9397041 password a password to be used as the seed for the secret key
    * @9397041 salt a Java byte[16] array containing a salt - if left blank a random salt will be generated (recommended)
    * @Return the Base32 encoded secret key
    */
    remote string function generateKey (required string password, array salt = [])
    {
        if (arrayLen(salt) == 0)
        {
            var secureRandom = createObject("java", "java.security.SecureRandom").init();
            var buffer = createObject("java", "java.nio.ByteBuffer").allocate(16);
            arguments.salt = buffer.array();
            secureRandom.nextBytes(arguments.salt);
        }
        else if(arrayLen(salt) != 16)
        {
            throw(message="Salt must be byte[16]", errorcode="Authenticator.BadSalt");
        }

        var keyFactory = createObject("java", "javax.crypto.SecretKeyFactory").getInstance("PBKDF2WithHmacSHA1");
        var keySpec = createObject("java", "javax.crypto.spec.PBEKeySpec").init(arguments.password.toCharArray(), salt, 128, 80);
        var secretKey = keyFactory.generateSecret(keySpec);
        return Base32encode(secretKey.getEncoded());
    }

 

 

 

Charlie Arehart
Community Expert
Community Expert
September 14, 2023

My suggestion was really as simple as I said: just edit the cfc. Make any change at all. Then call it as before. Doing that should cause cf to recompile it (all cfml is compiled to Java).

 

I'm proposing this because sometimes a file that was "working" before can change to "not working" because of some change that affects low-level Java features. It could be a cf update, a JVM update, or a change in cf config files. Forcing a recompilation can cause cf to create Java code that now "works" works after what was that change. 

 

It's just a guess, and the point is that it's easiest to try. You may want to add some change that you can confirm "seeing", as various factors could prevent that immediate recompilation. 

/Charlie (troubleshooter, carehart. org)