BoxLang 🚀 A New JVM Dynamic Language Learn More...

cf-passwordhash

v1.0.0 Projects

cf-passwordhash

A single bundled JAR plus a ColdFusion CFC that backports the ColdFusion 2025 passwordHashGenerate / passwordHashVerify password-hashing API to CF2016-CF2025, Lucee 5/6/7, and BoxLang. Drop in the JAR, call the same function names you will use on CF2025, and a future engine upgrade needs no code changes and no data migration.

Why this exists

ColdFusion 2025 introduced a unified password-hashing API covering Argon2, BCrypt, and SCrypt. That API is stranded on CF2025 alone. If your application runs on an older Adobe CF version, Lucee, or BoxLang, you previously had no standard way to produce or verify those hashes. This library gives you the identical call signatures and byte-for-byte compatible hash output on older engines, so upgrading to CF2025 later is a no-op at both the code and the database layer.

Supported engines

All eleven configurations below pass the full no-TestBox test suite:

Engine Tested version
Adobe CF2016, 2018, 2021, 2023, 2025
Lucee5, 6, 7
BoxLanglatest (native, adobe-compat, lucee-compat)

On BoxLang the library needs the bx-compat-cfml module so that createObject("java", ...) and the CFC behave the way they do on Adobe CF and Lucee. The bundled BoxLang server configs install it on first run.

Hash compatibility

  • Argon2, BCrypt, SCrypt - hashes produced by this library are byte-for-byte identical to hashes produced by CF2025's native functions. Parity is proven bidirectionally: hashes generated here verify on CF2025, and hashes generated on CF2025 verify here.
  • PBKDF2 - CF2025 has no native PBKDF2 function, so there is nothing to be compatible with. This library uses a self-describing format: $pbkdf2-sha256$i=ITERATIONS$SALT$HASH. It also verifies legacy colon-delimited iterations:salt: hash (SHA1) hashes so you can migrate existing records without a bulk rehash.

Installation

Choose exactly one of these two methods. Using both at the same time double-loads the JAR and causes class conflicts.

Copy lib/cf-passwordhash-1.0.0.jar into a folder that is declared as libDirs in your server.json:

{
    "app": {
        "libDirs": "./lib"
    }
}

Restart the server. The JAR is on the classpath automatically.

Option 2 - Application.cfc javaSettings

Copy lib/cf-passwordhash-1.0.0.jar into a lib/ folder next to your Application.cfc, then add:

this.javaSettings = [
    "loadPaths":               [ expandPath("./lib") ],
    "loadColdFusionClassPath": false,
    "reloadOnChange":          false
];

Usage - CFC

Copy PasswordHash.cfc into your project root (or any mapped folder). Then:

svc = new PasswordHash();

// Generate a hash using the default algorithm (Argon2)
hash = svc.passwordHashGenerate( password );

// Verify a password against a stored hash (algorithm auto-detected from the hash prefix)
ok = svc.passwordHashVerify( password, hash );

// Specify an algorithm explicitly
hash = svc.passwordHashGenerate( password, "BCrypt" );
ok   = svc.passwordHashVerify( password, hash, "BCrypt" );

// Pass custom options (ordered struct literal)
hash = svc.passwordHashGenerate( password, "Argon2", [ "MEMORYCOST": 19456, "CPUCOST": 2 ] );

// Inspect a stored hash
info = svc.passwordHashInfo( hash );
// returns a map describing the hash. Keys are uppercase and vary by algorithm
// (for example ALGORITHM, plus the cost parameters embedded in the hash).
// Read keys with bracket access and the exact uppercase name so it works the
// same on Adobe CF, Lucee, and BoxLang:
alg = info[ "ALGORITHM" ];

Usage - global shim (CF2025-style unqualified calls)

On any engine that does NOT have the native CF2025 functions, include the shim once - either in Application.cfc or at the top of each template that needs it:

<cfinclude template="/path/to/include/PasswordHashFunctions.cfm">

After that, call the functions exactly as you would on CF2025:

hash = passwordHashGenerate( password );
ok   = passwordHashVerify( password, hash );
info = passwordHashInfo( hash );

Important: do NOT include the shim on CF2025. The shim declares functions with the same names as the built-ins, which causes a name collision. On CF2025 the native functions are already available - just call them directly or use the CFC wrapper.

Algorithm options

Argon2 (default)

Option Default Notes
SALTLENGTH16bytes
HASHLENGTH32bytes of derived key
PARALLEL1parallelism factor
MEMORYCOST4096kilobytes of memory
CPUCOST3time-cost iterations

BCrypt

Option Default Notes
ROUNDS10log2 cost factor (4-31)
VERSION$2aBCrypt version string

SCrypt

Option Default Notes
SALTLENGTH16bytes
KEYLENGTH32bytes of derived key
PARALLEL1parallelism factor
MEMORYCOST8block size parameter (r)
CPUCOST16384CPU/memory cost (N), must be power of 2

PBKDF2

Option Default Notes
CPUCOST210000iteration count (time cost)
SALTLENGTH16bytes
KEYLENGTH32bytes of derived key

Building from source

A prebuilt JAR is committed to lib/ so you do not need to build. If you want to build from source:

On Windows:

pwsh ./build.ps1

On macOS/Linux:

./build.sh

The build scripts download Bouncy Castle (bcprov-jdk18on-1.78.1.jar) from Maven Central, compile the Java source with javac --release 8, assemble a fat shaded JAR, and copy it to both dist/ and lib/.

Running the tests

Start a server for any engine:

box server start serverConfigFile=server.cf2025.json

Then open http://localhost:8925/tests/runner.cfm in a browser (replace the port with the one for the engine you started). The runner executes all test files and reports pass/fail counts.

Server config files are provided for all eleven tested configurations. Port assignments:

File Engine Port
server.cf2016.jsonAdobe CF 20168916
server.cf2018.jsonAdobe CF 20188918
server.cf2021.jsonAdobe CF 20218921
server.cf2023.jsonAdobe CF 20238923
server.cf2025.jsonAdobe CF 20258925
server.lucee5.jsonLucee 58935
server.lucee6.jsonLucee 68936
server.lucee7.jsonLucee 78937
server.boxlang.jsonBoxLang8940
server.boxlang-adobe.jsonBoxLang (Adobe)8941
server.boxlang-lucee.jsonBoxLang (Lucee)8942

Project structure

cf-passwordhash/
  lib/
    cf-passwordhash-1.0.0.jar   prebuilt fat JAR (Bouncy Castle bundled)
  include/
    PasswordHashFunctions.cfm   optional global-shim UDFs
  tests/
    runner.cfm                  test runner (no TestBox required)
    test-argon2.cfm
    test-bcrypt.cfm
    test-scrypt.cfm
    test-pbkdf2.cfm
    test-cfc.cfm
    test-shim.cfm
    test-native-2025.cfm        parity test against CF2025 built-ins
    ...
  PasswordHash.cfc              CFC wrapper
  Application.cfc               dev application component
  index.cfm                     quick-start demo
  box.json
  build.ps1
  build.sh

License

MIT - see LICENSE.

This library bundles Bouncy Castle (bcprov-jdk18on 1.78.1). See THIRD-PARTY-NOTICES.txt for the Bouncy Castle licence text.

$ box install cf-passwordhash

No collaborators yet.
   
  • {{ getFullDate("2026-06-29T02:15:27Z") }}
  • {{ getFullDate("2026-06-29T02:15:28Z") }}
  • 33
  • 1