Creating and Verifying Digital Signatures

A straightforward method for creating and verifying digital signatures in HTML forms


January 08, 2009
URL:http://www.drdobbs.com/tools/creating-and-verifying-digital-signature/212701413

Rafael Palacios is a researcher at the Instituto de Investigacion Tecnologica and Assistant Professor at the School of Industrial Engineering, Universidad Pontificia Comillas.


Web-based forms provide an efficient way of interacting with users. HTML anchor elements ("links") let users jump from one page to another by clicking on active text or figures, but many jumps may be required to reach the page of interest. On the other hand, by using a small form consisting of just one text field, a search tool can process strings entered by users and instantly find pages they want to see. However, standard HTML forms cannot verify user identity, so they must be considered equivalent to paper forms anonymously sent via mail, and not those signed and delivered in person.

In some cases it is possible to identify users by requesting a login and password. With Web services, for instance, users already registered to the system can log in and send information via forms, while the server recognizes the author. But the problem is how to subscribe for the first time or in general how to identify a person without using a login and password. Sometimes the subscription process may request an email address where the system sends a code that is required to continue the process. Such approach verifies the email account of the user, but not user identity.

Another problem is that on-line stores simply trust the data entered by users with the only basic check they make is the validity of the credit-card number. Nonetheless, many on-line stores only send merchandise to the mail address registered at the credit card, so given that the identity of the person placing the order cannot be verified; at least whatever is bought with a credit card will be sent to the owner of the card who may eventually return the goods. If the persona who places the order could be verified, there will be less risk associated to accepting an order, even if the products are not materials goods but on-line services, software licenses or electronic material. In addition, there are many situations in which a signature is required by law, such as government-related paperwork,insurance forms, legal agreements, and the like.

In common HTML forms, users fill in the requested data and hit the Submit button. Figure 1 is a sample form comprising all input object types. After the Submit button, data are sent through the Internet and processed at a server, which generates a typical congratulations page that's presented to users.

Figure 1: HTML form

In the approach we propose here, users must have at least one personal certificate previously installed in the web browser. Now when the Submit button is pressed, a pop-up window appears before the information is actually sent. The window (Figure 2) shows the text to be signed, a pull-down menu to select which certificate will be used to sign the data, and a text field to grab the master password that protect all the certificates installed in the web browser. After signing the text, form data are sent to the server along with the digital signature generated.

Figure 2: Dialog window for signing the information entered in the form

The signing process is performed in a single JavaScript function (Listing One) that can be easily added to any existing form.

<SCRIPT type="text/JavaScript">
<!-- Begin
function signForm(theForm, theWindow, validation, varnames) {
    //header text
   var texttbs = "I affirm the following information:\n";
    //header text, spanish version
    //var texttbs = "Certifico que la siguiente informacion es cierta:\n";
   var vars = "";        //to store variable names
   var signature = "";   //to store the digital signature of texttbs
   var elem;
   var formSize = theForm.elements.length;
   for(var i = 0; i < formSize; i++) {
      elem = theForm.elements[i];
      switch (elem.type) {
         case "hidden":
         case "button":
         case "submit":
         case "reset":
         case "image":
         case "password":
         case "file":
            // Do not include previous elements in the text to be signed.
            break;
         case "select-one":
            var selectValue = elem.options[elem.selectedIndex].value;
            texttbs += elem.name + "=" + selectValue + "\n";
            vars += elem.name + ",";
            break;
         case "select-multiple":
            for(var op = 0; op < elem.length; op++) {
               if(elem.options[op].selected) {
                  texttbs += elem.name + "=" + elem.options[op].value + "\n";
                  vars += elem.name + ",";
               }
            }
            break;
         case "radio":
            if(elem.checked) {
               texttbs += elem.name + "=" + elem.value + "\n";
               vars += elem.name + ",";
            }
            break;
         case "checkbox":
            if(elem.checked) {
               texttbs += elem.name + "=" + elem.value + "\n";
               vars += elem.name + ",";
            }
            break;
         //In the case of unchecked radio buttons and checkboxes, variables
         //are not sent, hence they must not be signed.
         default: //input text
            texttbs += elem.name + "=" + elem.value + "\n";
            vars += elem.name + ",";
      }
   }
   //Digital signature
   signature = theWindow.crypto.signText(texttbs, "ask");
   if (signature.substr(0,5)=="error") {
      alert("Signature not created\n" + signature);
      return false;
   }
   //Store signature an vars in hidden inputs of the form
   theForm.signature.value = signature;
   theForm.varnames.value= vars;
   return true;
}
// End -->
</script>
Listing One: Definition of signForm function

The digital signature attached to the form achieves two main goals:

When the information is received at the server, it is necessary to verify the sender (by exploring the certificate) and to verify the signature, which involves checking that the data received corresponds to the data digitally signed. PHP functions are provided to perform the verification process in the server.

To complete the whole process, a timestamp signature could be provided by the server, which will be equivalent to getting a stamping copy of the paper delivered. This final step of the process is not part of the current paper.

Preparing a Form for Digital Signatures

HTML forms are standard HTML files that contain one pair of <FORM> and </FORM> labels within the body section. In between the form labels one can find several object definitions such as text fields, radio buttons, pull-down menus, and the like. These objects are created with special labels which have a name attribute that defines the variable name that holds the value entered by the user while filling up the form. Usually at the bottom of the page, and before the </form> label, a special object called "submit" is created. This object appears on the page as a Submit button (see CGI Programming with Perl, Second Edition, by Shishir Gundavaram, Scott Guelich, and Gunther Birznieks).

For error checking, or to help the user filling up the form, each input object can define JavaScript functions associated to some events such as OnChange, OnMouseOver, OnClick, and so on. In addition, the form label can also define a JavaScript function that will be called upon submission. In our proposed approach for digital signature, the only JavaScript action necessary is the one defined within the form label.

When the browser loads a web page that contains a form definition, it creates the graphical objects corresponding to the input labels defined in the HTML code, and lets users interact with those objects. Afterwards, when the user hits the Submit button, the browser gathers all the information entered into the input objects and sends variable names and corresponding values to the web server defined in the <FORM> label. If a JavaScript function is called before the data is actually sent, that function may have access to all form variables. A typical JavaScript application is to perform error checking before the data is actually sent to the server. In this article, we show how to generate a digital signature within a JavaScript function called signForm.

To enable digital signature in an existing form, previously created with any HTML editor, just three small changes must be done:

  1. Add the provided JavaScript code which defines signForm function. This code is added to the head section of the page.
  2. Add an OnSubmit parameter to the form label to call signForm function upon submission:

    <form action="verify.php" method="post" onSubmit="return signForm(this, window);">

  3. Add two hidden form obejcts to hold the digital signature and the names of the variables in the form:

    <p><input type="hidden" name="signature"></p>
    <p><input type="hidden" name="varnames"></p>

As you can may see in Figure 2, the text that the user must sign is a list of variable names and values. Consequently, the programmer of the form must use meaningful variable names and meaningful option values. In general, html forms use variable names and values that only make sense to the programmer since those names are not shown to the user (except if an advanced user decides to analyze the source code of the page).

How signForm Works

This function gathers data contained in form input objects, then generates a digital signature by calling crypto.signText. The function signText is part of the JavaScript language included in some browsers like Mozilla/Firefox and Netscape (though not in the current version of Internet Explorer). Until signText function becomes supported by IE, a workaround is to write a JavaScript function that calls CryptoAPI library, which can be made available to IE through Capicom ActiveX control (see Introducing CAPICOM, by John Lambert).

To make signForm useful for any HTML form, regardless of the amount of variables and the specific names of those variables, an algorithm to find all objects in the form was implemented (see Listing One). The number of objects in the form can be found in theForm.elements.length. Then a for loop is used to access each element using the statement elem = theForm.elements[i];.

The main properties of each element are:

Within the for loop, two string variables are built -- one to store the text to be signed (texttbs) and another one to store the variable names in the precise order in which they have been found in the form (vars). The variable texttbs is built appending the name of each form variable, the character "=", and the corresponding value.

At the end of the loop, the text to be signed is sent to signText function that shows the signing dialog (Figure 2). The result of signText is a digital signature in PKCS#7 format (see PKCS #7: Cryptographic Message Syntax Standard) that is stored in signature variable.

Finally, the signature is copied to the hidden element of the form called signature, and the vars string is copied to the hidden element called varnames. These hidden objects are treated like any other form object, so their variable names, as well as their values, are sent to the server along with all other objects in the form.

It is important to point out that the text to be signed is not sent to the server, since the server must build it again to avoid unencumbered tricks by data misrepresentation, as it will be explained in the following section.

Some input objects (such as the pull down menu) may show a text to the user other than the value stored in the variable of the object. For example:


<option value="spain">Spain (Espaqa)</option>

Both strings can be extracted from the element within the JavaScript function:


elem.options[elem.selectedIndex].value;  //extracts "spain"
elem.options[elem.selectedIndex].text; //extracts "Spain (Espaqa)"

Although the text label may be easier to understand by the user while signing, the value property is the information sent to the server at submission. Hence, it is more appropriate to work with the value instead of the text label, and accordingly the programmer must use meaningful values instead of values such as 01, 02, 03...

Server-side Verification Functions

Many form processing servers use programs written in PHP programming language to deal with the data received. These programs typically perform some checking for required variables and range of values. In addition, many publicly accessible forms also include CAPTCHA images that require additional verification in the server.

If the checking process fails, the PHP program generates an error page; otherwise it continues the process storing values in a database and generates a congratulation message.

To implement a digital signature procedure, the program running at the server must be able to check the digital signature that arrives along with the other variables of the form, prior to accepting and storing the information. In this section of the paper three helpful functions for processing signed forms are provided:

Signature Verification

In Listing Two, Validation() utilizes the two hidden variables varnames and signature created by the JavaScript function signForm. It stores the digital signature in a temporary file called validation_XXX.pem, adding the standard heading and footer for PKCS#7 data stored in PEM format. A number, generated with PHP function uniqid, is added to the name of the temporary file to let the server run in parallel several copies of the program without mixing files.

<html>
<head>
      <title>Verification Results</title>
</head>
<body>
   <h1>Verifying and storing the data sent to this server
   </h1>
<?php
function Validation($signature,$varnames)
{
   /*** Creates validation file comprising the signature ***/
   $uniq_id=uniqid();
   $fp=fopen("validation_".$uniq_id.".pem","w");
   fwrite($fp,"-----BEGIN PKCS7-----\n");
   fwrite($fp,$signature);
   fwrite($fp,"\n-----END PKCS7-----");
   fclose($fp);
   /*** Creates original_text, based on varnames ***/
   /* extract variable names */
   $i=0;
   $beg=0;
   $len=strlen($varnames);
   do{
      $end=strpos($varnames,',',$beg);
       if (!($end===false)) {
      $var_name[$i]=substr($varnames,$beg,$end-$beg);
      $i=$i+1;
      $beg=$end+1;
      }
   }while(($beg<$len) && !($end===false));
   $num_var=$i;

   /* Builds orginal_text */
   $original_text="I affirm the following information:\n";

   for($i=0;$i<$num_var;$i++){
      $original_text.=$var_name[$i]."=".$_REQUEST[$var_name[$i]]."\n";
   }
   /*** Creates original_text file ***/
   $fp=fopen("original_text_".$uniq_id.".txt","w");
   fwrite($fp,$original_text);
   fclose($fp);
   /*** Identifies CA ***/
   $ca=GetCA($signature);
   if ($ca==="/C=ES/O=FNMT/OU=FNMT Clase 2 CA\n") $root_cert="fnmt.pem";
   else if ($ca==="/[email protected]/C=ES/ST=Madrid/L=Madrid/ O=Universidad Pontificia Comillas/OU=STIC/CN=CA www.upcomillas.es\n") $root_cert="comillas.pem";
   else $root_cert=NULL; //same as unset
   /*** Calls openssl ***/
   if (isset($root_cert)) {
      $command="/opt/csw/bin/openssl smime -verify -in";
      $command.=" validation_".$uniq_id.".pem";
      $command.=" -inform PEM -binary -content";
      $command.=" original_text_".$uniq_id.".txt";
      $command.=" -CAfile ".$root_cert;
      //print $command;
      $rep=shell_exec($command." 2>&1");
   } else  {
      $rep='The issuer of your certificate is not trusted by the server';
   }
   /* delete temp files */
   unlink("validation_".$uniq_id.".pem");
   unlink("original_text_".$uniq_id.".txt");
   //print $rep;
   if (substr($rep,0,23)=="Verification successful")
      $rep=NULL;  //returns NULL if successful
   return $rep;
}
function GetName($signature)
{
   //Gets the name of the owner of the certificate
   /*** Creates validation file comprising the signature ***/
   $uniq_id=uniqid();
   $fp=fopen("validation_".$uniq_id.".pem","w");
   fwrite($fp,"-----BEGIN PKCS7-----\n");
   fwrite($fp,$signature);
   fwrite($fp,"\n-----END PKCS7-----");
   fclose($fp);
   $command="/opt/csw/bin/openssl pkcs7 -inform PEM -in";
   $command.="  validation_".$uniq_id.".pem";
   $command.=" -print_certs -noout | grep \^subject | head -1 | cut -d'=' -f2-";
   $rep=shell_exec($command." 2>&1");
   unlink("validation_".$uniq_id.".pem");
   //print $rep;
   return $rep;
}
function GetCA($signature)
{
   //Gets the name of the issuer of the certifcate
   /*** Creates validation file comprising the signature ***/
   $uniq_id=uniqid();
   $fp=fopen("validation_".$uniq_id.".pem","w");
   fwrite($fp,"-----BEGIN PKCS7-----\n");
   fwrite($fp,$signature);
   fwrite($fp,"\n-----END PKCS7-----");
   fclose($fp);
   $command="/opt/csw/bin/openssl pkcs7 -inform PEM -in";
   $command.="  validation_".$uniq_id.".pem";
   $command.=" -print_certs -noout | grep \^issuer | head -1 | cut -d'=' -f2-";
   $rep=shell_exec($command." 2>&1");
   unlink("validation_".$uniq_id.".pem");
   //print $rep;
   return $rep;
}
/***************************/
/*** PROGRAM BEGINS HERE ***/
$signature=$_REQUEST["signature"];
$varnames=$_REQUEST["varnames"];
if (!isset($signature) || !isset($varnames)) {
   die("<p>Error, no digital signature provided<br>Current (2007) version of IE does not support digital signature</p>\n</body></html>");
}
$ctrl=Validation($signature,$varnames);
if (isset($ctrl)) {
   die("<p>Error validating signature: ".$ctrl."</p>\n</body></html>");
}
/*** Validation OK, continue with normal procedure ***/
//variables verification
//storage in database (do not forget to add signature and varnames to the database)
//message
print "<p>Information signed by: ".GetName($signature);
print "<p>Thank you. Data received and stored correctly";

?>

</body>
</html>
Listing Two: Sample PHP file used to manage form submissions

Then Validation creates a string called original_text in the same way as the text to be signed was created within the JavaScript function signForm. For doing so, the function analyzes varnames to identify variable names in the correct order, then it obtains the values for each variable and creates original_text in the format variable=value.

It is important to note that original_text must be built using the values of the variables received from the form because these are the values that will be stored in the database and therefore the information that needs to be checked. If the string texttbs (created during the signing process within the JavaScript function signForm) were sent as a hidden field, server-side verification would be easier, but there will be no warranty that the values of the form variables match the information stored in texttbs variable. In that scenario an attacker may send any information in the name of the supplanted victim just by using a PKCS#7 digital signature (in signature variable), the original text which created that signature (in texttbs variable), and whatever data in form variables An altered version of signForm function may use the following definitions:


texttbs="hello,...";
signature="MIIGzQYJKoZIhvcNAQ....";

The signature and text used by the attacker could be obtained for example from any email message signed by the victim.

Consequently, the validation function must build the original text concatenating form variables, and the variable varnames is needed for two main reasons:

Even though a given application should know the list of variables that it must receive, some objects do not always send variables to the server (for example a checkbox does not send a variable unless it is checked). But even knowing the list of variables actually signed, it is necessary to know in which order they were read. The same set of variables and values but shuffling lines would be essentially the same information for a person, but would fail in signature verification. Therefore using varnames to create orignal_text makes things very easy and Validation becomes a general function independent of the application.

Once original_text has been created it is stored in a temporary file called original_text_XXX.txt. For signature validation both files will be used as argument to the program openssl.

The final step before verifying the signature is to identify the name of the certification authority that issued the certificate, since the system can only verify a signature if it can previously verify the validity of the certificate. In order to obtain the issuer, Validation calls GetCA function and gets the name of the user as a string with the Country, Organization, and so on. If the issuer of the certificate is one of the authorities trusted by the server (the server has its root certificate), then the process can continue. It is also possible to modify the call to openssl adding the option -noverify to avoid signer verification though it is not recommended.

Finally, the function must call the program openssl providing validation_XXX.pem and original_text_XXX.txt as the main arguments along with the root certificate of the authority that issued the certificate. The openssl command used for signature verifications is:

     

openssl smime -verify -in validation_XXX.pem -inform PEM -binary -content original_text_XXX.txt -CAfile fnmt.pem

Therefore, the call to openssl within PHP for a UNIX environment is performed building such command and calling shell_exec (see PHP Program Execution Functions) in the way in which standard output and standard error are trapped:


$command="/opt/csw/bin/openssl smime -verify -in";
$command.=" validation_".$uniq_id.".pem";
$command.=" -inform PEM -binary -content";
$command.=" original_text_".$uniq_id.".txt";
$command.=" -CAfile ".$root_cert;
$rep=shell_exec($command." 2>&1");

The answers that can be obtained are:


Verification successful
Verification failure

In UNIX systems, openssl prints the original text on standard output and the result of the verification on standard error. For this reason the argument in the call to shell_exec must include standard error redirection directive as show above.

The result of Validation is NULL in case of success and a string containing the error message in case of failure. The error message can be displayed to the user after calling Validation, as in Listing Two.

Certificate's Owner and Issuer

The owner and the issuer of a certificate are two of the fields stored in a PKCS#7 signature. To access this information easily, a PHP function called GetName is provided to obtain the owner's name and a function called GetCA is provided to obtain the issuer of the certificate; i.e., the name of the Certification Authority.

The program openssl makes available a command called pkcs7 that is used to analyze certificates stored within the signature file. The typical call to openssl would be:


openssl pkcs7 -inform PEM -in validation_XXX.pem -print_certs -noout

To extract just the name of just the issuer, this command is combined with UNIX commands grep and cut.

Conclusion

Listing One is a JavaScript function called signForm that can be added to any existing HTML form, previously created with any HTML editor, to create a digital signature of the data entered in the form. The function was developed in such a general way that it can automatically go through all form objects gathering variable names and values and creating a single string of text to be signed by users. By calling the standard JavaScript function signText, the string to be signed is shown in a dialog window that allows the user to verify the information and to select one of the available personal certificates to sign it. No additional configuration or pre-definition of form variable names is required, ensuring easy implantation in existing forms. The digital signature is sent to the server along with the current set of values.

Listing Two is a PHP function called Validation that must be run in the server to ensure that the data received matches the digital signature, hence allowing to decide if it is legally valid or not. Again this is an automatic function that will check all necessary variables without requiring any pre-definition. In this case a complete PHP program is provided as an example about how to call Validation function. Validation makes use of two additional functions called GetName and GetCA that extract the names of the owner and the issuer of the certificate. These useful functions can also be called directly from the PHP program to obtain such information without requiring any prior deep knowledge on cryptography and personal certificates.

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.