Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WebForm Password Generator Tool

0.00/5 (No votes)
18 Oct 2019 1  
In an earlier article, a WebForm password generator was considered; this article presents the results of its implementation.

1. Background Table of Contents

WebForm Generate Password

I recently published a WinForm application that generated passwords ( WinForm Generate Password Tool [^]). As I indicated in that article, I was considering writing the same tool as a WebForm application. This article presents the results.

2. Introduction Table of Contents

Because some readers may take issue with the design and implementation of this tool, I wish to quote John Walker [^].

2.1. Why Encrypt with JavaScript? Table of Contents

At first glance, JavaScript may seem an odd choice for implementing encryption. These programs are rather large and complicated, and downloading and running them them takes longer than would be required for a Java applet or to access a CGI program on a Web server. I chose JavaScript for two reasons: security and transparency.

Security.   The sole reason for encryption is to protect privacy. This means the process cannot involve any link whose security is suspect. If messages were encrypted by a Web server, they would have to pass over the Internet, where any intermediate site might intercept them. Even if some mechanism such as secure HTTP could absolutely prevent the data's being intercepted, you'd still have no way to be sure the site which performed the encryption didn't keep a copy in a file, conveniently tagged with your Internet address.

In order to have any degree of security, it is essential that all processing be done on your computer, without involving any transmission or interaction with other sites on the Internet. A Web browser with JavaScript makes this possible, since the programs embedded in these pages run entirely on your own computer and do not transmit anything over the Internet. Output appears only in text boxes, allowing you to cut and paste it to another application. From there on, security is up to you.

Transparency.   Any security-related tool is only as good as its design and implementation. Transparency means that, in essence, all the moving parts are visible so you can judge for yourself whether the tool merits your confidence. In the case of a program, this means that complete source code must be available, and that you can verify that the program you're running corresponds to the source code provided.

The very nature of JavaScript achieves this transparency. The programs are embedded into the Web pages you interact with; to examine them you need only use your browser's "View Source" facility, or save the page into a file on your computer and read it with a text editor; any JavaScript components the pages reference can be similarly downloaded and examined in source code form. JavaScript's being an interpreted language eliminates the risk of your running a program different from the purported source code: with an interpreted language what you read is what you run.

3. Implementation Table of Contents

Unlike the WinForm Tool, the WebForm Tool is more easily able to separate the user interface from the actual password generation. So the password generator can exist separately from other WebForm components. As a result, there are two JavaScript files: one that generates the password (generate_password.js) and one that supports the user interface (ui.js).

3.1. Password Generator Table of Contents

Generation of a password is accomplished by invoking the global entry point generate_password in the GeneratePassword module.

// ********************************************* generate_password

// global entry point
/// <summary>
/// wrap the creation of character sets and the generation of the
/// password, returning the generated password
/// </summary>
function generate_password ( options )
  {
  var character_sets = [ ];

  character_sets = create_character_sets ( options );

  return ( create_password ( character_sets,
                             options.desired_length ) );

  } // generate_password

The options parameter is a JSON structure that contains the Boolean values for the character sets and the desired length from which the password can be generated.

var options =
  {
  desired_length: MINIMUM_PASSWORD_CHARACTERS,
  lower_case:     true,
  numbers:        true,
  symbols:        true,
  upper_case:     true,
  all_characters: true,
  easy_to_read:   false,
  easy_to_say:    false,

  last_no_comma:0
  };

The values of the options components are set in the user interface.

The password generator is similar to that found in the WinForm implementation, with changes to convert C# to JavaScript. The major problem that was encountered was the lack of a JavaScript equivalent to the GetBytes method of the C# RNGCryptoServiceProvider. Since I wanted to use only "vanilla" Javascript, I was forced into using the dreaded Math.random function.

// ************************************************ random_integer

// local entry point
/// <summary>
/// using the built-in Math.random, generate a uniformly
/// distributed integer random variate in the range [ min, max )
/// </summary>
function random_integer ( min, max )
  {

  return Math.floor ( Math.random ( ) * ( max - min ) ) + min;

  } // random_integer

Note that random_integer is an entry point that is local to the GeneratePassword module.

Determining which characters are to be used in password generation occurs in the local entry point create_character_sets. Unfortunately, there is no universally accepted way of including the character class constants (e.g., export or import). So, DIGITS, LOWERCASE, PUNCTUATION, SPECIAL, and UPPERCASE were required to be copied from ui.js.

Other than the conversion from C# to JavaScript, the only difference between the WinForm implementation and the WebForm implementation of create_character_sets is that the structure holding the character classes is now an array (rather than a List).

// ***************************************** create_character_sets

// local entry point
/// <summary>
/// create an array of character sets that are dependent on the
/// contents of the options JavaScript object (JSON)
/// </summary>
/// <warning>
/// the values of the character class constants DIGITS, LOWERCASE,
/// PUNCTUATION, SPECIAL, and UPPERCASE must duplicate those in
/// ui.js.
/// </warning>
function create_character_sets ( options )
    {
    const DIGITS =      '0123456789';
    const LOWERCASE =   'abcdefghijklmnopqrstuvwxyz';
    const PUNCTUATION = '!#%&()*,-./:,?@[\]_{}"\'';
    const SPECIAL =     '!@#$%^&*()+=~[:\'<>?,.|';
    const UPPERCASE =   'ABCDEFGHIJKLMNOPQRSTUVWXYZ';

    var character_sets = [ ];
    var digits = "";
    var lowercase = "";
    var special = "";
    var uppercase = "";

    if ( options.numbers )              // does user want digits?
      {
      digits = DIGITS;
      }

    if ( options.lower_case )           // does user want lowercase?
      {
      lowercase = LOWERCASE;
      }

    if ( options.symbols )              // does user want symbols?
      {
      special = SPECIAL;
      }

    if ( options.upper_case )           // does user want uppercase?
      {
      uppercase = UPPERCASE;
      }

    if ( options.all_characters )
      {
                                // all_characters => no changes
                                // will made

      }
    else if ( options.easy_to_say )
      {
      digits = "";
      special = "";
      }
    else if ( options.easy_to_read )
      {
                                // remove the ambiguous
                                // characters 01OIoli!|
      if ( ( digits !== null ) && ( digits.length > 0 ) )
        {
        digits = digits.replace ( "0", "" ).
                        replace ( "1", "" );
        }

      if ( ( uppercase !== null ) && ( uppercase.length > 0 ) )
        {
        uppercase = uppercase.replace ( "O", "" ).
                              replace ( "I", "" );
        }

      if ( ( lowercase !== null ) && ( lowercase.length > 0 ) )
        {
        lowercase = lowercase.replace ( "o", "" ).
                              replace ( "l", "" ).
                              replace ( "i", "" );
        }

      if ( ( special !== null ) && ( special.length > 0 ) )
        {
        special = special.replace ( "!", "" ).
                          replace ( "|", "" );
        }
      }

    character_sets.length = 0;
                                // if a the copy of a character
                                // class has a non-zero length,
                                // add it to the array
    if ( uppercase.length > 0 )
      {
      character_sets.push ( uppercase );
      }

    if ( lowercase.length > 0 )
      {
      character_sets.push ( lowercase );
      }

    if ( digits.length > 0 )
      {
      character_sets.push ( digits );
      }

    if ( special.length > 0 )
      {
      character_sets.push ( special );
      }

    return ( character_sets );

    } // create_character_sets

With the character set now reflecting the user's choices, the creation of a new password can commence. As with the WinForm implementation, the WebForm implementation uses a modified version of the password generator algorithm found in kitsu.eb's answer in Generating Random Passwords [^].

The major change to that paradign was to incorporate the character_sets data structure. The structure was passed to create_password as the character_sets parameter. Recall that the character_sets structure contains only those character classes that should participate in the generation of a password. create_password is a local entry point.

// *********************************************** create_password

// local entry point
/// <summary>
/// from the desired characters and the desired length, create a
/// password
/// </summary>
function create_password ( character_sets,
                           desired_length )
  {
  var bytes = [ ];
  var characters = "";
  var i = 0;
  var index = -1;
  var password = "";
                              // get a sequence of random bytes;
                              // unfortunately there is no
                              // equivalent to GetBytes of the
                              // RNGCryptoServiceProvider available
                              // in JavaScript; so we resort to
                              // multiple invocations of
                              // Math.random
  for ( i = 0; ( i < desired_length ); i++ )
    {
    bytes [ i ] = random_integer ( 0, 128 );
    }

  for ( i = 0; ( i < bytes.length ); i++ )
      {
      var b = bytes [ i ];
                              // randomly select a character
                              // class for each byte
      index = random_integer ( 0, character_sets.length );
      characters = character_sets [ index ];
                              // use mod to project byte b
                              // into the correct range
      password += characters [ b % characters.length ];
      }

  return ( password );

  } // create_password

The intermediate variable characters was introduced for readability. I believe the alternative code is unintelligible.

3.2. User Interface Table of Contents

Test Harness

The vast amount of code for this project is the user interface, through which the user's intentions are captured. All screen manipulations occur through methods located in ui.js.

The reader should be aware that both the test harness (to the left in the preceding figure) and the generate password (to the right in the preceding figure) exist in the same html file (index.html). The pertinent HTML is

<body>

<div class="container"
     style="width:99%;
            background-color:#B0E0E6;">

    <div class="subcontainer"
         id="test_harness_div"
         style="width:100%;
                display:block;
                background-color:#B0E0E6;
                padding-bottom:25px;">
      <h3>Test Generate Password</h3>
      <button class="button blue_button"
              style="display:block;
                     margin: auto;
                     margin-bottom:1em;"
              onclick="UI.toggle_display('test_harness_div','generate_password_div')">
        Generate Password
      </button>
      :
      :
    </div>

    <div class="subcontainer"
         id="generate_password_div"
         style="width:100%;
                background-color:#B0E0E6;
                display:none;">

      <div style="width:99%;
                  margin-left:0.5em;">
      :
      :

The pertinent CSS is

body 
  {
  padding-left:10px;
	margin:1em;
	width:350px;
	}

.container
  {
  position: relative;
  }

.subcontainer 
  {
  position: absolute;
  }

and toggle_display is

// ************************************************ toggle_display

// global entry point
/// <summary>
/// if a <div> is displayed, hide it; if a <div> is not displayed,
/// show it
/// </summary>
function toggle_display ( div_1_id, div_2_id )
  {
  var div_1 = document.getElementById ( div_1_id );
  var div_2 = document.getElementById ( div_2_id );

  div_1.style.display = ( div_1.style.display == "none" ?
                                                "block" :
                                                "none");
  div_2.style.display = ( div_2.style.display == "none" ?
                                                "block" :
                                                "none");
  } // toggle_display

toggle_display is the handler for the onclick event for the Generate Password button in the test harness and for the Cancel and Accept buttons in the generate password.

3.2.1. Collecting User Preferences Table of Contents

In order to generate the user's password, the user's preferences need to be collected. This is accomplished by the generate password user interface.

In addition to the generated password, a most important product of the user interface is the options JSON structure. This structure records the user preferences that are used to actually generate a password.

var options =
  {
  desired_length: MINIMUM_PASSWORD_CHARACTERS,
  lower_case:     true,
  numbers:        true,
  symbols:        true,
  upper_case:     true,
  all_characters: true,
  easy_to_read:   false,
  easy_to_say:    false,

  last_no_comma:0
  };

The initial settings require a six-character (MINIMUM_PASSWORD_CHARACTERS) password that contains uppercase, lowercase, numeric, and special characters.

3.2.1.1. Desired Length Table of Contents

The desired length, in characters, of the password is specified through an input element with a type of number. The set_desired_length handler for the onclick event is:

// ******************************************** set_desired_length

// global entry point
/// <summary>
/// given a value of desired password length, place it into the
/// options object and regenerate the password
/// </summary>
function set_desired_length ( up_down_value )
  {

  options.desired_length = up_down_value;
  regenerate_password ( );

  }

When a new password length recorded in options, the password is regenerated.

3.2.1.2. Desired Character Classes Table of Contents

The character classes are specified through input elements with a type of check. Check boxes were chosen to allow more than one character class to be required. The set_checkbox_checked handler for the onclick event is:

// ****************************************** set_checkbox_checked

// global entry point
/// <summary>
/// given a checkbox id and an error message id, set the
/// appropriate Boolean value in the options object and
/// regenerate the password
/// </summary>
function set_checkbox_checked ( checkbox_id,
                                message_id )
  {
  var in_error = false;
  var checkbox = document.getElementById ( checkbox_id );
  var ischecked = checkbox.checked;
  var message = document.getElementById ( message_id );

  message.style.visibility = "hidden";

  switch ( checkbox.id )
    {
    case "lowercase":
      options.lower_case = ischecked;
      break;
    case "numbers":
      options.numbers = ischecked;
      break;
    case "symbols":
      options.symbols = ischecked;
      break;
    case "uppercase":
      options.upper_case = ischecked;
      break;
    default:
      in_error = true;
      message.innerHTML = "Unrecognized CheckBox " + checkbox.id;
      break;
    }

  if ( !in_error )
    {
    if ( !( options.lower_case ||
            options.numbers ||
            options.symbols ||
            options.upper_case ) )
      {
      in_error = true;
      message.innerHTML =
        "At least one checkbox must be checked";
      message.style.visibility = "visible";
      }
    }

  if ( !in_error )
    {
    regenerate_password ( );
    }

  } // set_checkbox_checked

At least one check box must be checked; otherwise an error message will be displayed. When a check box is checked or unchecked, the password is regenerated.

3.2.1.3. Desired Character Restrictions Table of Contents

Restrictions on the character classes are imposed through input elements with a type of radio. Radio boxes were chosen to allow only one restriction to be imposed at a time. The set_radio_checked handler for the onclick event is:

// ********************************************* set_radio_checked

// global entry point
/// <summary>
/// given a radiobutton name and an error message id, set the
/// appropriate Boolean value in the options object and
/// regenerate the password; all radio buttons must be checked
/// to insure that an earlier prior checked that is now unchecked
/// will be detected
/// </summary>
function set_radio_checked ( radiobutton_name,
                             message_id  )
  {
  var in_error = false;
  var message = document.getElementById ( message_id );
  var radio_buttons =
          document.getElementsByName ( radiobutton_name );

  message.style.visibility = "hidden";

  for ( var i = 0; ( i < radio_buttons.length ); i++ )
    {
    var ischecked = radio_buttons [ i ].checked;

    switch ( radio_buttons [ i ].id )
      {
      case "allcharacters":
        options.all_characters = ischecked;
        break;
      case "easytoread":
        options.easy_to_read = ischecked;
        break;
      case "easytosay":
        options.easy_to_say = ischecked;
        break;
      default:
        in_error = true;
        message.innerHTML = "Unrecognized RadioButton " +
                            radio_buttons [ i ].id;
        message.style.visibility = "visible";
        break;
      }
    }

  if ( !in_error )
    {
    regenerate_password ( );
    }

  } // set_radio_checked

In order to maintain a valid representation of the state of the radio buttons in the options JSON structure, the state of all radio buttons must be recorded. For unlike check boxes, only one radio button may be recorded as checked. When a radio button is checked or unchecked, the password is regenerated.

3.2.2. Exiting Password Generation Table of Contents

Whenever password generation is cancelled or a password is accepted, the user is returned to the Test Generate Password display. Again, this is accomplished by invoking toggle_display. If the password generation is cancelled, the display is the same as the initial display of Test Generate Password. However, if the password is accepted, a revised form is displayed.

Accepted Generate Password

In addition to displaying the accepted password, the user can copy the returned password to the Clipboard. This functionality is provided so that the password can be futher copied into another textbox.

 

4. References Table of Contents

5. Conclusion Table of Contents

I have presented a WebForm that provides users with the ability to generate passwords.

6. Browser Compatibility Table of Contents

Browser Compatibility

Neither IE nor Safari toggle between <div>s, making the user interface unworkable. Edge hides the updown portion of the <input type="number"...> control until the mouse hovers over it.

7. Development Environment Table of Contents

The WebForm Generate Password tool was developed in the following environment:

Microsoft Windows 7 Professional SP 1
Microsoft Visual Studio 2008 Professional SP1
JavaScript Lint 0.3.0 (JavaScript-C 1.5 2004-09-24)
Firefox 68.0.2

8. History Table of Contents

10/20/2019 Original article

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here