asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search





Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology
International

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Platform Neutral and Transparent Encryption of Sensitive Customer Information
By Zhenlei Cai
Rating: 3.8 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

    Introduction

    To minimize damages from a security breach, online businesses should encrypt sensitive customer information (passwords, credit card numbers, etc.) stored in their databases. Recent incidents that ran headlines have only made this practice a must [1].

    Ideally, an implementation of the encryption should be platform neutral so that platform migration cost is low (for example, jumping from the .NET camp to J2EE).

    So you are a die-hard Microsoft shop and don't need to worry about other platforms? Well, you should still make your solution language neutral. You can achieve this by using the .NET Framework Class Library. It offers many encryption APIs that can be called from any .NET capable language, such as VB .NET, VC++, or C# [2].

    So far so good, but what if one of your developers forgot to call the encryption functions, called the wrong ones, or called the right ones with incorrect parameters? Encryption can be complex and difficult to get right the first time. Ideally, we want to make it transparent. This means using encryption automatically every time sensitive data is changed or inserted. The developers do not need to explicitly call any encryption function, so the programming errors listed above can be eliminated. One way to achieve transparency is to move the encryption logic from the application layer to the database layer.

    In this article we first introduce a free but high quality open source C++ encryption library. Then we show how we used it to solve two problems: (a) encryption of passwords based on SHA hashes, and (b) credit card number encryption/decryption using the triple DES algorithm. Finally, by wrapping the C++ encryption code inside two Microsoft SQL Server extended stored procedures, we move the encryption logic to the database layer, making the encryption both platform neutral and transparent.

    The Crypto++ Open Source C++ Class Library For Encryption

    Crypto++ [3], open source and in the public domain, is a very mature and well-designed C++ class library available in source code and pre-compiled form. The latest pre-compiled Win32 version is FIPS 140-2 Level 1 validated.

    Crypto++ covers almost all the better-known encryption algorithms. Sample algorithms are:

    • Symmetric block ciphers (same key for both encryption and decryption): DES, Triple-DES, Blowfish, TEA, Skipjack, etc.
    • public key cryptography (different key for encryption and decryption, and the decryption key cannot be derived from the encryption key): RSA, DSA, etc.
    • one-way hash functions : SHA-1, MD4, MD5, SHA-2 etc.
    • stream ciphers: Panama, ARC4, etc
    • padding schemes for public-key systems: PKCS#1 v2.0, OAEP, etc.
    • key agreement schemes: Diffie-Hellman (DH), etc.
    • utility functions such as base 64 coding ,32-bit CRC and random number generators
    • and many others [3].

    Key concepts in Crypto++ are data sources, sinks, and filters. In general, plain text data originates from a source, undergoes a series of filters (encryption algorithms), and emerges at a sink as the cipher text. The converse happens with decryption. The sources and sinks can be files, I/O or network streams, or C++ strings. Crypto++ has an elegant object-oriented class hierarchy and uses C++ templates heavily internally.

    The Crypto++ download contains MS Visual C++ workspace and project files that build the sources into a static library (cryptlib.lib). There are also example programs (see test.cpp).

    Although the latest version is 5.01, the author only verified all the code in this article with version 4.2.

    Password Encryption Using Crypto++

    Hashes are 'condensed representations' of a message or a data file. The 'condensed representation' is of fixed length and is known as a 'message digest' or 'fingerprint'. MD5 and SHA-1 are the two most widely used hashing algorithms. It is computationally infeasible to produce two messages having the same SHA-1 or MD5 hashes. So if we store the SHA-1 hash of user passwords in our databases, we can authenticate user logins by first computing the SHA-1 hash of the password supplied by the user who is attempting to log in and then comparing the hash against the copy stored in the database. Hashing algorithms are one-way, meaning nobody can "decrypt" a hash into the original message. So even if a hacker gains access to the hashes, he still has no way to learn about user passwords. Hashes are also often used to sign big file downloads to prove they have not been tampered.

    It takes less than 10 lines of code to compute a SHA-1 hash with Crypto++:

    
    ---- Listing 1, computing SHA hash ------
    1 	std::string* getShaHash(const char* str) {
    2		SHA				sha;
    3		HashFilter		shaFilter(sha);
    4		std::string*	pHashValue = new std::string();
    5		new StringSource(str, true, &shaFilter);
    6		Base64Encoder encoder(new StringSink(*pHashValue), false);
    7		shaFilter.TransferAllTo(encoder);
    8		return pHashValue;
    	}
    
    
    Function getShaHash accepts one parameter, a C string, and returns the BASE64 encoded SHA-1 hash of that string as a newly allocated C++ STL string object. Theoretically, the SHA-1 algorithm's input and output are both byte arrays; however, user passwords are naturally C strings and the hashes are typically stored in a database as string fields. This function will cast the input string to a byte array and present the SHA output as a string by BASE64 encoding the output byte array. Now let's look at the function line by line.

    Line 2 declares a SHA-1 algorithm object sha.

    Line 3 declares a filter which computes SHA-1 hashes.

    Line 4 allocates the return value of the function, a C++ STL string.

    Line 5 first declares a source object (in this case, a StringSource with the input C string as the data), then connects it with the SHA filter. The StringSource object will cast the input C string into a byte array before feeding it into the SHA-1 algorithm. Note here we dynamically allocate the StringSource object but do not free it later because its memory is managed internally by Crypto++.

    Line 6 declares a filter, the BASE64 encoder for encoding the byte array into a string, and a StringSink for holding that string.

    Line 7 starts the whole pipeline. It connects the SHA filter with the BASE64 encoder filter and starts data transforms from the StringSource all the way to StringSink. That is, the different stages of the pipeline will first compute the input C string's SHA hash, then encode the hash in BASE64 format and finally store the result in pHashValue.

    Note there's no explicit memory de-allocation anywhere because it's all taken care of by Crypto++ internally.

    Credit Card Number Encryption Using Crypto++

    Unlike password hashing, credit card number encryption cannot be one way because it must be decrypted before being sent to payment processors that process credit card transactions. Two-way encryptions can be symmetric or asymmetric. Public key encryption, widely used for e-commerce sites secured by SSL, is asymmetric because two keys must be used, one public and one private, for encryption and decryption. Public key encryption is ideal for two parties who need to communicate securely because only the public keys need to be exchanged between them. Any eavesdropper can have the public keys but cannot break the communication without the private keys.

    Here we are going to show how to perform simple credit card number encryption. We only need symmetric encryption, the same key for both encryption and decryption. We are going to use triple DES encryption. DES is an older US national standard for symmetric encryption [4]. Triple DES is just applying a single DES process three times with three different keys to make it more secure. For the so called DES-EDE3, the three steps are encryption, decryption, and encryption. For details see [5]. The tripleDes() function below will encrypt or decrypt (depending on the encrypting_mode parameter) an array of bytes (or chars) using the DES-EDE3 algorithm into another array of bytes.

    
    ---- Listing 2, triple DES encryption/decryption  ------
    	/**
     	* Function: perform 192 bit key triple DES (DES-EDE3) block encryption 
     	* Input Parameter:  plain_text, a C string or byte array that's the plain text
     	* Input Parameter:  len, length of the input plain text in bytes
    	* Input Parameter: encrypting_mode, true if encrypting, false if decrypting
     	* Output Parameter: output_length, the length of the encryption output in bytes
     	* Return: the encryption output, an array of bytes
     	*/
    	byte* tripleDes(const byte* plain_text, int len, int * output_length, bool 
    	encrypting_mode )
    	{
    0		static const byte encrKey[24] = 
    		{0x3F,0x6F,0x6B,0x69,0x20,0x5E,0x5F,0x45,0x65,0x54,0x5D,0x56,0x63,0x68,0x6E,0x6F
    		,0x14,0x32,0x2C,0x41,0x3F,0xD3,0x9B,0xA3};
    		/* initialize a DES-EDE3 encryption filter with a 192 bit (24 bytes) key */
    1		static DES_EDE3_Encryption des_encryptor (encrKey, 24);
    2		static DES_EDE3_Decryption des_decryptor (encrKey, 24);
    3	/* calculate how many blocks (each block is 8 bytes) the input consists of, add 
    	padding if needed */
    4		int nBlocks = len / 8;						
    5		if (len % 8 != 0)
    6			nBlocks ++;
    7		// allocate the output buffer
    8		byte*	pResult = new byte[nBlocks * 8];
    9		byte	plainByte [8];
    10		// encrypt the blocks one by one
    11		for (int block_num = 0; block_num < nBlocks; block_num ++)
    12		{
    13			// build the 8 byte block plain text, add padding if needed
    14			for (int i = 0 ; i < 8; i++)
    15			{
    16				// this is a full block
    17				if (block_num * 8 + i < len)
    18				{
    19					plainByte[i] = plain_text[block_num * 8 + i];
    20				}
    21				// this is the last block that needs padding
    22				else
    23				{
    24					plainByte[i]  = 0x00; // pad with a random byte
    25				}
    26			}
    27			// encrypt the 8 byte block and store in output buffer
    28		if (encrypting_mode)
    29			des_encryptor.ProcessBlock(plainByte, pResult + block_num * 8);	
    30		else
    31			des_decryptor.ProcessBlock(plainByte, pResult + block_num * 8);
    32		}
    33		*output_length= nBlocks * 8;
    34		return pResult;
    	} 
    
    
    Line 0 defines the 24 byte key.

    Line 1 and 2 creates a DES-EDE3 encryptor and decryptor respectively.

    Line 3 to 9 divide the input into blocks 8 bytes long and allocate the output buffer.

    Line 11 to 26 process each block in turn, pad the last block with zeros if less than 8 bytes long.

    Line 29 and 31 do the actual encryption or decryption.

    This is a generic function that can be used for credit card numbers as well as any arbitrarily long text.

    Transparent Encryption With SQL Server Extended Stored Procedures

    Think about your application. When a user enters his password for the first time to create an account, wouldn't it be nice if you can just store the password verbatim into the database. "You forgot to encrypt it!", you are screaming. What if the database silently encrypted any password handed to it? Then you'd be free from even having to think about encryption. If you are using Microsoft SQL Server, we can achieve this form of transparent encryption through SQL Server extended stored procedures (ESP). Similar tricks can be done in other databases too.

    You probably know a stored procedure is basically a set of SQL statements packaged as one function that can be executed with one call from an application. Extended stored procedures are functions written in C/C++ that can be directly called in a SQL statement as a SQL function. We are going to write one ESP using Visual C++ (or Visual Studio .NET). The steps are simple: write the ESP in C++, compile it, link it with opends60.lib (SQL Server's Open Data Services API library) to build a DLL, and finally copy the DLL to SQL Server's system directory (a typical directory is C:\Program Files\Microsoft SQL Server\MSSQL\binn). You may need SQL Server API SDK or Developer Edition to get opends60.lib. For details see [6] or SQL Server documentation.

    Assume you now know how to write ESPs. In SQL we want to call our password encrypting ESP like this:

    
    	/*Encrypt password */
    	exec master.dbo.xp_get_sha_hash @Password, @pass_sha OUTPUT
    
    
    Here @Password is the input plain text password, @pass_sha is the output hash value computed by our ESP xp_get_sha_hash().

    Then we can update the "password_hash" field of a user's record in the database (assuming the user is changing his password) like this:

    
    UPDATE UserAccount   SET   password_hash=@pass_sha WHERE user_id=@UserId
    
    
    If we wrap the above two SQL statements into a stored procedure with a signature of UpdatePassword(@Password,@UserId), then the application layer only needs to call this stored procedure (in an ODBC or JDBC call), with the encryption happening at the database layer transparently.

    Here is the actual C++ code for ESP xp_get_sha_hash(). You can see most of the code is just validating parameters and checking errors. Near the end it calls the getShaHash() function defined in listing 1, copies the resulting STL string to the output C string, and frees the STL string.

    
    --- Listing 3, extended stored procedure for computing password hash in SQL ---
    
    extern "C" __declspec(dllexport)  SRVRETCODE xp_get_sha_hash(SRV_PROC* pSrvProc)
    {
        char		msg [512];
        BYTE        param_type;
        ULONG       max_param_len;
        ULONG       actual_param_len;
        BOOL        bDummy;
    	char		input_buffer [INPUT_BUFFER_SIZE];
    
        // Count up the number of input parameters.  There should be 2.
        if (srv_rpcparams(pSrvProc) != 2)
        {
    		printHashUsage (pSrvProc);
    		return (XP_ERROR);
    	}
    	// ----------------------- first param --------------------
        // Use srv_paraminfo to get data type and length information.
        if (srv_paraminfo(pSrvProc, 1, ¶m_type, &max_param_len, 
    &actual_param_len, NULL, &bDummy) == FAIL)
        {
            printError (pSrvProc, "srv_paraminfo failed...");
            return (XP_ERROR);    
    	}
        // Make sure 1st parameter is a INPUT parameter
        if ((srv_paramstatus(pSrvProc, 1) & SRV_PARAMRETURN) != FAIL)
            {
            printError (pSrvProc, "First parameter must be input");
            return (XP_ERROR);    
            }
        // Make sure first parameter is of char or varchar datatype
        if (param_type != SRVBIGVARCHAR && param_type != SRVVARCHAR && param_type != 
    SRVBIGCHAR )
        {
            printError (pSrvProc,"First parameter must be of char or varchar type");
            return (XP_ERROR);    
        }
    
    	if (actual_param_len >= INPUT_BUFFER_SIZE) 
    	{
            sprintf (msg, "First parameter cannot be more than %d chars long, it's 
    %d chars" , INPUT_BUFFER_SIZE, actual_param_len);
    		printError (pSrvProc, "msg");
            return (XP_ERROR);    
        }
    	// Now store input to a buffer
        if (srv_paraminfo(pSrvProc, 1, ¶m_type, &max_param_len, 
    &actual_param_len, (unsigned char*)input_buffer, &bDummy) == FAIL)
        {
            printError (pSrvProc, "srv_paraminfo failed...");
            return (XP_ERROR);    
    	}
    	// must convert to  NULL terminated string.
    	input_buffer[actual_param_len] = '\0';
    
    	// ----------------------- 2 nd param --------------------
        // Use srv_paraminfo to get data type and length information.
        if (srv_paraminfo(pSrvProc, 2, ¶m_type, &max_param_len, 
    &actual_param_len, NULL, &bDummy) == FAIL)
        {
            printError (pSrvProc, "srv_paraminfo failed...");
            return (XP_ERROR);    
    	}
        // Make sure 2nd parameter is a return (OUTPUT) parameter
        if ((srv_paramstatus(pSrvProc, 2) & SRV_PARAMRETURN) == FAIL)
            {
            printError (pSrvProc,"Second parameter must be output");
            return (XP_ERROR);    
            }
        // Make sure first parameter is of char or varchar datatype
        if (param_type != SRVBIGVARCHAR && param_type != SRVVARCHAR && param_type != 
    SRVBIGCHAR )
        {
            printError (pSrvProc,"Second parameter must be of char or varchar 
    type");
            return (XP_ERROR);    
        }
    
    
        // Make sure 2nd paramter is large enough to hold data
    	std::string* hashStr = getShaHash(input_buffer);
    	if (hashStr->length() > max_param_len)
        {
    		sprintf (msg, "Second parameter too short to hold hash result, need 
    to be >= %d long", hashStr->length());
            printError (pSrvProc, msg);
    		delete hashStr;
            return (XP_ERROR);    
        }
    
        // Set the output parameter
        if (FAIL == srv_paramsetoutput(pSrvProc, 2, (unsigned char*) hashStr-
    >c_str(), (ULONG) hashStr->length(), FALSE))
        {
            printError (pSrvProc, "srv_paramsetoutput failed...");
    		delete hashStr;
            return (XP_ERROR);    
        }
    	delete hashStr;
        return (XP_NOERROR);
    }
    
    
    Remember, in your Visual C++ project that builds this ESP into the DLL, you need to add cryptlib.lib, the static library built from the Crypto++ download, to the list of static libraries (such as opends60.lib mentioned above) that need to be linked in.

    We can write another ESP for credit-card number encryption. This is left as an exercise for our readers.

    References

    [1] FBI Probing Theft of 8 Million Credit Card Numbers
    (http://abcnews.go.com/wire/US/reuters20030219_555.html)

    [2] The System.Security.Cryptography package in .NET class lib
    (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemsecuritycryptography.asp)

    [3] Crypto++: http://www.eskimo.com/~weidai/cryptlib.html

    [4] DATA ENCRYPTION STANDARD (DES), http://www.itl.nist.gov/fipspubs/fip46-2.htm

    [5] Triple DES Encryption, http://www.tropsoft.com/strongenc/des3.htm

    [6] Programming Extended Stored Procedures(in MSDN), http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odssql/ods_6_con_01_1ptb.asp

    Acknowledgement

    Tom McGarry at Lokitech came up with the original idea of encryption within stored procedures. Serge Knystautas helped with the planning of this article. The author wishes to thank their help and insights.

    About the Author

    Zhenlei Cai likes everything related to Java, Linux and the Internet. He enjoys getting his hands dirty in all kinds of new (and often open source) technologies: J2EE (servlets, JSP, JSTL, EJB ...), XML, Apache projects (Ant, Velocity), etc.

    Zhenlei writes software both for fun and for changing the world. One of his Java programs made him a finalists in the 2002 Google programming competition. He also developed and maintains a popular JSP/Linux powered Web site www.gaocan.com (Chinese) from a DSL connection in his bedroom. On any given day an average of 10,000 Chinese travelers use the site to find optimal train travel routes and look for other travelers' reviews on things like hotels.

    Most recently at Lokitech, he was a member of a small team that developed the world's first live video streaming Internet casino using J2EE technologies. Before that Zhenlei had worked in a few culturally-diverse places. These include the American network design and simulation software company OPNET and German industrial giant Siemens's ill-fated optical networking startup, Optisphere Networks. Zhenlei can be reached at "camelcai at fastmail dot fm".

  • Rate This Article
    Not HelpfulMost Helpful
    1 2 3 4 5
    Supporting Products/Tools
    AspEncrypt
    Built around the Microsoft CryptoAPI, AspEncrypt helps you harness all major encryption and hashing algorithms such as DES, Triple-DES, RC2, RC4, RSA, MD5 and SHA1 in just a few lines of code. The component can be used in tandem with AspEmail to send encrypted and signed mail in the industry-standard S/MIME format, or with AspUpload to encrypt files as they are being uploaded. AspEncrypt can also be used to issue and manage X.509 digital certificates.
    [Top]
    AspPDF
    AspPDF is an ASP/ASP.NET component which enables generation and management of documents in PDF format. Features include advanced text formatting, font embedding, form fill-in, images, tables, content and page extraction, document stitching, encryption, digital signatures, and more.
    [Top]
    Other Articles
    Feb 3, 2005 - ASP.NET Mixed Mode Authentication
    In many web applications it is desirable for both intranet users and external parties to be able to seamlessly log onto the system. The problem this raises is that it is not easy to allow intranet users to log in via Windows integrated authentication while also allowing external parties to log in to the same application using standard forms authentication. This article will show you one way to achieve the best of both worlds when it comes to authentication.
    [Read This Article]  [Top]
    Dec 8, 2004 - Designing Role-Based Security Models for .NET
    In this article, Michele Leroux Bustamante discusses authentication, authorization and role-based security in .NET. Along the way, he provides some best practices for implementing role-based security in some typical .NET application scenarios including rich clients, Web applications, and Web services.
    [Read This Article]  [Top]
    May 11, 2004 - SharePoint Security and .NET Impersonation
    When implementing custom components that require access to restricted resources, implicit impersonation must be used. Jay Nathan shows how to create a class that makes using .NET Impersonation a snap.
    [Read This Article]  [Top]
    Mar 10, 2004 - Intellectual Property Protection and Code Obfuscation
    Learn about the execution process of CLR-based programs and how to protect your applications from being easily disassembled back into source code.
    [Read This Article]  [Top]
    Feb 24, 2004 - How to Send Secure Mail in ASP-Based E-Commerce Applications - Part II
    Businesses that utilize encrypted e-mail may find Secure Multipurpose Internet Mail Extensions (S/MIME) to be somewhat restrictive. This article shows how to use security features in PDF as an alternative to S/MIME.
    [Read This Article]  [Top]
    Feb 2, 2004 - Fighting Spambots with .NET and AI
    Bill Gates, in a recent interview, predicted the end of spam by 2006. One of the methods he mentioned involved a challenge only a real live person could handle. Adnan Masood shows how to use AI and .NET to create a user verification scheme that incorporates similar concepts Gates alluded to.
    [Read This Article]  [Top]
    Jan 21, 2004 - Configuring .NET Code Access Security
    Code Access Security (CAS) is the .NET Framework security model that grants code permission to resources based on "evidence" pertaining to the encapsulating assembly. In this article, David Myers examines CAS and explains different configuration methods.
    [Read This Article]  [Top]
    Jan 15, 2003 - Exploring Machine.Config - User Security and More
    Christopher Spann offers a .NET configuration tip that should help ease system administrators' fears of security compromise and thus assuage growing developer demand for a .NET environment.
    [Read This Article]  [Top]
    Dec 10, 2002 - Encrypting Cookie Data with ASP.NET
    You don't have to be a cryptography expert or spend lots of money on third-party components to secure sensitive data in .NET. In this article, Wayne Plourde shows just how easy it is to encrypt cookie data using encryption classes in the .NET System.Security.Cryptography namespace.
    [Read This Article]  [Top]
    Aug 21, 2002 - Web Application Error Handling and Logging For ASP
    One of the most important aspects of an application is how well it responds to the user, and this includes response to errors. In this article, Adam Tuliper shares techniques for catching ASP errors and shows how to create a notification system that is sure to keep customers at bay.
    [Read This Article]  [Top]
    Mailing List
    Want to receive email when the next article is published? Just Click Here to sign up.

    Support the Active Server Industry



    JupiterOnlineMedia

    internet.comearthweb.comDevx.commediabistro.comGraphics.com

    Search:

    Jupitermedia Corporation has two divisions: Jupiterimages and JupiterOnlineMedia

    Jupitermedia Corporate Info


    Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

    Advertise | Newsletters | Tech Jobs | Shopping | E-mail Offers