[[Passwords]]
= Passwords

ifdef::env-github[]
:tip-caption: :bulb:
:note-caption: :information_source:
:important-caption: :heavy_exclamation_mark:
:caution-caption: :fire:
:warning-caption: :warning:
endif::[]

One of the core features of WildFly Elytron is the ability to work with many different formats for representing passwords, WildFly Elytron also contains APIs that can be used to convert from clear text passwords to these representations that can be stored in the underlying identity stores.

This section will document how these APIs can be used to work with the different password types.

:toc:

== PasswordFactory

Working with passwords will require interaction with the `org.wildfly.security.password.PasswordFactory` API, this obtains access to implementations from `java.security.Provider` instances, there are two different ways these can be identified: -

 . By querying the globally installed Providers
 . By passing in the `Provider` to the `getInstance` method of the `PasswordFactory` API.

In both cases the WildFly Elytron implementations are provided by the `org.wildfly.security.WildFlyElytronProvider` provider.

When relying on the Provider being globally registered the `PasswordFactory` for a specific algorithm can be obtained as: -

[source,java]
----
PasswordFactory passwordFactory = PasswordFactory.getInstance(algorithm);
----

However an alternative approach could be to manually instantiate the Provider and pass it in when obtaining an instance of the `PasswordFactory`: -

[source,java]
----
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
...
PasswordFactory passwordFactory = PasswordFactory.getInstance(algorithm, ELYTRON_PROVIDER);
----

== Clear Password

The simplest type of `Password` to obtain from the `PasswordFactory` is a clear text password, the following code illustrates how this can be obtained.

[source,java]
----
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
static final String TEST_PASSWORD = "test_password";

public static void main(String[] args) throws Exception {
    PasswordFactory passwordFactory = PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR, ELYTRON_PROVIDER);

    ClearPasswordSpec passwordSpec = new ClearPasswordSpec(TEST_PASSWORD.toCharArray());
    Password password = passwordFactory.generatePassword(passwordSpec);

    System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(password, TEST_PASSWORD.toCharArray())));
}
----

A second approach is to obtain a raw representation of the `ClearPassword`, however this will need to be translated into the `PasswordFactory` if it will be used for evidence validation but if the `Password` is not being used for verification this can be a suitable alternative provided the parameters have been pre-verified.

[source,java]
----
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
static final String TEST_PASSWORD = "test_password";

public static void main(String[] args) throws Exception {
    PasswordFactory passwordFactory = PasswordFactory.getInstance(ClearPassword.ALGORITHM_CLEAR, ELYTRON_PROVIDER);

    Password rawPassword = ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, TEST_PASSWORD.toCharArray());

    Password password = passwordFactory.translate(rawPassword);

    System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(password, TEST_PASSWORD.toCharArray())));
}
----

The raw password can be used for other areas of the Elytron APIs however if it is used for validation an error similar to the following will be thrown.

[source,java]
----
Exception in thread "main" java.security.InvalidKeyException
    at org.wildfly.security.password.impl.PasswordFactorySpiImpl.engineVerify(PasswordFactorySpiImpl.java:762)
    at org.wildfly.security.password.PasswordFactory.verify(PasswordFactory.java:210)
[source,java]
----

[[simple-digest]]
== Simple Digest

The next type of password is the simple digest, for this password type the clear text password id digested using the specified algorithm however no salt is used.

The following algorithms are applicable to this type of encoding.

 * simple-digest-md2
 * simple-digest-md5
 * simple-digest-sha-1
 * simple-digest-sha-256
 * simple-digest-sha-384
 * simple-digest-sha-512

The following example illustrates how the `Password` instance can be created starting from the clear text password,from this the digested representation of the password can be obtained which could be used to store the password.  The digested representation is then used to create a further `Password` instance.

[source,java]
----
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
static final String TEST_PASSWORD = "test_password";

public static void main(String[] args) throws Exception {
    PasswordFactory passwordFactory = PasswordFactory.getInstance(SimpleDigestPassword.ALGORITHM_SIMPLE_DIGEST_SHA_512, ELYTRON_PROVIDER);

    ClearPasswordSpec clearSpec = new ClearPasswordSpec(TEST_PASSWORD.toCharArray());
    SimpleDigestPassword original = (SimpleDigestPassword) passwordFactory.generatePassword(clearSpec);

    byte[] digest = original.getDigest();
    HashPasswordSpec hashSpec = new HashPasswordSpec(digest);

    SimpleDigestPassword restored = (SimpleDigestPassword) passwordFactory.generatePassword(hashSpec);

    System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
}
----

Starting from the digest the raw APIs can be used as.

[source,java]
----
SimpleDigestPassword rawPassword = SimpleDigestPassword.createRaw(SimpleDigestPassword.ALGORITHM_SIMPLE_DIGEST_SHA_512, digest);

SimpleDigestPassword restored = (SimpleDigestPassword) passwordFactory.translate(rawPassword);

System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
----

[[salted-digest]]
== Salted Digest

Two variations of salted digests are supported, these can either be digested with the salt first or the password first.  The following algorithms are supported: -

 * password-salt-digest-md5
 * password-salt-digest-sha-1
 * password-salt-digest-sha-256
 * password-salt-digest-sha-384
 * password-salt-digest-sha-512
 * salt-password-digest-md5
 * salt-password-digest-sha-1
 * salt-password-digest-sha-256
 * salt-password-digest-sha-384
 * salt-password-digest-sha-512

The following example shows how using a generated salt the password can be created from a clear text password and then subsequently how the password can be recreated from the salt and digest.

[source,java]
----
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
static final String TEST_PASSWORD = "test_password";

public static void main(String[] args) throws Exception {
    PasswordFactory passwordFactory = PasswordFactory.getInstance(SaltedSimpleDigestPassword.ALGORITHM_PASSWORD_SALT_DIGEST_SHA_512, ELYTRON_PROVIDER);

    byte[] salt = new byte[32];
    SecureRandom random = new SecureRandom();
    random.nextBytes(salt);

    SaltedPasswordAlgorithmSpec saltedSpec = new SaltedPasswordAlgorithmSpec(salt);
    EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec(TEST_PASSWORD.toCharArray(), saltedSpec);

    SaltedSimpleDigestPassword original = (SaltedSimpleDigestPassword) passwordFactory.generatePassword(encryptableSpec);

    byte[] digest = original.getDigest();

    SaltedHashPasswordSpec saltedHashSpec = new SaltedHashPasswordSpec(digest, salt);

    SaltedSimpleDigestPassword restored = (SaltedSimpleDigestPassword) passwordFactory.generatePassword(saltedHashSpec);

    System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
}
----

Alternatively the salt could be generated automatically by using a `ClearPasswordSpec`.

[source,java]
----
ClearPasswordSpec clearSpec = new ClearPasswordSpec(TEST_PASSWORD.toCharArray());
SaltedSimpleDigestPassword original = (SaltedSimpleDigestPassword) passwordFactory.generatePassword(clearSpec);

byte[] salt = original.getSalt();
byte[] digest = original.getDigest();
----

Starting with the digest and salt the raw APIs can also be used.

[source,java]
----
SaltedSimpleDigestPassword rawPassword = SaltedSimpleDigestPassword.createRaw(SaltedSimpleDigestPassword.ALGORITHM_PASSWORD_SALT_DIGEST_SHA_512, digest, salt);

SaltedSimpleDigestPassword restored = (SaltedSimpleDigestPassword) passwordFactory.translate(rawPassword);

System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
----

== Digest

The Digest passwords are an alternative form of digest where the username, realm and password are digested together delimited with a ':', these are usable with clear text authentication mechanisms but also usable with the digest authentication mechanisms also eliminating the transmission of clear text passwords during authentication.  The following algorithms are supported: -

 * digest-md5
 * digest-sha
 * digest-sha-256
 * digest-sha-384
 * digest-sha-512
 * digest-sha-512-256

The following example illustrates how a password can be created from the username, realm, and password and then how it can be recreated from the digest, realm, and password.

[source,java]
----
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
static final String TEST_USERNAME = "test_username";
static final String TEST_REALM = "Test Realm";
static final String TEST_PASSWORD = "test_password";

public static void main(String[] args) throws Exception {
    PasswordFactory passwordFactory = PasswordFactory.getInstance(DigestPassword.ALGORITHM_DIGEST_MD5, ELYTRON_PROVIDER);

    DigestPasswordAlgorithmSpec digestAlgorithmSpec = new DigestPasswordAlgorithmSpec(TEST_USERNAME, TEST_REALM);
    EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec(TEST_PASSWORD.toCharArray(), digestAlgorithmSpec);

    DigestPassword original = (DigestPassword) passwordFactory.generatePassword(encryptableSpec);

    byte[] digest = original.getDigest();

    DigestPasswordSpec digestPasswordSpec = new DigestPasswordSpec(TEST_USERNAME, TEST_REALM, digest);

    DigestPassword restored = (DigestPassword) passwordFactory.generatePassword(digestPasswordSpec);

    System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
}
----

For this password type is not possible to create the password from the `ClearPasswordSpec` as additional information always needs to be specified and can not be dynamically or randomly generated, however the raw APIs can still be used.

[source,java]
----
DigestPassword rawPassword = DigestPassword.createRaw(DigestPassword.ALGORITHM_DIGEST_MD5, TEST_USERNAME, TEST_REALM, digest);

DigestPassword restored = (DigestPassword) passwordFactory.translate(rawPassword);

System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
----

[[scram]]
== SCRAM

Another set of passwords more tightly tied to a specific authentication mechanism are the SCRAM password types, the following algorithms are supported: -

 * scram-sha-1
 * scram-sha-256
 * scram-sha-384
 * scram-sha-512

The following demonstrates how a clear password can be converted to a scram password using a specified salt and iteration count and how this can be recreated from the digested value.

[source,java]
----
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();

static final String TEST_PASSWORD = "test_password";

public static void main(String[] args) throws Exception {
    PasswordFactory passwordFactory = PasswordFactory.getInstance(ScramDigestPassword.ALGORITHM_SCRAM_SHA_512, ELYTRON_PROVIDER);

    byte[] salt = new byte[12];
    SecureRandom random = new SecureRandom();
    random.nextBytes(salt);

    IteratedSaltedPasswordAlgorithmSpec iteratedAlgorithmSpec = new IteratedSaltedPasswordAlgorithmSpec(2000, salt);
    EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec(TEST_PASSWORD.toCharArray(), iteratedAlgorithmSpec);

    ScramDigestPassword original = (ScramDigestPassword) passwordFactory.generatePassword(encryptableSpec);

    byte[] digest = original.getDigest();

    IteratedSaltedHashPasswordSpec scramPasswordSpec = new IteratedSaltedHashPasswordSpec(digest, salt, 2000);

    ScramDigestPassword restored = (ScramDigestPassword) passwordFactory.generatePassword(scramPasswordSpec);

    System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
}
----

Alternatively instead of using the `IteratedSaltedPasswordAlgorithmSpec` is it also possible to use a `SaltedPasswordAlgorithmSpec` when converting from the clear text password and a default iteration count will be used instead, this is similar to how the conversion happens for <<salted-digest, salted digest>> passwords.

It is also possible to omit the salt and iteration count and these will be generated.

[source,java]
----
ClearPasswordSpec clearSpec = new ClearPasswordSpec(TEST_PASSWORD.toCharArray());

ScramDigestPassword original = (ScramDigestPassword) passwordFactory.generatePassword(clearSpec);

byte[] salt = original.getSalt();
byte[] digest = original.getDigest();
int iterationCount = original.getIterationCount();
----

Starting with a digest, salt, and iteration count the raw APIs can also be used.

[source,java]
----
ScramDigestPassword rawPassword = ScramDigestPassword.createRaw(ScramDigestPassword.ALGORITHM_SCRAM_SHA_256, digest, salt, 2000);

ScramDigestPassword restored = (ScramDigestPassword) passwordFactory.translate(rawPassword);

System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
----

== OTP

One more type of mechanism specific password is the one time password type.  The following algorithms are supported: -

 * otp-md5
 * otp-sha1
 * otp-sha256
 * otp-sha384
 * otp-sha512

The following demonstrates how a clear password can be converted to a one time password using a specified seed and iteration count and how this can be recreated from the hashed value.

[source,java]
----
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();
static final String TEST_PASSWORD = "test_password";

public static void main(String[] args) throws Exception {
    PasswordFactory passwordFactory = PasswordFactory.getInstance(OneTimePassword.ALGORITHM_OTP_SHA_512, ELYTRON_PROVIDER);

    String seed = "ke1234";
    int sequenceNumber = 500;

    OneTimePasswordAlgorithmSpec oneTimeAlgorithmSpec = new OneTimePasswordAlgorithmSpec(OneTimePassword.ALGORITHM_OTP_SHA_512, seed, sequenceNumber);
    EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec(TEST_PASSWORD.toCharArray(), oneTimeAlgorithmSpec);

    OneTimePassword original = (OneTimePassword) passwordFactory.generatePassword(encryptableSpec);

    byte[] hash = original.getHash();

    OneTimePasswordSpec oneTimeSpec = new OneTimePasswordSpec(hash, seed, sequenceNumber);

    OneTimePassword restored = (OneTimePassword) passwordFactory.generatePassword(oneTimeSpec);
}
----

This example does not include verification as that is handled by the SASL mechanism which also increments the sequence and replaces the hash, this does mean this password type needs to be used with a security realm which also supports updates.

Starting with the hash and sequence number the raw APIs can also be used.

[source,java]
----
OneTimePassword rawPassword = OneTimePassword.createRaw(OneTimePassword.ALGORITHM_OTP_SHA_512, original.getHash(), original.getSeed(), original.getSequenceNumber());

OneTimePassword restored = (OneTimePassword) passwordFactory.translate(rawPassword);
----

[[bcrypt]]
== Other Iterated Salted Types

The following algorithms are also supported for alternative iterated salted password types: -

 * bcrypt
 * sun-crypt-md5
 * sun-crypt-md5-bare-salt
 * crypt-sha-256
 * crypt-sha-512
 * bsd-crypt-des

The general pattern for working with these password types is the same as was used for <<scram, Scram>> password types if an interaction count is specified or the same as <<salted-digest, salted digest>> password types if a default iteration count is to be used instead.

== Other Salted Types

The following algorithms are also supported for salted password types: -

 * crypt-md5
 * crypt-des
 
The general pattern for working with these password types is the same as was used for <<salted-digest, salted digest>> password types as no iteration count is required.

== Masked Password Types

Finally a set of masked password types are also supported to add support for legacy password types which were previously supported within PicketBox, the following algorithms are supported.

 * masked-MD5-DES
 * masked-MD5-DES-CBC-PKCS5
 * masked-MD5-3DES
 * masked-MD5-3DES-CBC-PKCS5
 * masked-SHA1-DES-EDE
 * masked-SHA1-DES-EDE-CBC-PKCS5
 * masked-SHA1-RC2-40
 * masked-SHA1-RC2-40-CBC-PKCS5
 * masked-SHA1-RC2-128
 * masked-SHA1-RC2-128-CBC-PKCS5
 * masked-SHA1-RC4-40
 * masked-SHA1-RC4-40-ECB
 * masked-SHA1-RC4-128
 * masked-SHA1-RC4-128-ECB
 * masked-HMAC-SHA1-AES-128
 * masked-HMAC-SHA224-AES-128
 * masked-HMAC-SHA256-AES-128
 * masked-HMAC-SHA384-AES-128
 * masked-HMAC-SHA512-AES-128
 * masked-HMAC-SHA1-AES-256
 * masked-HMAC-SHA224-AES-256
 * masked-HMAC-SHA256-AES-256
 * masked-HMAC-SHA384-AES-256
 * masked-HMAC-SHA512-AES-256
 * masked-PBKDF-HMAC-SHA1
 * masked-PBKDF-HMAC-SHA224
 * masked-PBKDF-HMAC-SHA256
 * masked-PBKDF-HMAC-SHA384
 * masked-PBKDF-HMAC-SHA512

[source,java]
----
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();

static final String TEST_PASSWORD = "test_password";

public static void main(String[] args) throws Exception {
    PasswordFactory passwordFactory = PasswordFactory.getInstance(MaskedPassword.ALGORITHM_MASKED_MD5_DES, ELYTRON_PROVIDER);

    char[] key = "my_secret_key".toCharArray();

    byte[] salt = new byte[8];
    SecureRandom random = new SecureRandom();
    random.nextBytes(salt);

    int iterationCount = 100;

    MaskedPasswordAlgorithmSpec maskedAlgorithmSpec = new MaskedPasswordAlgorithmSpec(key, iterationCount, salt);
    EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec(TEST_PASSWORD.toCharArray(), maskedAlgorithmSpec);

    MaskedPassword original = (MaskedPassword) passwordFactory.generatePassword(encryptableSpec);

    byte[] masked = original.getMaskedPasswordBytes();

    MaskedPasswordSpec maskedPasswordSpec = new MaskedPasswordSpec(key, iterationCount, salt, masked);

    MaskedPassword restored = (MaskedPassword) passwordFactory.generatePassword(maskedPasswordSpec);

    System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
}
----

As with the other password types the raw password APIs can also be used to recreate the password.

[source,java]
----
MaskedPassword rawPassword = MaskedPassword.createRaw(MaskedPassword.ALGORITHM_MASKED_MD5_DES, key, iterationCount, salt, masked);

MaskedPassword restored = (MaskedPassword) passwordFactory.translate(rawPassword);

System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
----

[[modular-crypt]]
== Modular Crypt Encoding

A number of password types can be encoded using modular crypt allowing information such as the password type, the hash or digest, the salt, and the iteration count to be encoded in a single String, this can make storage and retrieval of passwords easier as multiple pieces of related data can be handled as one.

Within the WildFly Elytron project the utility `org.wildfly.security.password.util.ModularCrypt` can be used to handle the encoding and decoding.

The following password types can be encoded and decoded: -

 * `BCryptPassword`
 * `BSDUnixDESCryptPassword`
 * `UnixDESCryptPassword`
 * `UnixMD5CryptPassword`
 * `SunUnixMD5CryptPassword`
 * `UnixSHACryptPassword`

The following code demonstrates this for a `BSDUnixDESCryptPassword`: -

[source,java]
----
static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();

static final String TEST_PASSWORD = "myPassword";

public static void main(String[] args) throws Exception {
    PasswordFactory passwordFactory = PasswordFactory.getInstance(BSDUnixDESCryptPassword.ALGORITHM_BSD_CRYPT_DES, ELYTRON_PROVIDER);

    int iterationCount = BSDUnixDESCryptPassword.DEFAULT_ITERATION_COUNT;

    byte[] salt = new byte[BSDUnixDESCryptPassword.BSD_CRYPT_DES_SALT_SIZE];
    SecureRandom random = new SecureRandom();
    random.nextBytes(salt);

    IteratedSaltedPasswordAlgorithmSpec iteratedAlgorithmSpec = new IteratedSaltedPasswordAlgorithmSpec(iterationCount, salt);
    EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec(TEST_PASSWORD.toCharArray(), iteratedAlgorithmSpec);

    BSDUnixDESCryptPassword original = (BSDUnixDESCryptPassword) passwordFactory.generatePassword(encryptableSpec);

    String modularCryptString = ModularCrypt.encodeAsString(original);

    Password rawPassword = ModularCrypt.decode(modularCryptString);

    BSDUnixDESCryptPassword restored = (BSDUnixDESCryptPassword) passwordFactory.translate(rawPassword);

    System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, TEST_PASSWORD.toCharArray())));
}
----

NOTE: The `Password` returned from the call to `ModularCrypt.decode(...)` is a raw password so needs translating by the `PasswordFactory`.

