tl;dr
Your last two characters are not missing, they are undefined.
Your faulty math resulted in code points 155 & 138 which are not assigned to any character in Unicode.
Details
For real work, as others commented:
- Your generateded passwords will be too short.
- Your generated passwords will be too predictable.
- For such important tasks, you should rely on proven libraries that are already written, reviewed, and tested.
But for fun, I will take on your challenge.
Last two characters missing
First to address your specific question:
output that I get when I use 79 as my seed: koLN59 This is the output I am supposed to get when using 79 as my seed: koLN59#c I am not sure why the last two characters are not showing up.
As commented by Andreas, your math is wrong.
You are trying to get a couple of specific characters, #
NUMBER SIGN at code point 35, and c
LATIN SMALL LETTER C at code point 99.
To get there, you are starting with a positive random number from 0-32 inclusive (33 exclusive). Then you add to 126 (which in Unicode is TILDE ~
). So there is no way mathematically to get code points 35 & 99 when you are adding any positive random integer to 126.
Furthermore, you should not be adding small numbers to 126. Code points 127 to 159 inclusive are not assigned in Unicode. See Wikipedia list of code points & characters.
Code points 155 & 138 not defined in Unicode.
➥ Your code is resulting in rand1
being code point 155, and rand2
being code point 138. Neither 155 nor 138 are defined in Unicode. Hence nothing appearing on your console.
See this version of your code:
package work.basil.demo.pw;
import java.util.Random;
public class Broken
{
public static String generateRandomPassword ( )
{
int seed = 79;
Random rand = new Random( seed );
char lowerCase1 = ( char ) ( rand.nextInt( 26 ) + 97 );
char lowerCase2 = ( char ) ( rand.nextInt( 26 ) + 97 );
char upperCase1 = ( char ) ( rand.nextInt( 26 ) + 65 );
char upperCase2 = ( char ) ( rand.nextInt( 26 ) + 65 );
int num1 = rand.nextInt( 10 );
int num2 = rand.nextInt( 10 );
char rand1 = ( char ) ( rand.nextInt( 33 ) + 126 );
char rand2 = ( char ) ( rand.nextInt( 33 ) + 126 );
System.out.println( "rand1 int = " + ( int ) rand1 );
System.out.println( "rand2 int = " + ( int ) rand2 );
String tempPass = ( "" + lowerCase1 + lowerCase2 + upperCase1 + upperCase2 + num1 + num2 + rand1 + rand2 );
System.out.println( "length of tempPass: " + tempPass.length() );
return tempPass;
}
public static void main ( String[] args )
{
System.out.println(
Broken.generateRandomPassword()
);
}
}
… run live at IdeOne.com. Notice the length: 8, not 6. You are getting the last two characters but they are not defined. So no glyph appears.
rand1 int = 155
rand2 int = 138
length of tempPass: 8
koLN59
char
type is obsolete
Avoid the char
type in Java. This type is now legacy, unable to represent even half of the 143,859 characters defined in Unicode.
Instead, use the code point integer numbers assigned to characters in Unicode. These range from zero to just over a million.
In the code below, we just use the String
type.
Separate UI from logic
Tip: Software is usually better designed when following the rule of Separation of concerns. Separate out different features and responsibilities into separate chunks of code.
Generally your user-interface code should be separated from your business logic. In your case, this means separating your console interactions with the user from the code that generates the passwords.
Notice in code below that we have one class with a method for the password generation. We have a separate class that uses the password-generator class using hard-coded values. You should add a third separate class that uses Scanner
to interact with the user in order to invoke the password-generator.
Example code
Rather than play math tricks with code point integers, I would work with arrays or lists of your various permitted characters. Then use a random number as an index into that string to retrieve a particular character.
Here we use a cryptographically strong random number generator, SecureRandom
, rather than Random
.
This code uses the new records feature in Java 16. But you could rewrite that easily for earlier Java.
package work.basil.demo.pw;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class PasswordGenerator
{
// Static fields
static private int[] lowercase = "abcdefghijklmnopqrstuvwxyz".codePoints().toArray();
static private int[] uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".codePoints().toArray();
static private int[] digits = "1234567890".codePoints().toArray();
static private int[] punctuation = "!@#$%".codePoints().toArray();
// Member fields.
java.security.SecureRandom random = new java.security.SecureRandom();
public String generate ( final int minLength , final int maxLength , final int minLowercase , final int maxLowercase , final int minUppercase , final int maxUppercase , final int minDigits , final int maxDigits )
{
// Validate inputs.
int maxTotal = ( maxLowercase + maxUppercase + maxDigits );
if ( maxTotal >= maxLength )
{
throw new IllegalArgumentException( "Your maximums for lowercase, uppercase, and digits meets or exceeds maximum password length. That leaves no room for punctuation. Message # c525776c-24c1-42de-8638-75cd2ae92f53." );
}
// Prepare to build password.
int passwordLength = this.randomInRange( minLength , maxLength );
final List < String > characters = new ArrayList <>( passwordLength );
// Build password
record Input(int[] array , int count)
{
}
final List < Input > inputs =
new ArrayList <>(
List.of(
new Input( lowercase , this.randomInRange( minLowercase , maxLowercase ) ) ,
new Input( uppercase , this.randomInRange( minUppercase , maxUppercase ) ) ,
new Input( lowercase , this.randomInRange( minDigits , maxDigits ) )
) );
inputs.add( new Input( punctuation , passwordLength - inputs.stream().mapToInt( input -> input.count ).sum() ) );
for ( Input input : inputs )
{
for ( int i = 0 ; i < input.count ; i++ )
{
final int index = this.random.nextInt( input.array.length );
final int codePoint = input.array[ index ];
characters.add( Character.toString( codePoint ) );
}
}
Collections.shuffle( characters );
// Report new password.
String result = characters.stream().collect( Collectors.joining() );
System.out.println( "result = " + result + " with length: " + result.length() );
return result;
}
private int randomInRange ( int minInclusive , int maxInclusive )
{
return ( int ) ( ( this.random.nextDouble() * ( maxInclusive + 1 - minInclusive ) ) + minInclusive ); // The maximum is *exclusive*, so add one to make it effectively inclusive.
}
public static void main ( String[] args )
{
System.out.println(
new PasswordGenerator().generate( 8 , 10 , 2 , 3 , 2 , 3 , 1 , 2 )
);
}
}
Java 17 getting enhanced random generators
While Java 16 is current today, Java 17 later this year will get more features in the area of random number generators.
See: JEP 356: Enhanced Pseudo-Random Number Generators.