The spring-security-crypto module is the standalone cryptography toolkit inside Spring Security, and it exists so you can hash passwords, encrypt data, and generate keys without dragging in filters, authentication managers, or the rest of the framework. It ships as its own jar, org.springframework.security:spring-security-crypto, and depends on nothing beyond the JDK. That makes it a common pick for batch jobs, CLI tools, and services that need BCrypt but not a login flow.
Most teams meet this module indirectly: it comes along when you add spring-boot-starter-security. But you can also declare it on its own. The trap is treating its APIs as interchangeable. Password encoders, the Encryptors factory, and the key generators each solve a different problem, and using the wrong one, or the wrong constructor, is where the real risk lives.
What lives in the module
Three packages carry the weight. org.springframework.security.crypto.password holds the PasswordEncoder interface and its implementations. org.springframework.security.crypto.encrypt holds the Encryptors factory for symmetric encryption. org.springframework.security.crypto.keygen holds BytesKeyGenerator and StringKeyGenerator for salts and tokens.
To pull it in without the full starter, add the dependency directly:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
<version>6.3.1</version>
</dependency>
Check your Spring Boot BOM before pinning a version so the crypto module lines up with the rest of your Spring Security jars. Version drift between spring-security-crypto and spring-security-core is a frequent source of NoSuchMethodError at runtime.
Password hashing done right
For storing passwords, reach for PasswordEncoder. The default recommendation from the Spring team is BCryptPasswordEncoder, or DelegatingPasswordEncoder when you need to support multiple formats during a migration.
PasswordEncoder encoder = new BCryptPasswordEncoder();
String hash = encoder.encode("s3cr3t-value");
boolean ok = encoder.matches("s3cr3t-value", hash);
BCrypt generates its own random salt and stores it inside the hash string, so you never manage salts yourself. The work factor defaults to 10; bump it to 12 or 13 on modern hardware, measuring the cost so a single verification stays in the tens-of-milliseconds range rather than seconds.
If you are on Spring Security 5.8 or later and want a memory-hard algorithm, Argon2PasswordEncoder and SCryptPasswordEncoder are both in the module. They pull in BouncyCastle, so weigh that transitive dependency. Whatever you choose, wrap it with PasswordEncoderFactories.createDelegatingPasswordEncoder(). That prefixes every hash with an identifier like {bcrypt} so you can rotate algorithms later without a flag day.
Never use NoOpPasswordEncoder outside a throwaway test, and never reach for a bare MessageDigest SHA-256 loop for passwords. A fast hash is exactly what an attacker wants when they crack a dump offline.
Symmetric encryption with Encryptors
When you need to encrypt data at rest, not hash it, use the Encryptors factory. Encryptors.stronger(password, salt) gives you AES-256 in GCM mode with authenticated encryption, which means tampering is detected on decrypt.
String salt = KeyGenerators.string().generateKey();
TextEncryptor textEncryptor = Encryptors.text("a-strong-passphrase", salt);
String cipher = textEncryptor.encrypt("card-token-1234");
String plain = textEncryptor.decrypt(cipher);
Two things bite people here. First, the salt argument must be a hex-encoded string, which is exactly what KeyGenerators.string() produces; passing a raw passphrase throws. Second, Encryptors.text historically used CBC mode, so prefer Encryptors.stronger when you want GCM. Treat the password argument as a key-derivation input, store it in a secrets manager, and never commit it.
For a fuller treatment of how third-party crypto libraries slip into your build unnoticed, our writeup on software composition analysis covers how transitive crypto dependencies get flagged.
Generating keys, salts, and tokens
The keygen package produces cryptographically secure random bytes backed by SecureRandom. Use KeyGenerators.secureRandom(16) for a 128-bit salt, or KeyGenerators.string() for a hex string suitable for the Encryptors salt argument. For opaque tokens such as a password-reset nonce, generate more entropy, at least 32 bytes, and base64-url encode the result before putting it in a link.
BytesKeyGenerator gen = KeyGenerators.secureRandom(32);
byte[] token = gen.generateKey();
Do not roll your own new Random() for anything security-relevant. java.util.Random is predictable; the whole point of this package is that it wires SecureRandom for you.
Keeping the dependency healthy
Because spring-security-crypto sometimes pulls BouncyCastle transitively for Argon2 and SCrypt, that library becomes part of your attack surface. Cryptography dependencies get CVEs like any other code, and a vulnerable BouncyCastle version can undermine the very primitives you added the module to get. Pin versions through the Spring BOM, watch advisories on the GitHub Security tab for spring-projects/spring-security, and run a dependency scan on every build so a downgraded or vulnerable crypto jar does not slip in transitively. An SCA tool such as Safeguard can flag a stale BouncyCastle pulled in three levels deep, which manual review usually misses.
Compare your current versions against the Maven Central release notes before upgrading, and read the migration guide when moving across a major line so a renamed encoder constructor does not surprise you in production.
FAQ
Is spring-security-crypto safe to use without the rest of Spring Security?
Yes. The module has no dependency on the servlet layer or the authentication framework. You can add just spring-security-crypto to hash passwords or encrypt data in a plain Java service or a batch job.
Which PasswordEncoder should I pick?
Start with BCryptPasswordEncoder wrapped in a delegating encoder. Move to Argon2PasswordEncoder if you need a memory-hard function and can accept the BouncyCastle dependency. Avoid any fast general-purpose hash for passwords.
Why does Encryptors throw on my salt value?
The salt argument to Encryptors.text and Encryptors.stronger must be a hex-encoded string, not an arbitrary passphrase. Generate it with KeyGenerators.string().generateKey().
Does the module include asymmetric encryption?
No. spring-security-crypto covers password hashing, symmetric encryption, and key generation. For RSA or elliptic-curve work, use the JDK's java.security APIs or a library like BouncyCastle directly.