Secret key cryptographyFrom WikiJava
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.
What's changedThis 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 codepackage 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> |
