/** * $URL: https://source.sakaiproject.org/svn/basiclti/trunk/basiclti-common/src/java/org/sakaiproject/linktool/LinkToolUtil.java $ * $Id: LinkToolUtil.java 105077 2012-02-24 22:54:29Z ottenhoff@longsight.com $ * * Copyright (c) 2006-2009 The Sakai Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sakaiproject.linktool; import java.io.File; import java.io.IOException; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileInputStream; import org.sakaiproject.component.cover.ServerConfigurationService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.security.*; import javax.crypto.*; import javax.crypto.spec.*; /** * Some Sakai Utility code for the Rutgers LinkTool. */ public class LinkToolUtil { /** Our log (commons). */ private static Log M_log = LogFactory.getLog(LinkToolUtil.class); private static final String privkeyname = "sakai.rutgers.linktool.privkey"; private static final String saltname = "sakai.rutgers.linktool.salt"; private static Object sync_object = new Object(); private static boolean LinkToolSetupComplete = false; private static String homedir = null; private static SecretKey secretKey = null; private static SecretKey salt = null; private static SecretKey readSecretKey(String filename, String alg) { FileInputStream file = null; SecretKey privkey = null; try { file = new FileInputStream(filename); byte[] bytes = new byte[file.available()]; file.read(bytes); privkey = new SecretKeySpec(bytes, alg); } catch (Exception e) { M_log.error("Unable to read key from " + filename); privkey = null; } finally { if ( file != null ) { try { file.close(); } catch (Exception e) { M_log.error("Unable to close file " + filename); } } } return privkey; } private static char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * Convert byte array to hex string * * @param ba * array of bytes * @throws Exception. */ private static String byteArray2Hex(byte[] ba){ StringBuffer sb = new StringBuffer(); for (int i = 0; i < ba.length; i++){ int hbits = (ba[i] & 0x000000f0) >> 4; int lbits = ba[i] & 0x0000000f; sb.append("" + hexChars[hbits] + hexChars[lbits]); } return sb.toString(); } /** * Generate a secret key, and write it to a file * * @param dirname * writes to file privkeyname in this * directory. dirname assumed to end in / */ private static void genkey(String dirname) { try { /* Generate key. */ M_log.info("Generating new key in " + dirname + privkeyname); SecretKey key = KeyGenerator.getInstance("Blowfish").generateKey(); /* Write private key to file. */ writeKey(key, dirname + privkeyname); } catch (Exception e) { M_log.debug("Error generating key", e); } } /** * Writes <code>key</code> to file with name <code>filename</code> * * @throws IOException if something goes wrong. */ private static void writeKey(Key key, String filename) { FileOutputStream file = null; try { file = new FileOutputStream(filename); file.write(key.getEncoded()); } catch (FileNotFoundException e) { M_log.error("Unable to write new key to " + filename); } catch (IOException e) { M_log.error("Unable to write new key to " + filename); } finally { if ( file != null ) { try { file.close(); } catch (Exception e) { M_log.error("Unable to write new key to " + filename); } } } } /** * Generate a random salt, and write it to a file * * @param dirname * writes to file saltname in this * directory. dirname assumed to end in / */ private static void gensalt(String dirname) { try { // Generate a key for the HMAC-SHA1 keyed-hashing algorithm KeyGenerator keyGen = KeyGenerator.getInstance("HmacSHA1"); SecretKey key = keyGen.generateKey(); writeKey(key, dirname + saltname); } catch (Exception e) { M_log.warn("Error generating salt", e); } } public static String encrypt(String str) { LinkToolSetup(); if ( secretKey == null ) return null; try { Cipher ecipher = Cipher.getInstance("Blowfish"); ecipher.init(Cipher.ENCRYPT_MODE, secretKey); // Encode the string into bytes using utf-8 byte[] utf8 = str.getBytes("UTF8"); // Encrypt byte[] enc = ecipher.doFinal(utf8); // Encode bytes to base64 to get a string return byteArray2Hex(enc); } catch (javax.crypto.BadPaddingException e) { M_log.warn("linktool encrypt bad padding"); } catch (javax.crypto.IllegalBlockSizeException e) { M_log.warn("linktool encrypt illegal block size"); } catch (java.security.NoSuchAlgorithmException e) { M_log.warn("linktool encrypt no such algorithm"); } catch (java.security.InvalidKeyException e) { M_log.warn("linktool encrypt invalid key"); } catch (javax.crypto.NoSuchPaddingException e) { M_log.warn("linktool encrypt no such padding"); } catch (java.io.UnsupportedEncodingException e) { M_log.warn("linktool encrypt unsupported encoding"); } return null; } public static String decrypt (String enc) { LinkToolSetup(); if ( secretKey == null ) return null; try { Cipher dcipher = Cipher.getInstance("Blowfish"); dcipher.init(Cipher.DECRYPT_MODE, secretKey); byte[] dec = hex2byte(enc); // Decrypt byte[] utf8 = dcipher.doFinal(dec); // Decode using utf-8 return new String(utf8, "UTF8"); } catch (Exception ignore) { M_log.warn("linktool decrypt failed"); } return null; } public static byte[] hex2byte(String strhex) { if(strhex==null) return null; int l = strhex.length(); if(l %2 ==1) return null; byte[] b = new byte[l/2]; for(int i = 0 ; i < l/2 ;i++){ b[i] = (byte)Integer.parseInt(strhex.substring(i *2,i*2 +2),16); } return b; } // Setup the LinkTool Environment Variables one time // If this fails, it does not re-try public static void LinkToolSetup() { if ( LinkToolSetupComplete ) return; // We will only do this once - so we make all wait here synchronized(sync_object) { homedir = ServerConfigurationService.getString("linktool.home", ServerConfigurationService.getSakaiHomePath()); if (homedir == null) homedir = "/etc/"; if (!homedir.endsWith("/")) homedir = homedir + "/"; if (!(new File(homedir + privkeyname)).canRead()) { genkey(homedir); } secretKey = readSecretKey(homedir + privkeyname, "Blowfish"); if (!(new File(homedir + saltname)).canRead()) { gensalt(homedir); } salt = readSecretKey(homedir + saltname, "HmacSHA1"); if ( salt != null && secretKey != null ) { M_log.info("LinkToolSetup complete"); } else { M_log.warn("LinkToolSetup failed - cannot create encrypted sessions"); } LinkToolSetupComplete = true; } } }