Follow-up on June 2024 CF update: more on change of default algorithm from CFMX_COMPAT
As another case where Adobe is opting to sacrifice compatibility for security, the update changes from using the very old default of CFMX_COMPAT (as the default) to using either of a couple different alternatives, depending on the function. And if you're not careful/paying attention, you could break some critical part of your app by applying this update.
TLDR; In this post, I want to share a bit more to help you understand the impact of this update (which I blogged about in June), whether you're a developer or an administrator--and whether you've applied or not yet applied the update. Even if you HAVE done it and "all seems well" (in test or even in prod), do beware there may be nasty problems waiting to bite you that could take time to crop up. I'll explain the issues, and help you find the code using these functions, then help you determine if that code is or IS NOT affected by this change. I'll also discuss some real-world scenarios and challenges, with solutions.
Finally, I'll explain an available JVM arg (-Dcoldfusion.encryption.useCFMX_COMPATAsDefault=TRUE) that can be used to revert this behavior, for those who may feel they need to sacrifice security for compatibility, so as to get to this June update and take their time to address this change in the encryption default. I also explain how the CFMX_COMPAT algorithm DOES still remain available as an option, despite what some have asserted, which may be an acceptable option to use. Then I wrap up with some thoughts on how it may not be so bad that I'm only getting this post out a few weeks after the June update.
For more, read on.
Contents:
- Background
- What the June update does (and does not) affect
- How to find potentially affected code
- Why might this change of behavior lead to a problem? And how can you handle it?
- On using the new JVM arg to change the default back to CFMX_COMPAT
- Note that CFMX_COMPAT has NOT been removed
- Summary: and on the delay in getting around to all this
Background
Readers of my blog should have seen that I did a blog post the day Adobe released in mid-June an update for ColdFusion 2023 and 2021 (updates 8 and 14, respectively). As always, the main focus of my post was to get the word out about the update (as not everyone notices them, even with the CF Admin feature to indicate a new update is available--which doesn't help those whose servers have no internet access.)But I also gave a heads-up about the risk to beware in this update, reflected in the title, Updates released for ColdFusion 2023/2021, today June 11 2024, and another possible breaking change. And as always I shared in the post the link to Adobe's technote for each version's update (which usually reflect the same info for a given update, though not always), and I also shared a link to the Adobe forum entry that they (Saurav) created to announce the update.
And that technote offered a good bit of info that should have helped most to grok the matter, but I find that sometimes people come away from the technotes still a little mystified. Often in my blog post announcing the update I'll elaborate a bit, but sometimes since I also only learn of the update that day, I don't really get a chance to dig too deeply. This post is my follow-up to that.
(And sorry that it's been a few weeks. Sadly, in my work I help people in dealing with things like CF updates and a LOT more. Sometimes that precludes me getting to write all the posts I might want to, or other matters come up--such as the Java update released last week, or my take on the special "patch" Adobe came out with to help with fallout from the March CF updates.)
What the June update does (and does not) affect
You could be forgiven for either under-estimating or over-estimating what the impact is of the June CF updates. Some came away thinking "this doesn't affect me, as we don't do any encryption", while others said "we only do encryption in one place and we confirmed it's ok". (Again, it has more to do with than JUST encryption/decryption.) Still others might be frozen in panic: "how the heck am I ever going to be able to figure out what's impacted in our code?"
Here's the crux of the matter:
- if you use any of these 7 CFML functions, you have to at least assess your use of them: Encrypt, EncryptBinary, Decrypt, DecryptBinary, Hash, Rand, Randomize. RandRange
- note that each takes an argument that names an algorithm (all 7 functions got that algorithm arg starting with CF7). Sadly, the argument may be the 2nd or 3rd, depending on the function. You've got to look at the docs (which Adobe links to in the update technote, which I link to in my blog post about the update)
- the issue is that if your code does NOT specify that arg naming the algorithm, then prior to this update CF would have used "CFMX_COMPAT" as the value by default
- After the update, such code would now use a DIFFERENT default algorithm: and that's where things can go wrong. If you have in the past created some database record or file or link using that old algorithm, then any CFML code that would now try to "read" that would use the NEW algorthm, which may fail (with an error or just logically be incorrect)
- Note also that the new "default algorithm" varies between the seven different functions. See that Adobe update technote for the details on each
- In brief, after that update the default encryption algorithm for the encrypt/decrypt functions is changing from CFMX_COMPAT to AES/CBC/PKCS5Padding. And for the hash function it is changing from CFMX_COMPAT to SHA-256 hashing algorithm, while finally for the rand* functions it is changing from CFMX_COMPAT to SHA1PRNG.
- If instead your code DOES specify the arg naming an algorithm, then that code does NOT "need" to change
- That said, if you DO specify that old CFMX_COMPAT value as the algorithm you're using, Adobe is suggesting people switch to a better one
So the bottom line is that you need to find any use of these in your code (and I share tips below on doing that, including via different tools), and then you need to determine if your use of those functions needs to change, per the points above.
As for any code you have using these seven CFML functions where you DO specify the algorithm argument, that code is not affected at all by this change in the default.
How to find potentially affected code
So how can you find that potentially affected code? Well, on the surface, you just need to "search your code base". I do want to share a couple of tips on that.
1) First, when it comes to searching for the functions, note that there are 4 prefix values you can search for which will cover all 7 functions: encrypt, decrypt, hash, and rand. That's perhaps easier than looking for all 7. (More on "how" to search for them in a moment.)
Of course, you may well find hits that you can ignore: references to those terms in comments or text, javascript code, etc.
2) Second, as for HOW to search for the code, that will depend on your tools (and your comfort with them). If you use an editor/IDE--whether that's VSCode, Sublime, Notepad++, or perhaps CFBuilder, Dreamweaver, CF Studio (yep, there are CF people still using that), you can easily search for those strings without even need of a regex.
Or note there are tools that are dedicated file text search tools. Everyone has their favorites, whether as Windows or Linux or Mac tools. I'll share that my favorite file text search tool on Windows remains the free (and modern) File Locator Lite tool, which I have written about before.
3) [Update since first posting this:] Pete Freitag let me know in the comments below on Sep 12, 2024 that he had altered his (commercial) Fixinator tool so that it now finds when one has code that uses this cfmx_compat algorithm. Note that it identifies it equally whether your code "implicitly" sets it (a concern for compatibility reasons, which is the real focus of this post) or "explicitly" sets it (still a concern for security reasons).
This begs the question of whether the Adobe Code Security Analysis tool within CFBuilder (the free VSCode-based extension) identifies this. I've not yet checked, and Adobe has not yet said so explicitly, that I know of.
4) [Update since first posting this:] Along the same lines, I thought I should add here also that I have not heard yet of any "patch" from Adobe to help identify when your code does "implicitly" use cfmx_compat (assuming you use the -Dcoldfusion.encryption.useCFMX_COMPATAsDefault=TRUE, or before applying this update). Some readers will know that Adobe came out with such a "patch" after the March updates, to log when code performs "implicit scope searches" that would otherwise be blocked per that update. For more, see my July post following-up on that March update.
With those two updates to this post out of the way, now back to what I had written originally...
Now that you've found the code, what next?
Again, the change per this update ONLY affects you where you have code using those 7 functions but which does NOT specify the algorithm argument. Once you've found such code, what can you do about it? And why should you worry about the problems associated with this change in behavior--whether you change your code or not?
Essentially you have 3 choices (once you find that you have code affected by this change):
- You could indeed "just change" the code to use a different algorithm...but that may have unexpected impacts. I cover this more in the next couple of sections
- Or you could of course change the affected code to EXPLICITLY set it to use "cfmx_compat"...but there are pros and cons to that (but I do want to say it IS possible, though some think it's not). I cover this in subsequent discussions below
- Finally, you could change the JVM arg (-Dcoldfusion.encryption.useCFMX_COMPATAsDefault=TRUE, discussed more below), to let your code continue to implicitly use cfmx_compat as the default (allowing code where you don't set the algorithm to continue to run as before). That still leaves you exposed to the negatives of that old cfmx_compat...you've traded security for compatibility. And I discuss that below also
I'll reiterate and elaborate on these choices as I discuss different scenarios below.
Why might this change of behavior lead to a problem? And how can you handle it?
So why should this change concern you? Well, there are at least a few scenarios where you could get bitten, whether doing encryption/decryption or hashing, for example. These are just a few scenarios: I'm sure there are others.
Encryption challenges
if you have some affected CFML encryption code (which doesn't set the algorithm argument), then prior to this update the value it would have been based on that CFMX_COMPAT algorithm. But the same code running AFTER the update (which doesn't set the algorithm argument) would now produce a DIFFERENT (for the same incoming value and encryption key), because it would be using the new default algorithm.And if you store the result of such encryption in a database or file or perhaps an email or URL that has been shared, the later attempt to DECRYPT that value may either FAIL or simply produce a DIFFERENT result--in which case some logical test or use of the decrypted value will be incorrect. Let's look a little closer.
What if you have used encryption for data stored in a database?
At least if you stored that encrypted value in a database, you know how it was encrypted before this change (using cfmx_compat, if none was specified in your code before this update), so you have multiple choices:
- you could change your code (doing the encryption and the decryption) to instead formally USE the CFMX_COMPAT value. That code will now "keep on working" (though Adobe would argue that the CFMX_COMPAT algorithm is old and insecure). Don't believe those who are asserting that CFMX_COMPAT has been removed: it has not. I'll reiterate this point below. This is an option you can consider on a case-by-case basis, if you may have different code/use cases doing such encryption/decryption
- you could use the "lifeline" (or "training wheels") that Adobe has offered with this update, and implement a new JVM argument that reverts CF to the previous behavior, where the default algorithm (if none is specified) is again "CFMX_COMPAT". That's a temporary option available only until CF2025, but at least it allows you to still be able to apply the June update--which has other benefits security fixes beyond this one issue. More on this option below. Again, this just changes the default for code that does NOT specify an algorithm. You could still do either the change above or the next one
- Otherwise if you ARE motivated to change your algorithm, you could perform a process to extract your encrypted data from the database (decrypting it with "cfmx_compat", if that's how it was encrypted), and then re-encrypt it with a better encryption algorithm (the new default or one you choose) and store it back into the database. Then you could change your code to use that new (better) encryption algorithm going forward.
Do note that a subtlety about changing to using a new encryption algorithm (other than CFMX_COMPAT) is that the encrypt* and decrypt* functions also take a required "key" argument (in addition to the string to be encrypted), and in the case of CFMX_COMPAT that key could be any string. But with all the other algorithms, this "key" value must be something generated from the CFML function called generatesecretkey. See the CFML docs for more, such as for encrypt, for example.
What if you have used encryption for data "sent elsewhere"?
But what if you may have used the encryption mechanism (without an algorithm specified) to create a value that was sent to someone in an email or to create some URL that has been shared? Or perhaps you offer an API where people can obtain something you encrypted and THEY have stored it. In these cases you may have no way to "get the encrypted value back" to re-encrypt it (like in the database discussion above).You still have at least 3 choices, like in the previous section. But if you DON'T want to change your decryption code to revert to CFMX_COMPAT using either the 1st or 2nd options just listed, you will have to deal with the fact that if you rely on the new default (or choose some better algorithm), some later attempt to decrypt such a value (in an email or url, for example) may fail (if you can't readily tell somehow which algorithm was used).
One thought is that you could implement error handling that tries to decrypt it using the new algorithm such that it fails, you could then try using CFMX_COMPAT. It's imperfect, but I'm just spit-balling here. Perhaps we'll hear from others in comments as they discuss how they may have dealt with all this.
(Again, if you do change to using a new algorithm for the encrypt/decrypt functions, you will need to change their second argument (the "key") to use generatesecretkey() instead of just any string.)
Hashing challenges
If you've got code using the CFML hash function (and you didn't specify the algorithm to use), then this leads to perhaps a more challenging situation: first, again the hashed result for a given input value will be different (before and after this update--for any code doing a hash but that does not include the argument naming the hashing algorithm).
So for example, you may be hashing password values before storing them in a database. This is a widely recommended better practice compared encrypting them--because it's a one-way process: you are given a password and you hash it, which produces a very random string which you store in the database. And then when you are TESTING a password against that stored value, you hash that password to be checked and COMPARE THAT HASHED VALUE to what's stored in the database. On the surface, it's great. No one can ever figure out what the password was, that was stored in the database because the hashing is a one-way process (that's the generally accepted understanding, at least.)
The problem is that now you ALSO cannot "recover" any of the hashed passwords to "re-hash them with a new algorithm" as was "so easy" to do above regarding encrypted values stored in a database (and people who hash passwords may still encrypt other sensitive database, so you may have to deal with both situations).
In this scenario, since you don't HAVE the values that would need to be "reprocessed", it would seem these are your options. They are similar to those listed above:
- Change your code (that does the hash without specifying an algorithm) to go ahead and specify CFMX_COMPAT as the algorithm. Some might argue that it's perhaps not quite AS important to change to a more secure algorithm for hashing. You need to study and decide that for yourself
- Implement the JVM argument (mentioned in the previous section) which reverts CF to the previous behavior, so the default algorithm (if none is specified) is again "CFMX_COMPAT". Once more: that's a temporary option available only until CF2025. More on this option below. But it buys you time to deal otherwise with this problem, while still being able to apply the June update. Again, this just changes the default for code that does NOT specify an algorithm. You could still do either the change above or the next one
- If you WOULD want to use a new algorithm for some code that creates a hash, such as to store a hashed password in a database (going forward), it would seem you'd need to resort to creating a new column in your database to store such a NEWLY/differently hashed password and perhaps another column tracking THAT you're using the newly hashed password. Then modify your code that both CREATES and USES such a hashed password to work differently based on these two scenarios
That last suggestion may feel hacky/kludgy. Again, I'm just spit-balling here: perhaps someone will offer different/better ideas for resolving this dilemma (where you have code doing a hash but which had no algorithm--and you don't want to use CFMX_COMPAT going forward, but you need to deal with values hashed using the old CFMX_COMPAT in the past).
At least with hash, there is no "key" arg (like with the encrypt/decrypt functions), so no need to worry about switching it from a simple string to using generatesecretkey().
What about the rand* functions?
On the surface, it would seem that the rand* functions (rand, randomize, and randrange) will generally be easier to deal with. You don't normally need to "work backward" from such a randomized value, such that if your code starts using a new algorithm (by default, because none is specified in your code) it will just create new differently-random values.
Before we wrap up, let me elaborate a bit on the notion of setting CF to still use CFMX_COMPAT.
On using the new JVM arg to change the default back to CFMX_COMPAT
As I noted at the open (and have now reiterated a couple of times), Adobe did recognize that this change (trading compatibility for security) might prove to be "too much" for some folks to readily attend to (to find and change their code). But again this June update entails MORE than just this one change: what if you want to get to this June update but do NOT want to (or can't) change your affected code (as discussed above)?
Just like with the key change in the March 2024 update (regarding searchimplicitscopes), Adobe has offered an option in this release allowing you to revert CF back to its previous behavior of using CFMX_COMPAT as the default algorithm (in the affected functions, and where you do not specify their algorithm argument).
As discussed in the June update's technotes (for each of CF2023 and 2021, linked to in my first blog post on the June update), that JVM argument is:
Before doing this, not a couple of things:
First, as for where to put such a JVM arg, you could add it--carefully--in the CF Admin's "java & jvm" page and its "java args" field, or via the jvm.config file (such as in CF's cfusion/bin folder) and its java.args line. In either case, don't add such a JVM arg as a "new line" there: all the args are technically all on a single line. Either add it to the end of the line or after some other arg there, being sure to leave a space before or after any other. (But do NOT put a space WITHIN that argument above, including before or after the "=", which is a mistake Adobe initially made in the technotes.) Any such mistake could keep CF from starting--which would also keep you from getting back into the CF Admin to change the args, if that's where you changed them.
Then also, as I noted above, implementing this JVM arg changes ONLY the default algorithm for code that does NOT specify an algorithm. Code that already does name an algorithm (or that you change in addressing these issues) will work just fine using whatever algorithm you name in that function.
But again this is workaround you can rely on only TEMPORARILY: Adobe makes clear in the technotes for this update that the JVM arg will NOT function in CF2025 and beyond. (Since it's a -D argument, it's not that "ColdFusion will fail to start if you leave the argument in beyond CF2023." It will simply be IGNORED in CF2025 and beyond--and then you will HAVE to deal with the code changes discussed above, for any of the seven functions where you don't specify an algorithm.)
As a final aside, some may want to hear this clarification: Adobe has NOT provided with this update any sort of application-level setting for changing the default algorithm for these seven functions, like they did for the searchimplicitscopes setting discussed in the March 2024 update. That's as much because (as discussed at the outset and in the update technotes), the default is changing to different algorithms for the different functions.
Note that CFMX_COMPAT has NOT been removed
Finally, as I've also said a couple of times above, you absolutely CAN change your code (where you use these functions but fail to name an algorithm) to that the code instead DOES specify to use the "old" CFMX_COMPAT algorithm. This is even stated specifically in the June update technotes: "Note that CFMX_COMPAT is still available."
Again, Adobe discourages people from using it (as indeed they long have, referring to it as "the least secure" algorithm since they added more and more new ones starting with CF7, aka CFMX7.)
But for some situations, using that specific "old" algorithm (naming it, in your code) may be the right solution, or an acceptable one, at least may be a better short-term workaround than using the JVM arg (which again affects ALL use of the affected functions where code doesn't name an algorithm).
Summary: and on the delay in getting around to all this
So, phew, that was another marathon post. Why would I need to say so much when Adobe's discussion of the whole matter is just a couple dozen sentences? Well, I'll let you judge if somehow I've added "needless melodrama" or "over-explaining" to this matter. I think all that I said above needed to be said, judging from the help I have been offering people the past month, either in the community or as clients of my troubleshooting consulting services.
And indeed, while I am sorry I didn't get this note out sooner after the June update, the time since the update (released while I and others were at CFCamp in Germany that week of June) has allowed me the opportunity to learn more and solidify my own understanding of things as I've helped others. That delay can be seen as helping you. :-)
Speaking of delays, one more bit of "good news" (that might diminish the pain of this being delayed getting posted) is that some folks are indeed themselves delayed in getting around to this June update: many are still reeling and dealing with the March 2024 update. Or perhaps in their haste they opted to just put in the jvm arg (to deal with the key change in either or each of the updates).
Lastly, I'll say again that I am open to more ideas from folks either on the scenarios I described or the solutions I proposed. If I get enough new ideas, I may even create a new post on that more specifically (and of course I would credit anyone who shared a new idea, unless you opted to remain anonymous in your comment).
As always, hope all this was helpful.
For more content like this from Charlie Arehart:Need more help with problems?
- Signup to get his blog posts by email:
- Follow his blog RSS feed
- View the rest of his blog posts
- View his blog posts on the Adobe CF portal
- If you may prefer direct help, rather than digging around here/elsewhere or via comments, he can help via his online consulting services
- See that page for more on how he can help a) over the web, safely and securely, b) usually very quickly, c) teaching you along the way, and d) with satisfaction guaranteed
Now if you do fix this by adding the CFMX_COMPAT algorithm to your encrypt or decrypt calls, Fixinator will still mark that as an issue, since it is not considered a secure algorithm.
Great post as always Charlie!
So while it will indeed help find "any" use of cfmx_compat (explicit or implicit), folks who seek a way to find ONLY the implicit uses will have to consider what I offered. Or they can use fixinator, but realizing it will identify code that would not "break" before the update (or without the new jvm arg set to true). As you say they should consider changing the algorithm...or at least setting it to "cfmx_compat" to maintain compatibility (at the cost of security).
Then later runs of fixinator can remind them they've left it set explicitly. :-)
When you say
"Change your code (that does the hash without specifying an algorithm) to go ahead and specify CFMX_COMPAT as the algorithm. Some might argue that it's perhaps not quite AS important to change to a more secure algorithm for hashing. You need to study and decide that for yourself"
Should this be MD5 which was the default?
As for your question, you feel md5 was "the default" when? I'm referring to it had been cfmx_compat BEFORE the update, which is the crux of the article.
I know it's a lot of info to keep track of. :-) If you see something I could/should improve, do let me know.
hash("something", "CFMX_COMPAT") - in other words you are both right.
But to you AND Tony I would point out that the technote for the June updates does clearly say that "the default encryption algorithm of Hash function has changed from CFMX_COMPAT to SHA-256 hashing algorithm".
Note that makes no mention of MD5 (neither the technote for CF2021 update 14 nor CF2021 update 8, which are those June updates.) More on that in a moment.
First, when I replied to Tony asking if he was referring to before or after the update, it's just that I knew the default HAD changed: I didn't recall what it changed TO. :-) But after confirming the above, I have also now updated both this blog post AND my one from June to quote what Adobe says in the technote about what the defaults WERE and now ARE.
But that still leaves this confusion about MD5. And I think I may see where it's coming from. I notice that the current CFML Reference for hash (https://helpx.adobe....) has its table of arguments/parameters, with the "algorithm" arg's available values, and it does indeed show among those "MD5: (default)", which might suggest that IS (or WAS at some point) the default.
But beyond what the technote says, I'll also note how the top of that same CFML Reference page for Hash in the "history" section says again clearly: "ColdFusion (2023 release) Update 8 and ColdFusion (2021 release) Update 14: Changed the default algorithm from CFMX_COMPAT to SHA-256."
So I appreciate Tony's confusion now. And indeed now I'm confused. :-)
Either it's that the top of that page and the technote are right (and the parameters indication of MD5 as the default is wrong), or it's the other way around. Again, I was just going on what the technote said. :-)
Pete, have you done any closer analysis of this? Or Tony, or anyone? If anyone can prove that either NOW or (especially) BEFORE the update, the default algorithm IS or WAS MD5, we'd need to get them to correct that page AND the technote (and then my blog posts).
I know what would need to be done to do it. I just can't commit to doing it right now. But since you both are interested in this, perhaps you will or have, or again perhaps Pete or anyone has more to share.
Take a look at this code example: https://trycf.com/gi...
1) First, thanks for the example to prove that indeed if one DID use cfmx_compat then that WOULD indeed be an identical result to using MD5. And fair enough. That was valuable to learn.
2) But your example doesn't show using NO argument, nor using the SHA-256 option.
And indeed if you modify the code to add those (I have, below), you will see that indeed BEFORE the update (or such as on CF2018, which that trycf.com site does permit), the resulting hash (if done WITHOUT any argument) is indeed the same as either CFMX_COMPAT or MD5.
But if run AFTER the update, the resulting hash (if done WITHOUT any argument) is indeed the same as SHA-256 .
That means the technote and history section (and my blog posts) have it right.
3) But the parameters section is wrong to still indicate that MD5 is the default (and it's CONFUSING also as it did not clarify that CFMX_COMPAT was the same as specifying MD5). I'll bring that to their attention.
In any case, to Tony's question, yes you COULD change the code to use MD5 (which happens to be the same as CFMX_COMPAT, in the case of hash alone), to remain "backward-compatible". Again, thanks for clarifying THAT point, Pete.
4) Even so, to your final point, "if you say MD5 is the default or you say CFMX_COMPAT is the default they both are technically correct", well, that is in fact NOT technically correct. :-) Again the default HAS changed per this update to SHA-256.
In case anyone needs proof, I offer the two additional hash examples (with no arg and with sha-256) AND I also print out the CF version (and update level) in my slight modification to Pete's code, here:
https://trycf.com/gi...
First, beware that on trycf.com, the CF2021 version offered there is from update 12--BEFORE the update 13 in June. So it shows the same result as CF2018, not CF2023.
But if you try the same code at Adobe's cffiddle.org, you'll see both CF2021 and 2023 showing the same hash results--as they updated both to the latest CF versions, and no jvm args.
Indeed, the one thing that is NOT easy to reflect in that code is whether the new jvm arg (to revert the cfmx_compat default) has been used. If it has (or if testing against CF2023 or 2021 BEFORE the JUNE updates, or CF2018 or earlier), then the 3rd line (no arg) will be the same as the first 2 (cfmx_compat/md5).
AFTER the update (and without the JVM arg reverting things) the 3rd line will be the same as the 4th (sha256).
And apologies to any who's heads are spinning or feel that all this all "too much". But I think for some readers the clarifications will be useful.
1) Great
2) I didn't show using no argument in my example because it is now SHA-256, I was just trying to show that MD5 and CFMX_COMPAT are the same thing for the hash algorithm.
3) Yes, I don't know why the ever called it CFMX_COMPAT because it is just MD5. For encrypt/decrypt it makes sense that they call it CFMX_COMPAT because they made up their own algorithm so it is not a standard algorithm, but for the hash they used a standard algorithm.
4) Ah, I think this is where you thought I was disputing that it changed. I was just trying to point out that the hashing algorithm used when you specify CFMX_COMPAT is the MD5 algorithm, so the result is equivalent. I should have said: "if you say MD5 was the PREVIOUS default or CFMX_COMPAT was the PREVIOUS default they are both technically correct".
Also, I corrected a couple of minor mistakes in my last comment where I once wrote sha256 for sha-256, and another time I mistakenly referred to aes256. They should both be sha-256, and now are.