Wednesday, 25th April 2012
Follow WikiJava on twitter now. @Wikijava

Secret key cryptography

From WikiJava

Jump to: navigation, search


This article is an extension and reorganization of the (very successful) Secret Key Cryptography Tutorial. In this article, thanks to AES for providing the code for using user given passwords, on the original article. The original article contains detailed explanation about how the cryptography works and how it's used here. I recommend you to check out that article too if you face troubles understanding this.

Contents

What's changed

This version of the secret key tutorial takes a secret key and an Inizialization vector [1]. Rather than generating a random password at each run.

This is done using the following piece of code:

SecretKeySpec keySpec = new SecretKeySpec(skey.getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(ivx.getBytes());

Where we basically generate secret key and initialization vector objects that can be used to generate the Cypher object.

Other changes are mostly for readability, I rationalized the exception handling, and extracted to a separate factory method the building of the Cypher.

Another difference is that this example uses the AES cryptographic algorithm rather than the DES that the other article used. Actually you can see in the code:

cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

That the algorithm used is defined as a string. So with little use of imagination you can modify the code to handle multiple algorithms. If you do so, feel free to post whatever you come out with on this wiki.

Below, the full code.

The full code

package org.wikijava.cryptography.generalExamples;
 
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
 
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
 
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
 
public class CryptographyWithReusedPass {
 
	private static String encrypt(String message, String skey, String ivx)
			throws CryptographyException, UnsupportedEncodingException {
 
		SecretKeySpec keySpec = new SecretKeySpec(skey.getBytes(), "AES");
		IvParameterSpec ivSpec = new IvParameterSpec(ivx.getBytes());
 
		Cipher cipher = getCypher(keySpec, ivSpec, Cipher.ENCRYPT_MODE);
 
		// Gets the raw bytes to encrypt, UTF8 is needed for
		// having a standard character set
		byte[] stringBytes;
 
		stringBytes = message.getBytes("UTF8");
 
		// encrypt using the cypher
		byte[] raw;
		try {
			raw = cipher.doFinal(stringBytes);
		} catch (IllegalBlockSizeException e) {
			throw new CryptographyException(e);
		} catch (BadPaddingException e) {
			throw new CryptographyException(e);
		}
 
		// converts to base64 for easier display.
		BASE64Encoder encoder = new BASE64Encoder();
		String base64 = encoder.encode(raw);
 
		return base64;
	}
 
	public static String decrypt(String encrypted, String skey, String ivx) throws CryptographyException {
 
 
		SecretKeySpec keySpec = new SecretKeySpec(skey.getBytes(), "AES");
		IvParameterSpec ivSpec = new IvParameterSpec(ivx.getBytes());
 
		Cipher cipher = getCypher(keySpec, ivSpec, Cipher.DECRYPT_MODE);
 
		// decode the BASE64 coded message
		BASE64Decoder decoder = new BASE64Decoder();
		byte[] raw;
		try {
			raw = decoder.decodeBuffer(encrypted);
		} catch (IOException e) {
			throw new CryptographyException("Reading error", e);
		}
 
		// decode the message
		byte[] stringBytes;
		try {
			stringBytes = cipher.doFinal(raw);
		} catch (IllegalBlockSizeException e) {
			throw new CryptographyException("Encrypted message was corrupted",
					e);
		} catch (BadPaddingException e) {
			throw new CryptographyException("Encrypted message was corrupted",
					e);
		}
 
		// converts the decoded message to a String
		String clear;
		try {
			clear = new String(stringBytes, "UTF8");
		} catch (UnsupportedEncodingException e) {
			throw new CryptographyException(e);
		}
		return clear;
	}
 
	/**
	 * @param keySpec
	 * @param ivSpec
	 * @param mode
	 * @return
	 * @throws CryptographyException
	 */
	public static Cipher getCypher(SecretKeySpec keySpec,
			IvParameterSpec ivSpec, int mode) throws CryptographyException {
		// Get a cipher object.
		Cipher cipher;
		try {
			cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException("invalid algorithm", e);
		} catch (NoSuchPaddingException e) {
			throw new RuntimeException("invalid padding", e);
		}
		try {
			cipher.init(mode, keySpec, ivSpec);
		} catch (InvalidKeyException e) {
			throw new CryptographyException("invalid key", e);
		} catch (InvalidAlgorithmParameterException e) {
			throw new RuntimeException("invalid algorithm parameter.", e);
		}
		return cipher;
	}
 
	/**
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		String message;
		String key;
		String ivx;
		if (args.length == 3) {
			message = args[0];
			key = args[1];
			ivx = args[2];
		} else {
			System.out.println("usage: ");
			System.out
					.println("CryptographyWithReusedPass [message] [skey] [ivx]\n key and ivx must have length 16 ");
			return;
		}
 
		System.out.println("clear message: " + message);
 
		String encrypted;
		try {
			encrypted = encrypt(message, key, ivx);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return;
		} catch (CryptographyException e) {
			e.printStackTrace();
			return;
		}
		System.out.println("encrypted message: " + encrypted);
 
		String decrypted;
		try {
			decrypted = decrypt(encrypted, key, ivx);
		} catch (CryptographyException e) {
			e.printStackTrace();
			return;
		}
		System.out.println("decrypted message: " + decrypted);
 
	}
}


Bonus - full code for a demo JSP (without nice error checking)

Below is the full code for a demo JSP demonstrating the same ideas as above, with the extra bonus of demonstrating a simple one-way hashing function. Hashing is more secure for storing and checking user passwords for example. Note that this hashing example isn't as secure as it could be however since it doesn't use salt (so that every result is different) and just checks for equality after hashing.

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page language="Java" import="javax.crypto.*" %>
<%@ page language="Java" import="javax.crypto.spec.*" %>
<%@ page language="Java" import="sun.misc.BASE64Decoder" %>
<%@ page language="Java" import="sun.misc.BASE64Encoder" %>
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Encryption/Decryption/Hashing Demo!</title>
</head>
<%
//this is not a recommended way to securely store your key or password obviously
//also note the hashing here doesn't use salt and just checks by equality after hashing so it is not as secure
//also there are a lot of exceptions that aren't being caught nicely here
 
//define hardcoded key here as a String. Should be stored in a better way.
String key = "7777777777777777";//must be 16 long
String iv =  "8888888888888888";//must be 16 long
 
//gather submitted values
String action = request.getParameter("action");
if (action == null || action.equals("null"))
    action = "no action";
String orig_string = request.getParameter("orig_string");
 
//define some variables to use later
String result_string = "";
String result_display = "";
 
//check which action we need to do and perform action
if (action.equals("no action")){
    result_display = "";
}
else {
 
    result_display = "<b>Results</b> <br>";
 
    if (action.equals("encrypt")){
        //we have a string to encrypt in orig_string, encrypt it and display
 
        //get key stuff, using given key so we can encrypt and decrypt with it
        SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
        IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
 
        //get a cipher object.
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
 
        //get the raw bytes to encrypt, UTF8 is needed for having a standard character set
        byte[] stringBytes = orig_string.getBytes("UTF8");
 
        //encrypt using the cypher
        byte[] raw = cipher.doFinal(stringBytes);
 
        //convert to base64 for easier display (and storing like in a database)
        BASE64Encoder encoder = new BASE64Encoder();
        result_string = encoder.encode(raw);
 
        //build display
        result_display += "<p>Action completed: "+action+"</p>";
        result_display += "<p>Original string: ["+orig_string+"]</p>";
        result_display += "<p>Encrypted string: [<b>"+result_string+"</b>]</p>";
    }
    else if (action.equals("decrypt")){
        //we have a string to decrypt in orig_string, decrypt it and display
 
        //get key stuff, using given key so we can encrypt and decrypt with it
        SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
        IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
 
        //get a cipher object.
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
 
        //decode the BASE64 coded message
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] raw = decoder.decodeBuffer(orig_string);
 
        //decode the message
        byte[] stringBytes = cipher.doFinal(raw);
 
        //convert the decoded message to a String
        result_string = new String(stringBytes, "UTF8");
 
        //build display
        result_display += "<p>Action completed: "+action+"</p>";
        result_display += "<p>Original string: ["+orig_string+"]</p>";
        result_display += "<p>Decrypted string: [<b>"+result_string+"</b>]</p>";
    }
    else if (action.equals("hash")){
        //we have a string to hash in orig_string, hash it and display
 
        //hash, results in byte array
        java.security.MessageDigest d = java.security.MessageDigest.getInstance("SHA-1");
        d.reset();
        d.update(orig_string.getBytes());
        byte[] b = d.digest();
 
        //now convert byte array to hex string
        StringBuilder sb = new StringBuilder(b.length * 2);
        for (int i = 0; i < b.length; i++){
            int v = b[i] & 0xff;
            if (v < 16)
                sb.append('0');
            sb.append(Integer.toHexString(v));
        }
        result_string = sb.toString();
 
        //build display
        result_display += "<p>Action completed: "+action+"</p>";
        result_display += "<p>Original string: ["+orig_string+"]</p>";
        result_display += "<p>Hashed string: [<b>"+result_string+"</b>]</p>";
    }
    else if (action.equals("hash-check")){
        //we have a string that is supposed to be the stored hashed password in stored_hashed_password_string
        //and one that is a user entered password in plain text in orig_string
        String stored_hashed_password = request.getParameter("stored_hashed_password_string");
        String input_string_hashed = "";
 
        //hash the plain text password, results in byte array
        java.security.MessageDigest d = java.security.MessageDigest.getInstance("SHA-1");
        d.reset();
        d.update(orig_string.getBytes());
        byte[] b = d.digest();
 
        //now convert byte array to hex string
        StringBuilder sb = new StringBuilder(b.length * 2);
        for (int i = 0; i < b.length; i++){
            int v = b[i] & 0xff;
            if (v < 16)
                sb.append('0');
            sb.append(Integer.toHexString(v));
        }
        input_string_hashed = sb.toString();
 
        //do match check
        String match_display = "hash-check failed, no match";
        if (input_string_hashed.equals(stored_hashed_password))
            match_display = "hash-check succeeded, matched!";
 
        //build display
        result_display += "<p>Action completed: "+action+"</p>";
        result_display += "<p>Input string: "+orig_string+"</p>";
        result_display += "<p>Input string hashed: ["+input_string_hashed+"]";
        result_display += "<br>Stored hashed string: ["+stored_hashed_password+"]</p>";
        result_display += "<p><b>"+match_display+"</b></p>";
    }
}
 
%>
<h2>Demo</h2>
<h3>Demonstrates basics of symmetrical encryption and decryption as well as hashing!</h3>
 
<div style="background-color: yellow; margin-bottom: 25px; margin-top: 20px;">
    <%=result_display%>
</div>
 
<hr>
 
<div style="margin-bottom: 25px;">
    <b>Encrypting</b><br>
    <form method="post" name="encryptDecryptForm" action="encryptDecryptHashDemo.jsp">
        Original (plaintext): <input name="orig_string" value="">
        <input type=hidden name="action" value="encrypt">
        <input type=submit name="submit" value="encrypt this">
    </form>
</div>
 
<div style="margin-bottom: 25px;">
    <b>Decrypting</b><br>
    <form method="post" name="encryptDecryptForm" action="encryptDecryptHashDemo.jsp">
        Original (encrypted): <input name="orig_string" value="">
        <input type=hidden name="action" value="decrypt">
        <input type=submit name="submit" value="decrypt this">
    </form>
</div>
 
<div style="margin-bottom: 25px;">
    <b>One-way hashing</b><br>
    <form method="post" name="encryptDecryptForm" action="encryptDecryptHashDemo.jsp">
        Original (before hash): <input name="orig_string" value="">
        <input type=hidden name="action" value="hash">
        <input type=submit name="submit" value="hash this">
    </form>
</div>
 
<div style="margin-bottom: 25px;">
    <b>Password check/hashing:</b><br>
    -an example is [thisismysecretpassword] which should hash to [beafed26caf2bca148540bd3ce216c567e3824e7])<br>
    <form method="post" name="encryptDecryptForm" action="encryptDecryptHashDemo.jsp">
        Stored Hashed Password: <input name="stored_hashed_password_string" value=""><br>
        Password: <input name="orig_string" value=""><br>
        <input type=hidden name="action" value="hash-check">
        <input type=submit name="submit" value="password check this">
    </form>
</div>
 
</html>

Comments from the users

To be notified via mail on the updates of this discussion you can login and click on watch at the top of the page


Comments on wikijava are disabled now, cause excessive spam.