Chapter 8: XEncrypt - Client-side ActiveX Control

Contents

8.1 What is XEncrypt?

The AspEncrypt.dll file contains an ActiveX control component called XEncrypt which can be used on the client side inside Internet Explorer. Therefore, all the cryptographic operations offered by AspEncrypt can be performed on the user's machine and not just the web server. XEncrypt exposes the same properties and methods as CryptoManager with the exception of a few methods not applicable to client-side operations.

Most importantly, XEncrypt allows your Web-based application to perform operations involving the user's private key such as digital signing or data decryption without jeopardizing the security of the private key.

XEncrypt does not require a registration key, so you do not need to purchase a license for every client machine XEncrypt will be downloaded to. However, XEncrypt will only work under IE and not any other environment such as VB, etc. For containers other than IE, you must continue using the CryptoManager object which requires a registration key.

8.2 Using XEncrypt under IE

The XEncrypt ActiveX control resides in the same DLL as the rest of the AspEncrypt objects, aspencrypt.dll. To use XEncrypt on the client side, your web page must reference it using the <OBJECT> tag, as follows:

<OBJECT
  CLASSID="CLSID:F9463571-87CB-4A90-A1AC-2284B7F5AF4E"
  CODEBASE="aspencrypt.dll"
  ID="XEncrypt">

The CLASSID attribute is the globally unique identifier (GUID) of the XEncrypt control.

The CODEBASE attribute points to a relative path of the file aspencrypt.dll on your server. For this code to work, you must place the file aspencrypt.dll in a virtual directory on your web server where the browser can find and download it from. Note that you do not need to register aspencrypt.dll in that location. Your web server may have the file aspencrypt.dll registered in, say, the system folder c:\windows\system32, and another copy of that file placed in the same virtual directory as the HTML or ASP files that reference it.

The IE browser keeps cached copies of all ActiveX controls it has downloaded and installed in the past. This way it does not have to re-download and reinstall a control every time a page hosting this control is viewed by the user. However, if a new version of the control DLL file is placed on the server, if is necessary to force IE to download and reinstall this new version. This is achieved by appending a #VERSION=... keyword to the name of the DLL file in the CODEBASE attribute, as follows:

<OBJECT
  CLASSID="CLSID:F9463571-87CB-4A90-A1AC-2284B7F5AF4E"
  CODEBASE="aspencrypt.dll#VERSION=2,9,0,0"
  ID="XEncrypt">
</OBJECT>

The ID attribute is to be used by client-side scripts to reference the XEncrypt control (see code samples below).

AspEncrypt is a digitally signed DLL, so when your users run this page for the first time, they will see a message at the top of the page similar to this:

and after that, a dialog box similar to this:

If the user chooses "Install", the browser will download and register the file aspencrypt.dll on the user's machine. The AspEncrypt library is signed with Persits Software, Inc.'s digital ID. If you want your company's name to appear on this security dialog and not Persits Software, Inc., contact us and we will send you an unsigned copy of the DLL. You can then sign it using your own digital ID.

8.3 A Simple Client-Side Code Sample

XEncrypt is an invisible control with no user interface. It can only be invoked by client-side script. The following code sample demonstrates simple text encryption and decryption performed on the client side:

<!-- Client-side VBscript -->
<HTML>
<HEAD>

<TITLE>08_simple_vb.htm</TITLE>

<OBJECT
CLASSID="CLSID:F9463571-87CB-4A90-A1AC-2284B7F5AF4E"
CODEBASE="aspencrypt.dll"
ID="XEncrypt">
</OBJECT>

<SCRIPT LANGUAGE="VBSCRIPT">

Sub Encrypt
Set Context = XEncrypt.OpenContext("", False)
Set Key = Context.GenerateKeyFromPassword("my password")
Set Blob = Key.EncryptText( document.myForm.txtEncr.Value )
document.myForm.txtDecr.Value = Blob.Base64
End Sub

Sub Decrypt
Set Context = XEncrypt.OpenContext("", False)
Set Key = Context.GenerateKeyFromPassword("my password")
Set Blob = XEncrypt.CreateBlob
Blob.Base64 = document.myForm.txtDecr.Value
document.myForm.txtOriginal.Value = Key.DecryptText(Blob)
End Sub
</SCRIPT>

</HEAD>

<BODY>
<FORM NAME="myForm">
<INPUT TYPE="TEXT" NAME="txtEncr" SIZE="60" VALUE="Text to encrypt">
<INPUT TYPE="BUTTON" OnClick="Encrypt" VALUE="Encrypt">
<P>
<INPUT TYPE="TEXT" NAME="txtDecr" SIZE="60">
<INPUT TYPE="BUTTON" OnClick="Decrypt" VALUE="Decrypt">

<P>
<INPUT TYPE="TEXT" NAME="txtOriginal" SIZE="60">

</FORM>

</BODY>
</HTML>
<!-- Client-side Javascript -->
<HTML>
<HEAD>

<TITLE>08_simple_js.htm</TITLE>

<OBJECT
CLASSID="CLSID:F9463571-87CB-4A90-A1AC-2284B7F5AF4E"
CODEBASE="aspencrypt.dll"
ID="XEncrypt">
</OBJECT>

<SCRIPT LANGUAGE="JavaScript">

function Encrypt()
{
var Context = XEncrypt.OpenContext("", false);
var Key = Context.GenerateKeyFromPassword("my password");
var Blob = Key.EncryptText( document.forms[0].txtEncr.value );

document.forms[0].txtDecr.value = Blob.Base64;
}

function Decrypt()
{
var Context = XEncrypt.OpenContext("", false);
var Key = Context.GenerateKeyFromPassword("my password");
var Blob = XEncrypt.CreateBlob();
Blob.Base64 = document.forms[0].txtDecr.value;

document.forms[0].txtOriginal.value = Key.DecryptText(Blob);
}

</SCRIPT>

</HEAD>

<BODY>

<FORM NAME="myForm" ID="myForm">
<INPUT TYPE="TEXT" NAME="txtEncr" SIZE="60" VALUE="Text to encrypt">
<INPUT TYPE="BUTTON" OnClick="Encrypt()" VALUE="Encrypt">
<P>
<INPUT TYPE="TEXT" NAME="txtDecr" SIZE="60">
<INPUT TYPE="BUTTON" OnClick="Decrypt()" VALUE="Decrypt">
<P>
<INPUT TYPE="TEXT" NAME="txtOriginal" SIZE="60">

</FORM>

</BODY>
</HTML>

This code is very similar to the server-side scripts we have seen in the previous chapters except that here we do not explicitly create an instance of the CryptoManager object via CreateObject or New. Instead, the XEncrypt object is created via an <OBJECT> tag. Another difference is that we pass False instead of True to the OpenContext method to instruct XEncrypt to use a key container in the HKEY_CURRENT_USER and not HKEY_LOCAL_MACHINE portion of the registry.

Click the links below to run this code sample:

8.4 Signing with Personal Certificates

The main reason why XEncrypt has been introduced is to provide easy and safe access to the user's private keys residing on the client machine. XEncrypt enables the user to generate digital signatures and decrypt data with her private key using the IE browser. The private key never has to leave the user's machine, so its security is not jeopardized.

Once the user has obtained a personal certificate, he can start using its private key to perform digital signing or data decryption. The following code sample demonstrates how XEncrypt can be used to compute the digital signature of a text string with the user's certificate:

<!-- Client-side VBScript -->
<HTML>
<HEAD>

<TITLE>08_signcert_vb.htm</TITLE>

<OBJECT
CLASSID="CLSID:F9463571-87CB-4A90-A1AC-2284B7F5AF4E"
CODEBASE="aspencrypt.dll"
ID="XEncrypt">
</OBJECT>

<SCRIPT LANGUAGE="VBSCRIPT">
Sub Sign
' Open "MY" certificate store which contains client certs
Set Store = XEncrypt.OpenStore("MY", False )

' Does the store contain certificates?
Count = Store.Certificates.Count
If Count = 0 Then
MsgBox "You have no certificates."
Exit Sub
End If

' If store contains more than one, enable user to pick one
If Count > 1 Then
Set Cert = XEncrypt.PickCertificate(Store, 4+8+16,_
"Select Certificate Please",_
"Select the one you want to be used for signing")
If Cert Is Nothing Then Exit Sub
Else
' otherwise just pick that only one cert
Set Cert = Store.Certificates(1)
End If

' Make sure the cert has a private key associated with it
If Cert.PrivateKeyExists = False Then
MsgBox "This certificate has no private key associated with it."
Exit Sub
End If

' obtain private key context for this cert
Set Context = Cert.PrivateKeyContext

' create empty hash object associated with this context
Set Hash = Context.CreateHash
Hash.AddText document.frmSign.txtToSign.value

Set Blob = Hash.Sign(Context.KeySpec)

document.frmSign.txtSignature.value = Blob.Base64
End Sub

</SCRIPT>

</HEAD>
<BODY>

<FORM NAME="frmSign">
Text to sign:

<TEXTAREA NAME="txtToSign" COLS="80" ROWS="3">Hello World!</TEXTAREA><BR>
<INPUT TYPE="BUTTON" OnClick="Sign" VALUE="Sign">
<P>
<TEXTAREA NAME="txtSignature" COLS="80" ROWS="3"></TEXTAREA>

</FORM>
</BODY>
</HTML>
<!-- Client-side Javascript -->
<HTML>
<HEAD>

<TITLE>08_signcert_js.htm</TITLE>

<OBJECT
CLASSID="CLSID:F9463571-87CB-4A90-A1AC-2284B7F5AF4E"
CODEBASE="aspencrypt.dll"
ID="XEncrypt">
</OBJECT>

<SCRIPT LANGUAGE="JavaScript">
function Sign()
{
// Open "MY" certificate store which contains client certs
var Store = XEncrypt.OpenStore("MY", false );

// Does the store contain certificates?
var Count = Store.Certificates.Count;
if( Count == 0 )
{
alert( 'You have no certificates.' );
return;
}

// If store contains more than one, enable user to pick one
var Cert = null;
if( Count > 1 )
{
Cert = XEncrypt.PickCertificate(Store, 4+8+16,
"Select Certificate Please",
"Select the one you want to be used for signing" );

if( Cert == null )
return;
}
else
{
// otherwise just pick that only one cert
Cert = Store.Certificates(1)
}

// Make sure the cert has a private key associated with it
if( !Cert.PrivateKeyExists )
{
alert( 'This certificate has no private key associated with it.' );
return;
}

// obtain private key context for this cert
var Context = Cert.PrivateKeyContext;

// create empty hash object associated with this context
var Hash = Context.CreateHash();
Hash.AddText( document.forms[0].txtToSign.value );

var Blob = Hash.Sign(Context.KeySpec);
document.forms[0].txtSignature.value = Blob.Base64
}

</SCRIPT>
</HEAD>
<BODY>

<FORM NAME="frmSign">
Text to sign:<BR>
<TEXTAREA NAME="txtToSign" COLS="80" ROWS="3">Hello World!</TEXTAREA><BR>
<INPUT TYPE="BUTTON" OnClick="Sign()" VALUE="Sign">
<P>
<TEXTAREA NAME="txtSignature" COLS="80" ROWS="3"></TEXTAREA>
</FORM>

</BODY>
</HTML>

This code sample uses the previously unseen method PickCertificate exposed by XEncrypt. This method uses the undocumented CryptoAPI method CryptUIDlgSelectCertificateW which displays a list of certificates from a given certificate store. In our case we use the "MY" store opened by a call to CM.OpenStore.

PickCertificate's second argument is a combination of flags that hide certain attributes in the certificate list. In our case we hide all attributes except "Issued To", "Issued By" and "Expiration Date." The third and fourth arguments are optional. They specify captions displayed by the title and body of the certificate list dialog.

The PickCertificate method returns a CryptoCert object representing the user-selected certificate. Our code sample then uses this certificate's private key context to create a CryptoHash object, add user-specified text to it and create a digital signature via the Sign method. Certificate-based signing was already described in detail in Section 6.4 of Chapter 6. Note that we pass Context.KeySpec (which can be True or False) to the Sign method to ensure we are using the correct key pair from this certificate's key container.

Click the links below to run this code sample:

To verify the signatures created with this code sample use the signature verification code of Section 6.4.

Chapter 7: Issuing Certificates Chapter 9: PKCS#7 Signatures and Envelopes