Dr. Dobb's Journal April 2001
The Java Cryptography Extension (JCE) 1.2.1 provides cryptographic support for Java applications via its javax.crypto package. More specifically, the javax.crypto classes and interfaces enable operations such as encryption, key generation/key agreement, and Message Authentication Code (MAC). Specific support for encryption includes symmetric, asymmetric, block, and stream ciphers. In this article, I'll describe how you can add security to Java-based e-commerce systems by using javax.crypto to encrypt/decrypt a user's credit-card numbers.
There are generally two problems with the way many e-commerce sites handle credit-card numbers: They disallow spaces on input and they store them as plain text. The first problem makes it difficult for users to check if they correctly typed in their card numbers. You can't always add spaces and delete them before pressing the submit button because there frequently is a limit on how many characters may be typed into the field. The second problem makes it possible for intruders who break into a site's database to use the numbers without much effort.
The text fields for entering credit-card numbers and the corresponding instructions should be adjusted to let users enter spaces. Listing One is a Java class for preparing credit-card numbers. The removeSpaces() function takes a String and returns another one that does not contain any spaces. It works by stepping through the input String and storing characters that are not spaces in a StringBuffer. Afterwards, the String corresponding to this buffer is returned.
Alternatively, the keepDigits() function steps through the input String and retains only digits. This function is also useful for preparing phone and Social Security numbers that are typed into a single field. The function keepPhoneDigits() starts with the results of keepDigits(), tests for the presence of an area code, prepends a "1" if needed, and tests if the total length is correct. You will still need a separate field for entering a phone extension. Since keepDigits() uses Character.isDigit() for testing if a character is a digit, Unicode digits other than '0' through '9' might be included. If this becomes a concern, substitute the test '0'<=c<= '9'.
Chapter 4 of David Flanagan's Java in a Nutshell, Third Edition (O'Reilly & Associates, 1999) includes code showing how to use the Java encryption methods. You will need a web server that supports Java 2 SDK 1.2.1 or later and the javax.crypto package (available at http://java.sun.com/products/jce/index.html). Alternatively, you can use any full-strength encryption method. Do not use a simple method, such as XORing with a fixed sequence of bytes, because an intruder could enter a stolen credit-card number and deduce the key by comparing the plain text and encrypted versions of this number. Listing Two is an encryption/decryption implementation that uses the javax.crypto package.
For testing purposes, it is okay to store the encryption password in a server program. Since programs can generally be decompiled, this password should not be stored in the release version of a server program. Storing this password in a file or in the server's configuration data is not much better. Instead, encryption passwords should only be stored in memory. They should get there by using an HTML form and secure connection. The passwords should be stored in a locked cabinet and changed periodically. When changing a password, all stored credit-card numbers are to be decrypted with the old password and reencrypted with the new one. To prevent a brute force attack on the password, be sure to use a long password and an encryption method that supports using such passwords. DES only allows up to eight characters in a password while Triple DES allows up to 24 characters.
Although web servers can be set up so that only part of the file system is accessible to the public, putting the database on a separate computer from the server makes it harder for a hacker to get to it because two computers would have to be compromised. Some databases can be configured so that they only accept commands from specified computers. This adds a layer of protection since working through an authorized computer complicates the attack and increases the chance of detection.
If credit-card input fields allow spaces, then users can easily compare their card numbers to the ones they typed in. The Java class presented here has two ways for preparing credit-card numbers that contain spaces. In addition, these functions and another one in this class are useful for simplifying entry of telephone and Social Security numbers.
With occasional reports of credit-card numbers being obtained by intruders, it shouldn't be necessary to remind e-commerce programmers that credit-card numbers should only be stored in encrypted form. The code presented here effectively encrypts/decrypts credit-card numbers. If your web server does not support a high enough version of Java, then you will need to use another encryption method. In addition, protecting the encryption password and running the database on a computer other than your web server adds a layer of protection.
DDJ
public class CCNumber {
public static String removeSpaces (String ccNum) {
if (ccNum == null) return "";
int length = ccNum.length();
StringBuffer sbuf = new StringBuffer();
char c;
for (int i = 0; i < length; i++) {
c = ccNum.charAt (i);
if (c != ' ')
sbuf.append (c);
}
return sbuf.toString();
}
// Also useful for phone and Social Security numbers
public static String keepDigits (String ccNum) {
if (ccNum == null) return "";
int length = ccNum.length();
StringBuffer sbuf = new StringBuffer();
char c;
for (int i = 0; i < length; i++) {
c = ccNum.charAt (i);
if (Character.isDigit (c))
sbuf.append (c);
}
return sbuf.toString();
}
public static String keepPhoneDigits (String ccNum) {
String text = keepDigits (ccNum);
if (text.length() < 10)
return "** Missing area code: " + text;
if (!text.startsWith ("1"))
text = "1" + text;
if (text.length() != 11)
return "** Error in phone number: " + text;
return text;
}
}
import javax.crypto.*;
import javax.crypto.spec.*;
public class CCNumber {
// Same as in Listing One
public static byte[] encrypt (String ccNum, String password) {
if (ccNum == null || password == null) return null;
// Uses triple DES; password is up to 24 bytes long
SecretKey key = new SecretKeySpec (password.getBytes(), "DESede");
Cipher cipher = Cipher.getInstance ("DESede");
cipher.init (Cipher.ENCRYPT_MODE, key);
return cipher.doFinal (ccNum.getBytes());
}
public static String decrypt (byte[] codedNum, String password) {
if (codedNum == null || password == null) return null;
// Uses triple DES; password is up to 24 bytes long
SecretKey key = new SecretKeySpec (password.getBytes(), "DESede");
Cipher cipher = Cipher.getInstance ("DESede");
cipher.init (Cipher.DECRYPT_MODE, key);
return cipher.doFinal (codedNum.getBytes());
}
}