Java tutorial
/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache 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.apache.org/licenses/LICENSE-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.apache.hadoop.gateway.services.security.impl; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; import org.apache.commons.net.ntp.TimeStamp; import org.apache.hadoop.gateway.config.GatewayConfig; import org.apache.hadoop.gateway.services.ServiceLifecycleException; import org.apache.hadoop.gateway.services.security.EncryptionResult; import org.apache.hadoop.gateway.services.security.MasterService; import java.io.Console; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; public class DefaultMasterService implements MasterService { private static final String MASTER_PASSPHRASE = "masterpassphrase"; private static final String MASTER_PERSISTENCE_TAG = "#1.0# " + TimeStamp.getCurrentTime().toDateString(); private char[] master = null; private AESEncryptor aes = new AESEncryptor(MASTER_PASSPHRASE); /* (non-Javadoc) * @see org.apache.hadoop.gateway.services.security.impl.MasterService#getMasterSecret() */ @Override public char[] getMasterSecret() { // TODO: check permission call here return this.master; } @Override public void init(GatewayConfig config, Map<String, String> options) throws ServiceLifecycleException { // for testing only if (options.containsKey("master")) { this.master = options.get("master").toCharArray(); } else { File masterFile = new File( config.getGatewayHomeDir() + File.separator + "conf" + File.separator + "security", "master"); if (masterFile.exists()) { try { initializeFromMaster(masterFile); } catch (Exception e) { // TODO Auto-generated catch block throw new ServiceLifecycleException("Unable to load the persisted master secret.", e); } } else { if (options.get("persist-master").equals("true")) { displayWarning(true); } else { displayWarning(false); } promptUser(); if (options.get("persist-master").equals("true")) { persistMaster(master, masterFile); } } } } private void promptUser() { Console c = System.console(); if (c == null) { System.err.println("No console."); System.exit(1); } boolean noMatch; do { char[] newPassword1 = c.readPassword("Enter master secret: "); char[] newPassword2 = c.readPassword("Enter master secret again: "); noMatch = !Arrays.equals(newPassword1, newPassword2); if (noMatch) { c.format("Passwords don't match. Try again.%n"); } else { this.master = Arrays.copyOf(newPassword1, newPassword1.length); } Arrays.fill(newPassword1, ' '); Arrays.fill(newPassword2, ' '); } while (noMatch); } private void displayWarning(boolean persisting) { Console c = System.console(); if (c == null) { System.err.println("No console."); System.exit(1); } if (persisting) { c.printf( "***************************************************************************************************\n"); c.printf( "You have indicated that you would like to persist the master secret for this gateway instance.\n"); c.printf("Be aware that this is less secure than manually entering the secret on startup.\n"); c.printf("The persisted file will be encrypted and primarily protected through OS permissions.\n"); c.printf( "***************************************************************************************************\n"); } else { c.printf( "***************************************************************************************************\n"); c.printf( "Be aware that you will need to enter your master secret for future starts exactly as you do here.\n"); c.printf("This secret is needed to access protected resources for the gateway process.\n"); c.printf("The master secret must be protected, kept secret and not stored in clear text anywhere.\n"); c.printf( "***************************************************************************************************\n"); } } private void persistMaster(char[] master, File masterFile) { EncryptionResult atom = encryptMaster(master); try { ArrayList<String> lines = new ArrayList<String>(); lines.add(MASTER_PERSISTENCE_TAG); String line = Base64.encodeBase64String( (Base64.encodeBase64String(atom.salt) + "::" + Base64.encodeBase64String(atom.iv) + "::" + Base64.encodeBase64String(atom.cipher)).getBytes("UTF8")); lines.add(line); FileUtils.writeLines(masterFile, "UTF8", lines); // restrict os permissions to only the user running this process chmod("600", masterFile); } catch (IOException e) { // TODO log appropriate message that the master secret has not been persisted e.printStackTrace(); } } private EncryptionResult encryptMaster(char[] master) { // TODO Auto-generated method stub try { return aes.encrypt(new String(master)); } catch (Exception e) { // TODO log failed encryption attempt // need to ensure that we don't persist now e.printStackTrace(); } return null; } private void initializeFromMaster(File masterFile) throws Exception { try { List<String> lines = FileUtils.readLines(masterFile, "UTF8"); String tag = lines.get(0); // TODO: log - if appropriate - at least at finest level System.out.println("Loading from persistent master: " + tag); String line = new String(Base64.decodeBase64(lines.get(1))); String[] parts = line.split("::"); //System.out.println("salt: " + parts[0] + " : " + Base64.decodeBase64(parts[0])); //System.out.println("iv: " + parts[1]); //System.out.println("cipher: " + parts[2]); this.master = new String(aes.decrypt(Base64.decodeBase64(parts[0]), Base64.decodeBase64(parts[1]), Base64.decodeBase64(parts[2])), "UTF8").toCharArray(); } catch (IOException e) { e.printStackTrace(); throw e; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); throw e; } } @Override public void start() throws ServiceLifecycleException { } @Override public void stop() throws ServiceLifecycleException { } private void chmod(String args, File file) throws IOException { // TODO: move to Java 7 NIO support to add windows as well // TODO: look into the following for Windows: Runtime.getRuntime().exec("attrib -r myFile"); if (isUnixEnv()) { //args and file should never be null. if (args == null || file == null) throw new IOException("nullArg"); if (!file.exists()) throw new IOException("fileNotFound"); // " +" regular expression for 1 or more spaces final String[] argsString = args.split(" +"); List<String> cmdList = new ArrayList<String>(); cmdList.add("/bin/chmod"); cmdList.addAll(Arrays.asList(argsString)); cmdList.add(file.getAbsolutePath()); new ProcessBuilder(cmdList).start(); } } private boolean isUnixEnv() { return (File.separatorChar == '/'); } }