Chapter 2: Symmetric Encryption

Contents

2.1 What is AspEncrypt?

AspEncrypt is built on top of the Microsoft Cryptographic Application Programming Interface (CryptoAPI) which is part of the Windows API. Therefore, a brief description of CryptoAPI's architecture will help understand AspEncrypt's object model.

2.1.1 Cryptographic Service Providers

The CryptoAPI architecture is somewhat similar to ODBC in that it consists of an API layer (analogous to ODBC Manager) and a number of cryptographic modules underneath that layer that actually perform cryptographic tasks (analogous to ODBC drivers.) These modules are called Cryptographic Service Providers (CSPs).

All modern versions of Windows come with several standard CSPs. As far as AspEncrypt is concerned, there are only two CSPs that matter. Their names are:

  • Microsoft Strong Cryptographic Provider, and
  • Microsoft Enhanced RSA and AES Cryptographic Provider.

The former is usually the default one and offers all major encryption algorithms except the relatively new Advanced Encryption Standard (AES, also known as Rijndael) cipher. The latter offers AES along with all other ciphers, but is only available on Windows 2003 and later.

The list all Cryptographic Service Providers installed on the current machine can be obtained from the system registry under the key

HKEY_LOCAL_MACHINE\Software\Microsoft\Cryptography\Defaults\Providers

The default cryptographic provider for the current user can be found under the key

HKEY_CURRENT_USER\Software\Microsoft\Cryptography\Provider Types\Type 001

Note that on Windows 2000 and NT, the default CSP is usually Microsoft Base Cryptographic Provider v1.0. Its use should be avoided as it only offers weak 40-bit encryption.

2.1.2 Key Containers

Each CSP has a key database in which it stores its persistent cryptographic public/private key pairs. Each key database contains one or more key containers, each of which contains all the key pairs belonging to a specific user. Key containers can also have a machine-wide scope and accessible by all users with proper permissions. Each key container has a unique name. There are usually two key pairs in each container: a key-exchange key pair and signature key pair. One key pair is used to encrypt symmetric keys and the other to create digital signatures.

The Microsoft CSPs stores key containers in two subfolders underneath the c:\Documents and Settings tree: \<current user>\Application Data\Mcrosoft\Crypto\RSA and \All Users\Application Data\Microsoft\Crypto\RSA. Therefore, a key container is uniquely identified by its name and a flag indicating whether the key is located under the <current user> or All Users subfolder.

For symmetric-encryption purposes, the key containers are not needed, they only come into play when public/private-key operations are involved.

2.1.3 Cryptographic Contexts

An application uses cryptographic services provided by a certain CSP by opening a Cryptographic Context, which is a handle that connects the application with that CSP and, optionally, with one of its key containers. Every cryptographic operation attempted by an application begins with opening an appropriate cryptographic context. In AspEncrypt, a cryptographic context is represented by the CryptoContext object created with the methods OpenContext and OpenContextEx described in the next section.

2.2 OpenContext and OpenContextEx Methods

In most AspEncrypt-based applications, the first order of business is to create an instance of the CryptoContext object which can then do useful work, such as creating cryptographic keys and hash functions, managing certificates, etc.

To open a context associated with the default CSP, the method OpenContext of the top-level CryptoManager object should be used. The method expects 3 arguments:

  • the key container name (can be an empty string.)
  • a True/False flag specifying whether the key container's location is under the All Users or <current user> subfolder, and
  • an optional True/False flag specifying whether the key container should be populated by a new set of key pairs (False by default.)
The key containers are not used for symmetric-encryption and hash function purposes, so the code samples in this and next chapters always pass an empty string as the first argument. If the first argument is empty, the 2nd argument is irrelevant, so either True or False can be passed. However, for consistency, you should always pass True in ASP/ASP.NET applications, and False in stand-alone applications.
Set CM = Server.CreateObject("Persits.CryptoManager")
Set Context = CM.OpenContext( "", True )
ICryptoManager objCM = new CryptoManager();
ICryptoContext objContext = objCM.OpenContext( "", true, Missing.Value );

If the CSP needs to be specified explicitly, the method OpenContextEx should be used. Its first argument is the CSP name, and the other 3 arguments are the same as in OpenContext. This method should be used, for example, if the default CSP is Microsoft Strong Cryptographic Provider but Microsoft Enhanced RSA and AES Cryptographic Provider is needed to perform AES encryption:

Set CM = Server.CreateObject("Persits.CryptoManager")
Set Context = CM.OpenContextEx( _
"Microsoft Enhanced RSA and AES Cryptographic Provider", "", True )
ICryptoManager objCM = new CryptoManager();
ICryptoContext objContext = objCM.OpenContextEx(
"Microsoft Enhanced RSA and AES Cryptographic Provider",
"", true, Missing.Value );

For debugging purposes, it is a good idea to make sure your CryptoContext object is associated with the right cryptographic provider. To obtain the name of the context's CSP, use CryptoContext's read-only property ProviderName, as follows:

Set CM = Server.CreateObject("Persits.CryptoManager")
Set Context = CM.OpenContext( "", True )
Response.Write Context.ProviderName
ICryptoManager objCM = new CryptoManager();
ICryptoContext objContext = objCM.OpenContext( "", true, Missing.Value );
Response.Write( objContext.ProviderName );

2.3 Encrypting Text and Files

Once an instance of the CryptoContext object is obtained, it can be used to create a CryptoKey object that encapsulates the actual encryption and decryption functionality. A key can be password-derived, imported from an external source, or random.

2.3.1 GenerateKeyFromPassword Method

Password-derived keys are most common. To generate such a key, the CryptoContext method GenerateKeyFromPassword should be used. This method accepts 4 arguments:

  • the password string from which the key is to be derived (required);
  • the hash algorithm which specifies how the password string is to be turned into a binary key (optional);
  • the encryption algorithm (cipher) this key is to implement when called to perform an encryption or decryption (optional);
  • the key bit size (optional).

The following tables list all possible values and their meanings for the 2nd and 3rd argument:

Available hash algorithms (2nd argument to GenerateKeyFromPassword):

Value
Meaning
Bit size
CSP Required
calgMD4
MD4
128
Microsoft Strong Cryptographic Provider
calgMD5
MD5
128
Microsoft Strong Cryptographic Provider
calgSHA
SHA-1
160
Microsoft Strong Cryptographic Provider
calgSHA256
SHA-256
256
Microsoft Enhanced RSA and AES Cryptographic Provider
calgSHA384
SHA-384
384
Microsoft Enhanced RSA and AES Cryptographic Provider
calgSHA512
SHA-512
512
Microsoft Enhanced RSA and AES Cryptographic Provider

Default hash algorithm: calgSHA.

Available encryption algorithms / ciphers (3rd argument to GenerateKeyFromPassword):

Value
Meaning
Default effective (total) bit size
CSP Required
calgRC2
RC2
128
Microsoft Strong Cryptographic Provider
calgRC4
RC4
128
Microsoft Strong Cryptographic Provider
calgDES
DES
56 (64)
Microsoft Strong Cryptographic Provider
calg3DES
Triple-DES (3 keys)
168 (194)
Microsoft Strong Cryptographic Provider
calg3DES2
Triple-DES (2 keys)
112 (128)
Microsoft Strong Cryptographic Provider
calgAES128
AES-128
128
Microsoft Enhanced RSA and AES Cryptographic Provider
calgAES192
AES-192
192
Microsoft Enhanced RSA and AES Cryptographic Provider
calgAES256
AES-256
256
Microsoft Enhanced RSA and AES Cryptographic Provider

Default cipher: calgRC2.

Note that the DES family of ciphers has different effective and total bit sizes. That is because a DES key is stored in the first 7 bits of each byte, and the 8th bit is used for parity-check purposes.

If the 4th argument (key size) to GenerateKeyFromPassword is omitted, the default key size is used as per table above.

2.3.2 CryptoKey Object

The CryptoKey object obtained from a call to GenerateKeyFromPassword encapsulates various properties of an encryption key (such as the actual key bits, cipher, encryption mode, padding, etc.) It provides 3 pairs of methods to encrypt and decrypt text, binary data, and files. These methods are:

  • EncryptText
  • DecryptText
  • EncryptBinary
  • DecryptBinary
  • EncryptFile
  • DecryptFile

EncryptText expects a single argument, a text string to encrypt. Since the encryption process turns text into an unreadable sequence of bits, AspEncrypt provides a special auxiliary object, CryptoBob, to store binary data. The CryptoBlob object returns the data it contains via its properties Hex, Base64, Binary and Ansi in hexadecimal encoding, Base64 encoding, as a byte array, or as an ASCII string, respectively. CryptoBlob can be initialized via the same 4 properties also.

The following code snippets encrypt a text string with a password-derived Triple-DES key and stores the result in Hex format in a variable:

<!--METADATA TYPE="TypeLib" UUID="{B72DF063-28A4-11D3-BF19-009027438003}"-->

<%
Set CM = Server.CreateObject("Persits.CryptoManager")
Set Context = CM.OpenContext( "", True )
Set Key = Context.GenerateKeyFromPassword( "mypassword", calgSHA, calg3DES )
Set Blob = Key.EncryptText("some text")
txtEnc = Blob.Hex
%>
ICryptoManager objCM = new CryptoManager();
ICryptoContext objContext = objCM.OpenContext( "", true, Missing.Value );
ICryptoKey objKey = objContext.GenerateKeyFromPassword("mypassword",
   CryptoAlgorithms.calgSHA,
   CryptoAlgorithms.calg3DES,
   Missing.Value);
ICryptoBlob objBlob = objKey.EncryptText("some text");
String txtEnc = objBlob.Hex;

Note that the VBScript snippet above contains a special METADATA tag referencing AspEncrypt's type library GUID. This is necessary to make AspEncrypt's built-in constants such as calgSHA and calg3DES available to the script. There is no need for the METADATA tag in a .NET script but the constants must be prefixed with their respective data types, in this case CryptoAlgorithms.

The following code snippets perform the opposite operation: decrypt the Hex-encoded string generated by the previous snippets and store the result (original string) in another variable:

<!--METADATA TYPE="TypeLib" UUID="{B72DF063-28A4-11D3-BF19-009027438003}"-->

<%
Set CM = Server.CreateObject("Persits.CryptoManager")
Set Context = CM.OpenContext( "", True )
Set Key = Context.GenerateKeyFromPassword( "mypassword", calgSHA, calg3DES )
Set Blob = CM.CreateBlob
Blob.Hex = txtEnc
txtDec = Key.DecryptText(Blob)
%>
ICryptoManager objCM = new CryptoManager();
ICryptoContext objContext = objCM.OpenContext( "", true, Missing.Value );
ICryptoKey objKey = objContext.GenerateKeyFromPassword("mypassword",
   CryptoAlgorithms.calgSHA,
   CryptoAlgorithms.calg3DES,
   Missing.Value);
ICryptoBlob objBlob = objCM.CreateBlob();
objBlob.Hex = txtEnc;
txtDec = objKey.DecryptText(objBlob);

Here we create an empty CryptoBlob object via the CreateBlob method of the CryptoManager object, initialize it with the data obtained in the previous example, and pass it to the DecryptText method which returns the decrypted data as a regular string.

The EncryptBinary and DecryptBinary methods work in a similar way except that both methods have CryptoBlob objects as input and output.

The EncryptFile and DecryptFile methods expect two paths as their arguments: the path to the input and output files:

...
Key.EncryptFile "c:\path\original.txt", "c:\path\original.txt.enc"
...
Key.DecryptFile "c:\path\original.txt.enc", "c:\path\original.txt"
...
Key.EncryptFile( "c:\path\original.txt", "c:\path\original.txt.enc" );
...
Key.DecryptFile( "c:\path\original.txt.enc", "c:\path\original.txt" );

The following code sample (not shown here to conserve space) encrypts and decrypts a user-specified string with a user-specified password and cipher.

Click the links below to run this code sample:

2.4 Cipher Modes of Operation, Padding, Initialization Vectors

2.4.1 Padding

RC2, DES and AES are block ciphers, that is, they operate on blocks of plaintext individually. The block size is usually 8 bytes. If the length of the message is not a multiple of 8, the last block is padded before being encrypted. After the message is decrypted, it is unpadded. AspEncrypt supports zero padding, random padding (both self-explanatory) and PKCS#5 padding where each of the bytes being padded is filled with a number equal to the total number of bytes being padded. For example, if we have an 8-byte block, and only 3 bytes are filled, then we have 5 bytes to pad. Those 5 bytes are all assigned the value "5", for the 5 bytes of padding.

Padding is specified via the Padding property of the CryptoKey object. The valid values are: ccpPKCS5 (1, default), ccpRandom (2) and ccpZero (3).

...
Key.Padding = ccpZero
...
objKey.Padding = CryptoCipherPadding.ccpZero;

2.4.2 Cipher Modes and IVs

The simplest of the modes is the electronic codebook (ECB) mode where each block of the message is encrypted separately. The disadvantage of this method is that identical plaintext blocks are encrypted into identical ciphertext blocks.

In the cipher-block chaining (CBC) mode, each block of plaintext is XORed with the previous ciphertext block before being encrypted. This way, each ciphertext block is dependent on all plaintext blocks processed up to that point. Also, to make each message unique, an initialization vector (IV) must be used in the first block. By default, the IV is just a sequence of 0s.

There are other modes as well, such as the cipher feedback (CFB) mode, output feedback (OFB) mode, and ciphertext stealing (CTS) mode.

For more information and graphic representations of each mode, see this Wikipedia article: Block cipher modes of operation.

The cipher mode is specified via the Mode property of the CryptoKey object. The valid values are: ccmCBC (1, default), ccmECB (2), ccmOFB (3), ccmCFB (4), and ccmCTS (5).

...
Key.Mode = ccmCFB
...
objKey.Mode = CryptoCipherModes.ccmCFB;

The initialization vector is specified via the SetIV method of the CryptoKey object. The method expects a CryptoBlob object containing the IV's binary data as the only argument.

Set IVblob = CM.CreateBlob
IVblob.Base64 = "X6PfOtMlNmk="
Key.SetIV IVblob
ICryptoBlob objIVblob = objCM.CreateBlob();
objIVblob.Base64 = "X6PfOtMlNmk=";
objKey.SetIV( objIVblob );

2.5 Key Importing and Exporting

The ciphers AspEncrypt implements are standard across multiple languages and platforms. Therefore, AspEncrypt can be used to decrypt data generated on another platform by another package, provided it has access to the encryption key and other pertinent information such as the cipher mode, IV, etc. It can also generate encrypted data meant for decryption on another platform, provided the other platform is given access to the encryption key and other details.

2.5.1 ImportRawKey Method

To import a raw sequence of bits into a CryptoKey to be used for encryption or decryption, you should use the ImportRawKey method of the CryptoContext object. The method expects a CryptoBlob containing the binary data, the cipher name, and an optional flag indicating if the bytes in the CryptoBlob object should be put in a reverse order.

The following code snippet initializes a CryptoBlob object with a Hex-encoded 128-bit sequence and turns it into an RC4 encryption key which can then be used for either encryption or decryption:

<!--METADATA TYPE="TypeLib" UUID="{B72DF063-28A4-11D3-BF19-009027438003}"-->

<%
Set CM = Server.CreateObject("Persits.CryptoManager")

Set Context = CM.OpenContext( "", True )

Set Blob = CM.CreateBlob
Blob.Hex = "B7FE183CFF108EA7F3E0A90D55AA8CFA"

Set Key = Context.ImportRawKey( Blob, calgRC4 )
...
%>
ICryptoManager objCM = new CryptoManager();

ICryptoContext objContext = objCM.OpenContext( "", true, Missing.Value );
ICryptoBlob objBlob = objCM.CreateBlob();
objBlob.Hex = "B7FE183CFF108EA7F3E0A90D55AA8CFA";

ICryptoKey = objContext.ImportRawKey( objBlob, CryptoAlgorithms.calgRC4, Missing.Value );
...

If you are provided with a DES or Triple-DES key, its byte order is usually reversed. Therefore, to import such a key correctly, you must pass True as the last argument to ImportRawKey.

The following code sample takes a DES key and an initialization vector provided by another party and performs the decryption of a given string using that key and IV. The input data is as follows (all Base64-encoded):

DES Key:
mInO4JRGtQ4=
IV:
X6PfOtMlNmk=
Encrypted string:
dBUMGWhRA7dAbOuOvwBna1fwEHKAXW4wVozBoqc7R8o=
Cipher mode:
CBC
<!--METADATA TYPE="TypeLib" UUID="{B72DF063-28A4-11D3-BF19-009027438003}"-->

<%
Set CM = Server.CreateObject("Persits.CryptoManager")
Set context = CM.OpenContext("", True)

' DES encryption key (Base64-encoded)
Set KeyBlob = CM.CreateBlob
KeyBlob.Base64 = "mInO4JRGtQ4="

' Import key into CryptoKey object. Reverse byte order
Set Key = context.ImportRawKey(KeyBlob, calgDES, True)

' Specify initialization vector (IV, Base64-encoded)
Set IVblob = CM.CreateBlob
IVblob.Base64 = "X6PfOtMlNmk="
Key.SetIV IVblob

' Text to decipher (Base64-encoded)
Set TextBlob = CM.CreateBlob
TextBlob.Base64 = "dBUMGWhRA7dAbOuOvwBna1fwEHKAXW4wVozBoqc7R8o="

' Decrypt
Response.Write Key.DecryptText(TextBlob)
%>
ICryptoManager objCM = new CryptoManager();

ICryptoContext objContext = objCM.OpenContext( "", true, Missing.Value );

// DES encryption key (Base64-encoded)
ICryptoBlob objKeyBlob = objCM.CreateBlob();
objKeyBlob.Base64 = "mInO4JRGtQ4=";

// Import key into CryptoKey object. Reverse byte order
ICryptoKey objKey =
objContext.ImportRawKey( objKeyBlob, CryptoAlgorithms.calgDES, true );

// Specify initialization vector (IV, Base64-encoded)
ICryptoBlob objIVblob = objCM.CreateBlob();
objIVblob.Base64 = "X6PfOtMlNmk=";
objKey.SetIV( objIVblob );

// Text to decipher (Base64-encoded)
ICryptoBlob objTextBlob = objCM.CreateBlob();
objTextBlob.Base64 = "dBUMGWhRA7dAbOuOvwBna1fwEHKAXW4wVozBoqc7R8o=";

// perform decryption
txtResult.Text = objKey.DecryptText( objTextBlob );

The output should be FiveLittleMonkiesJumpingOnABed.

Click the links below to run this code sample:

In addition to Hex and Base64, the CryptoBlob object also offers the Ansi property which allows you to initialize a blob with an ASCII string. This property is especially useful for the DES family of ciphers which often uses ASCII strings to encode its keys. For example, the line

Blob.Ansi = "123456"

is equivalent to

Blob.Hex = "313233343536"

because the character '1' is Hex 31, '2' is Hex 32, etc.

2.6 Using AspEncrypt with AspUpload

2.6.1 Encrypting Files During an Upload

AspEncrypt can be used in tandem with AspUpload, our industry-standard file upload component, to encrypt files as they are being uploaded in a single operation. Note that AspEncrypt does not replace SSL -- it does not protect the files in transition from the user's computer to the server.

Files can be uploaded from the user's machine to the server with a browser via a form with the attribute ENCTYPE="multipart/form-data", such as:

<FORM METHOD="POST" ENCTYPE="multipart/form-data" ACTION="UploadScript.asp">
<INPUT TYPE=FILE NAME="FILE1">
<INPUT TYPE=FILE NAME="FILE2">
<INPUT TYPE=FILE NAME="FILE3">
<INPUT TYPE=SUBMIT VALUE="Upload!">
</FORM>

To capture the uploaded files with AspUpload, the following simple script can be used:

Set Upload = Server.CreateObject("Persits.Upload")
Upload.Save "c:\path"
<%@ Import Namespace="ASPUPLOADLib" %>
<%@ Page aspCompat="True" Debug=True %>
...
IUploadManager objUpload = new UploadManager();
objUpload.Save( @"c:\path", Missing.Value, Missing.Value);

AspUpload's Save method accepts two more optional arguments: an instance of the CryptoKey object to encrypt the uploaded files with, and an extension that will be appended to the original file name to form the name of the encrypted file. For example, if the third argument is "xxx", the file myfile.txt will be encrypted into the file myfile.txt.xxx. This way the original file extension is preserved.

The following code snippet creates a password-derived key and passes it to AspUpload's Save method:

Set CM = Server.CreateObject("Persits.CryptoManager")
Set Context = CM.OpenContext( "", True )
Set Key = Context.GenerateKeyFromPassword( "my password" )

Set Upload = Server.CreateObject("Persits.Upload")
Upload.Save "c:\path", Key, "xxx"
<%@ Import Namespace="ASPUPLOADLib" %>
<%@ Page aspCompat="True" %>
...
ICryptoManager objCM = new CryptoManager();
ICryptoContext objContext = objCM.OpenContext( "", true, Missing.Value );

ICryptoKey objKey = objContext.GenerateKeyFromPassword(
   "my password", Missing.Value, Missing.Value, Missing.Value );

IUploadManager objUpload = new UploadManager();
objUpload.Save( @"c:\path", objKey, "xxx" );

AspUpload also allows you to upload a text password along with the files. This password will be used by the component to derive an encryption key which will be applied to the files being uploaded. This functionality requires a simple protocol: your HTML form must have an <INPUT TYPE=TEXT NAME="ENCRYPTPASSWORD"> or <INPUT TYPE=HIDDEN NAME="ENCRYPTPASSWORD"> item through which the password is specified, and this item must appear in the form before all <INPUT TYPE=FILE> items, as follows:

<FORM METHOD="POST" ENCTYPE="multipart/form-data" ACTION="UploadScript.asp">
<INPUT TYPE=TEXT NAME="ENCRYPTPASSWORD">
<INPUT TYPE=FILE NAME="FILE1">
<INPUT TYPE=FILE NAME="FILE2">
<INPUT TYPE=FILE NAME="FILE3">
<INPUT TYPE=SUBMIT VALUE="Upload!">
</FORM>

When an item like that is present on the form, AspUpload will derive the encryption key from that password dynamically. You still need to pass an empty instance of the CryptoKey object to the Save method. An empty key is created via the CryptoContext method CreateEmptyKey. For example:

...
Set Key = Context.CreateEmptyKey

Set Upload = Server.CreateObject("Persits.Upload")
Upload.Save "c:\path", Key, "xxx"
...
ICryptoKey objKey = objContext.CreateEmptyKey(Missing.Value, Missing.Value );

IUploadManager objUpload = new UploadManager();
objUpload.Save( @"c:\path", objKey, "xxx" );

2.6.2 Decrypting Files During a Download

AspUpload enables your application to download a file from the web server even if this file is not located in a virtual directory via the method SendBinary:

Set Upload = Server.CreateObject("Persits.Upload")
Upload.SendBinary "c:\path\myfile.txt", True, "application/octet-stream"
<%@ Import Namespace="ASPUPLOADLib" %>
<%@ Page aspCompat="True" Debug=True %>
...
IUploadManager objUpload = new UploadManager();
objUpload.SendBinary( @"c:\path\myfile.txt"", true,
   "application/octet-stream", Missing.Value, Missing.Value);

If the file to be downloaded is encrypted, AspUpload can decrypt the file as it is being streamed to the user via the method DecryptAndSendBinary. Its 1st, 2nd and 3rd arguments are the same as with SendBinary. Its 4th argument is an instance of the CryptoKey object to decrypt the file with. Its 5th argument is a Boolean indicating whether the file extension (such as ".xxx) should be removed. Its 6th and 7th arguments are the same as SendBinary's 4th and 5th arguments.

Set Upload = Server.CreateObject("Persits.Upload")
Upload.DecryptAndSendBinary "c:\path\myfile.txt.xxx", True, _
   "application/octet-stream", Key, True
<%@ Page aspCompat="True" Debug=True %>
...
IUploadManager objUpload = new UploadManager();
objUpload.DecryptAndSendBinary( @"c:\path\myfile.txt.xxx", 1,
   "application/octet-stream", objKey, 1, Missing.Value, Missing.Value );

Note that in the C# version, we need to pass 1 instead of True (and 0 instead of False, if necessary.)

For more information on AspUpload's file download functionality, see the AspUpload User Manual.

Chapter 1: Introduction & Quick Start Chapter 3: One-way Hash Function