1. Overview

In this tutorial, we’ll look at generating secure random passwords in Scala.

2. Generating Secure Random Passwords

In this section, we’ll generate secure random passwords with different criteria. We’ll be fixing the password length to 16 characters for this tutorial.

2.1. Generating Alphanumeric Passwords

Let’s generate an alphanumeric password in Scala. We can use the SecureRandom class from the JDK to generate a cryptographically strong random string. Since we are generating an alphanumeric password, we can define the character set as:

val alphanumericChars = ('0' to '9') ++ ('A' to 'Z') ++ ('a' to 'z')

Notably, we utilized the to() method from Range to conveniently define the character sets.

Now that we’ve defined the character set, let’s use it to generate the random password:

def randomAlphaNumericSecurePwd = {
  val random = new SecureRandom()
  Iterator
    .continually(alphanumericChars(random.nextInt(alphanumericChars.length)))
    .take(16)
    .mkString
}

In the above implementation, we use the continually() method from Iterator to choose characters for the password. Subsequently, we take 16 characters from it and concatenate them to create the password.

Alternately, if we only want to generate a random string for non-password usages, we can utilize the scala.util.Random class:

val randomString = Random.alphanumeric.take(16).mkString

2.2. Generating Random Passwords Including Special Characters

In the previous section, we generated a random alphanumeric password. We can generate stronger passwords by including special characters as well. For that, let’s redefine the character set that supports special characters:

val printableChars = (33 to 126).map(_.toChar)

Here, we used the ASCII value range to define the character set for the password. ASCII values from 33 to 126 are generally called printable characters and include both special characters and alphanumerics.

Next, let’s generate the 16-character password using Iterator and the character set:

def randomSecurePwdWithSpecialChars = {
  val random = new SecureRandom()
  Iterator
    .continually(printableChars(random.nextInt(printableChars.length)))
    .take(16)
    .mkString
}

Incidentally, we can generate a non-secure random string using scala.util.Random:

Iterator.continually(Random.nextPrintableChar()).take(16).mkString

As previously emphasized, it’s important to stress that this implementation is not secure for use as a password due to its absence of cryptographic security measures.

2.3. Generating Random Passwords with Exclusions

Certain characters may look similar when displayed in different fonts, causing potential confusion. For instance, the number ‘0’ and the alphabet letter ‘O’ may look similar in some contexts, and this often causes one to be mistaken for the other. This ambiguity can pose complications, especially when used in passwords. As a result, it’s sometimes better to avoid using these characters in the password generation.

Let’s create the list of characters to be excluded from the generation:

val excludedChars = Set('O', 'o', 'l', '1', '0', '`')

Next, we can filter out these characters while generating the password:

def randomSecurePwdWithExclusions = {
  val random = new SecureRandom()
  Iterator
    .continually(printableChars(random.nextInt(printableChars.length)))
    .filterNot(excludedChars.contains)
    .take(16)
    .mkString
}

As a result, this method generates random passwords, excluding the pre-defined characters.

2.4. Generating Random Passwords with a Specific Mix of Characters

At times, we may require a specific combination of lowercase, uppercase, digits, and special characters in a password. For instance, we want to ensure at least one of each in the generated password.

In this section, we’ll explore the implementation of this requirement.

First, let’s define the character set for each of the types:

val lowerLetters = 'a' to 'z'
val upperLetters = 'A' to 'Z'
val numbers = '0' to '9'
val specialChars = IndexedSeq('!', '@', '#', '$', '&', '*', '?', '^', '(', ')')
val fullCharset = lowerLetters ++ upperLetters ++ numbers ++ specialChars

Now, let’s define a generic method that generates a random list of characters from the provided character set:

def pickRandomChars(charSet: IndexedSeq[Char], size: Int): List[Char] = {
  Iterator
    .continually(charSet(random.nextInt(charSet.length)))
    .take(size)
    .toList
}

This is a minor adjustment to the previous random generation method, accepting the character set and the desired number of characters to pick. It’s important to note that the method returns a list of the selected characters.

Subsequently, we can invoke this method for each of the character sets and combine the results:

val passwordChars: List[Char] = pickRandomChars(lowerLetters, 1) ++
  pickRandomChars(upperLetters, 1) ++
  pickRandomChars(numbers, 1) ++
  pickRandomChars(specialChars, 1) ++
  pickRandomChars(fullCharset, 12)

The passwordChars now contains 16 randomly picked characters, ensuring at least one lowercase and uppercase character, a digit, and a special character. It’s worth highlighting that we utilized the fullCharSet to choose the remaining random characters.

Finally, we can shuffle the character list and combine it to create a random password:

Random.shuffle(passwordChars).mkString

We can easily adjust both the character set and password conditions to suit our specific requirements, simplifying the process of generating random passwords.

3. Testing the Implementation

Now that we’re ready with implementations, we need to write unit tests to ensure that the solution is working as expected.

As we are dealing with generating a random password, conventional unit tests can be challenging to implement. However, this situation provides an excellent opportunity for employing property-based testing, given our clear understanding of the unique characteristics of the generated password.

We can utilize ScalaTest along with ScalaCheck to design property-based tests.

Let’s write the test for the case where we are generating a random password with predefined conditions for the number and types of characters:

"generate secure password with mix of condition" in {
  forAll { (_:Unit) =>
    val password = RandomPasswordGenerator.randomSecurePwdWithMix
    password.length shouldBe 16
    password.exists(_.isLower) shouldBe true
    password.exists(_.isUpper) shouldBe true
    password.exists(_.isDigit) shouldBe true
    password.exists(IndexedSeq('!', '@', '#', '$', '&', '*', '?', '^', '(', ')').contains) shouldBe true
  }
}

We converted the password conditions into properties to be checked in the test.

We can use similar property-based checks for the different implementations covered in this tutorial.

4. Conclusion

In this article, we discussed different methods for generating secure random passwords. We explored methods for generating alphanumeric passwords, enhancing security with special characters, excluding specific characters for clarity, and customizing passwords with specific character mixes. Additionally, we employed property-based testing to confirm the proper functioning of the solution.

As always, the sample code used in this tutorial and more test cases are available over on GitHub.