org.apache.hadoop.io.crypto.tool.BeeCli.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.io.crypto.tool.BeeCli.java

Source

/**
 * 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.io.crypto.tool;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.net.URL;
import java.util.Random;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.codec.binary.Base64;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.crypto.bee.BeeConstants;
import org.apache.hadoop.io.crypto.bee.RestResult;
import org.apache.hadoop.io.crypto.bee.key.sasl.common.SaslUtil;
import org.apache.hadoop.io.crypto.tool.kerberos.SpnegoRestCli;
import org.apache.hadoop.security.Credentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

public class BeeCli {
    static final Logger logger = LoggerFactory.getLogger(BeeCli.class);

    public enum CmdType {
        NONE(0), TOKEN(1), CRYPTO(2);
        public final int type;

        private CmdType(int type) {
            this.type = type;
        }
    }

    public enum CmdAction {
        NONE(0), TOKEN_CREATE(1), TOKEN_REMOVE(2), CRYPTO_ENCRYPT(3), CRYPTO_DECRYPT(4);
        public final int action;

        private CmdAction(int action) {
            this.action = action;
        }
    }

    private String strManagerUrl = "";
    private CmdType cmdType = CmdType.NONE;
    private CmdAction cmdAction = CmdAction.NONE;
    private String strFileInputPath = "";
    private String strFileOutputPath = "";
    private String strKeyName = "";
    private String strHexKey = "";

    private URL buildRequestUrl(String method) throws Exception {
        return buildRequestUrl(method, null);
    }

    private URL buildRequestUrl(String method, String parameter) throws Exception {
        // String strUrl = "http://" + this.strManagerUrl + BeeConstants.API_ROOT +
        // method;
        String strUrl = "https://" + this.strManagerUrl + BeeConstants.API_ROOT + method;
        if (parameter != null && !parameter.isEmpty()) {
            strUrl = strUrl + "/" + Base64.encodeBase64URLSafeString(parameter.getBytes());
        }

        return new URL(strUrl);
    }

    private void createToken() throws Exception {
        if (new File(this.strFileOutputPath).exists())
            throw new Exception("Output file already exists: " + this.strFileOutputPath);

        StringBuffer sb = new SpnegoRestCli(buildRequestUrl("createToken")).getResult();

        // logger.debug("Get response:" + sb.toString());

        JsonElement jelement = new JsonParser().parse(sb.toString());
        JsonObject jobject = jelement.getAsJsonObject();
        JsonObject jobjectMsg = jobject.getAsJsonObject("msg");
        String msg = jobjectMsg.toString();

        String result = jobject.get("result").toString();
        if (0 == result.replaceAll("\"", "").compareTo(BeeConstants.ResponseStatus.SUCCESS.toString())) {
            Credentials credentials = new Credentials();
            credentials.addSecretKey(new Text(SaslUtil.KEY_TOKEN_ALIAS), msg.getBytes());

            credentailsFileWriter(credentials, this.strFileOutputPath);
        } else {
            throw new Exception("Create token from Key Manager Server fail: " + msg);
        }
    }

    private void credentailsFileWriter(Credentials credentials, String path) throws Exception {
        DataOutputStream dos = new DataOutputStream(new FileOutputStream(path));
        credentials.writeTokenStorageToStream(dos);
        File f = new File(path);
        f.setExecutable(false, false);
        f.setReadable(true, true);
        f.setWritable(true, true);
    }

    private File rename2TmpFile(String srcPath) throws Exception {
        File file = new File(srcPath).getAbsoluteFile();
        File parentFile = file.getParentFile();
        String tmpName = "." + file.getName() + new Random().nextInt(10000);
        File tmpFile = new File(parentFile, tmpName);

        // In current Java version on linux, we should add more checking,
        // otherwise, File.renameTo always return true even file not movable
        if (file.exists()) {
            if (file.getParentFile().canWrite()) {
                if (file.renameTo(tmpFile)) {
                    return tmpFile;
                } else {
                    throw new Exception("Fail to delete file: " + this.strFileInputPath);
                }
            } else {
                throw new Exception("Permission denied to remove the file: " + file.getPath());
            }
        } else {
            throw new Exception("Input file not exist");
        }
    }

    private void removeToken() throws Exception {
        if (!new File(this.strFileInputPath).getParentFile().canExecute()) {
            throw new Exception("Permission Denied to access " + this.strFileInputPath);
        }
        Credentials credentials = Credentials.readTokenStorageFile(new Path("file://" + this.strFileInputPath),
                new Configuration());

        byte[] buffer = credentials.getSecretKey(new Text(SaslUtil.KEY_TOKEN_ALIAS));
        if (buffer.length == 0)
            throw new Exception("Fail! Bad Token file, fail to get token value");

        File tmpFile = rename2TmpFile(this.strFileInputPath);

        try {
            StringBuffer sb = new SpnegoRestCli(this.buildRequestUrl("removeToken", new String(buffer)))
                    .getResult();

            RestResult restResult = new Gson().fromJson(sb.toString(), RestResult.class);

            if (restResult.getResult().equals(BeeConstants.ResponseStatus.SUCCESS.toString())) {
                tmpFile.delete();
            } else {
                // Rollback: Move back the credential file
                // In general, no exception will be raise by File.renameTo
                // because it has write permission here (file.renameTo() is passed)
                tmpFile.renameTo(new File(this.strFileInputPath));
                throw new Exception("Fail to remove token from Key Manager server:" + restResult.getMsg());
            }

        } catch (Exception e) {
            // Rollback: Write back the credential file
            // In general, no exception will be raise by File.renameTo
            // because it has write permission here (file.delete() is passed)
            tmpFile.renameTo(new File(this.strFileInputPath));
            throw new Exception(e);
        }
    }

    private void fileCrypto() throws Exception {
        String strFileIn = "file://" + this.strFileInputPath;
        String strFileOut = "file://" + this.strFileOutputPath;
        String hexKey;
        if (this.strHexKey.isEmpty()) {
            StringBuffer sb = new SpnegoRestCli(buildRequestUrl("getHexkey/" + this.strKeyName)).getResult();
            RestResult restResult = new Gson().fromJson(sb.toString(), RestResult.class);
            if (restResult.getResult().equals(BeeConstants.ResponseStatus.SUCCESS.toString())) {
                hexKey = restResult.getMsg();
            } else {
                throw new Exception("Error! Get key from Key Manager fail. Message: " + restResult.getMsg());
            }
        } else {
            hexKey = this.strHexKey;
        }
        CryptoApiTool cryptoApiTool = new CryptoApiTool(hexKey, strFileIn, strFileOut);

        if (this.cmdAction.equals(CmdAction.CRYPTO_ENCRYPT)) {
            cryptoApiTool.apiEncryption();
        } else {
            cryptoApiTool.apiDecryption();
        }
    }

    public void requestHandler() throws Exception {
        if (this.cmdType == CmdType.TOKEN) {
            if (this.cmdAction == CmdAction.TOKEN_CREATE) {
                createToken();
            } else if (this.cmdAction == CmdAction.TOKEN_REMOVE) {
                removeToken();
            } else {
                throw new Exception("Not supported commnad action(" + cmdAction + ") for type (" + cmdType + ")");
            }
        } else if (this.cmdType == CmdType.CRYPTO) {
            fileCrypto();
        } else {
            throw new Exception("Not Supported command type");
        }
    }

    private void errorExitOnSetup(String msg) throws ParseException {
        System.out.println();
        System.out.println("Error! " + msg);
        System.out.println();
        System.exit(1);

    }

    public void setupOptions(String[] args) throws ParseException {
        String strUsage = "Usage: Support Actions\n"
                + "    Token Create: bee-cli.sh -type token -action create -km <BeeKeyManager:port> -fileout <FILEOUT>\n"
                + "    Token Remove: bee-cli.sh -type token -action remove -km <BeeKeyManager:port> -filein <FILEIN>\n"
                + "    Crypto Encrypt: bee-cli.sh -type crypto -action encrypt -km <BeeKeyManager:port> -keyname <keyname> -filein <FILEIN> -fileout <FILEOUT>\n"
                + "    Crypto Encrypt: bee-cli.sh -type crypto -action encrypt -hexkey <HexKeyString> -filein <FILEIN> -fileout <FILEOUT>\n"
                + "    Crypto Decrypt: bee-cli.sh -type crypto -action decrypt -km <BeeKeyManager:port> -keyname <keyname> -filein <FILEIN> -fileout <FILEOUT>\n"
                + "    Crypto Decrypt: bee-cli.sh -type crypto -action decrypt -hexkey <HexKeyString> -filein <FILEIN> -fileout <FILEOUT>\n";
        Options options = new Options();
        options.addOption("km", true, "The Url of Key Manager, E.g. 10.1.1.2:8080");
        options.addOption("type", true, "token/crypto");
        options.addOption("action", true, "<token>: create/remove, <crypto>:encrypt/decrypt");
        options.addOption("filein", true, "Full file path for input file");
        options.addOption("fileout", true, "Full file path for output file");
        options.addOption("keyname", true, "<crypto> The key name for file encryption/decryption");
        options.addOption("hexkey", true, "<crypto> The Hex secret key string for file encryption/decryption");

        CommandLineParser parser = new PosixParser();
        CommandLine cmd = parser.parse(options, args);

        HelpFormatter formatter = new HelpFormatter();

        System.out.println();
        if (cmd.hasOption("km")) {
            this.strManagerUrl = cmd.getOptionValue("km");
        }

        if (cmd.hasOption("type")) {
            if (0 == cmd.getOptionValue("type").compareTo("token"))
                cmdType = CmdType.TOKEN;
            else if (0 == cmd.getOptionValue("type").compareTo("crypto"))
                cmdType = CmdType.CRYPTO;
        }

        if (cmd.hasOption("action")) {
            if (0 == cmd.getOptionValue("action").compareTo("create"))
                cmdAction = CmdAction.TOKEN_CREATE;
            else if (0 == cmd.getOptionValue("action").compareTo("remove"))
                cmdAction = CmdAction.TOKEN_REMOVE;
            else if (0 == cmd.getOptionValue("action").compareTo("encrypt"))
                cmdAction = CmdAction.CRYPTO_ENCRYPT;
            else if (0 == cmd.getOptionValue("action").compareTo("decrypt"))
                cmdAction = CmdAction.CRYPTO_DECRYPT;
        }

        if (cmd.hasOption("filein")) {
            this.strFileInputPath = new File(cmd.getOptionValue("filein")).getAbsolutePath();
            if (!new File(this.strFileInputPath).getParentFile().canExecute()) {
                this.errorExitOnSetup("Permission Denied to access " + this.strFileInputPath);
            }
        }

        if (cmd.hasOption("fileout")) {
            this.strFileOutputPath = new File(cmd.getOptionValue("fileout")).getAbsolutePath();
            if (!new File(this.strFileOutputPath).getParentFile().canWrite()) {
                this.errorExitOnSetup("Permission Denied to write " + this.strFileOutputPath);
            }
        }

        if (cmd.hasOption("keyname"))
            strKeyName = cmd.getOptionValue("keyname");

        if (cmd.hasOption("hexkey"))
            this.strHexKey = cmd.getOptionValue("hexkey");

        if (cmdType == CmdType.NONE || cmdAction == CmdAction.NONE) {
            System.out.println("Error! Argument type/action must be specified");
            System.out.println(strUsage);
            formatter.printHelp("Arguments Detail", options);
            System.exit(1);
        }

        if (cmdType == CmdType.TOKEN) {
            if (this.strManagerUrl.isEmpty()) {
                this.errorExitOnSetup("Please specified key manager url by argument -km");
            }

            if (cmdAction == CmdAction.NONE) {
                this.errorExitOnSetup("Type <token> need arguments -action(create/remove)");
            } else {
                if (cmdAction == CmdAction.TOKEN_CREATE) {
                    if (this.strFileOutputPath.isEmpty()) {
                        this.errorExitOnSetup("Token create: need arguments -fileout");
                    } else {
                        System.out.println("token will be created to file " + this.strFileOutputPath);
                    }
                } else {
                    if (this.strFileInputPath.isEmpty()) {
                        this.errorExitOnSetup("Token remove: need arguments -filein");
                    } else {
                        System.out.println("token read from " + this.strFileInputPath);
                    }
                }
            }
        }

        if (cmdType == CmdType.CRYPTO) {
            if (cmdAction != CmdAction.CRYPTO_ENCRYPT && cmdAction != CmdAction.CRYPTO_DECRYPT) {
                this.errorExitOnSetup("Type <crypto> only support Action encrypt/decrypt");
            } else {
                if (this.strHexKey.isEmpty()) {
                    if (this.strKeyName.isEmpty()) {
                        this.errorExitOnSetup("Argument -keyname must be specified if you don't specify "
                                + "the argument -hexkey");
                    } else {
                        if (this.strManagerUrl.isEmpty()) {
                            this.errorExitOnSetup("Please specified key manager url by argument -km");
                        } else {
                            System.out.println(
                                    "Key Manager: " + this.strManagerUrl + ", Key Name: " + this.strKeyName);
                        }
                    }
                } else {
                    System.out.println("Hex Key String: " + this.strHexKey);
                }

                if (this.strFileInputPath.isEmpty() || this.strFileOutputPath.isEmpty()) {
                    this.errorExitOnSetup("crypto: Arguments -filein and -fileout must be specified");
                }
            }
        }
        System.out.println();
    }

    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) {
        // -type token -action create -km web.bdp:8080 -fileout /tmp/bee/tt.jobtoken
        // -type token -action remove -km web.bdp:8080 -filein /tmp/bee/tt.jobtoken
        // -type crypto -action encrypt -km web.bdp:8443 -keyname test1 -filein
        // /tmp/bee/install.log -fileout /tmp/bee/install.log.intel_aes
        // -type crypto -action encrypt -hexkey
        // 4c7e62f001ae63fd7567f9c9bd5ca1e161396726bea70fe843fec0f771546da7 -filein
        // /tmp/bee/install.log -fileout /tmp/bee/install.log.intel_aes
        // -type crypto -action decrypt -km web.bdp -keyname test1 -filein
        // /tmp/bee/install.log.intel_aes -fileout /tmp/bee/install.log.decrypt
        // -type crypto -action decrypt -hexkey
        // 4c7e62f001ae63fd7567f9c9bd5ca1e161396726bea70fe843fec0f771546da7 -filein
        // /tmp/bee/install.log.intel_aes -fileout /tmp/bee/install.log.decrypt
        BeeCli beeCli = null;
        try {
            beeCli = new BeeCli();
            beeCli.setupOptions(args);
            beeCli.requestHandler();
            System.out.println();
            System.out.println("Success.");
            if (beeCli.cmdType == CmdType.TOKEN) {
                if (beeCli.cmdAction == CmdAction.TOKEN_CREATE) {
                    System.out.println("Token File is created: " + beeCli.strFileOutputPath);
                } else {
                    System.out.println("Token File is removed from server and local: " + beeCli.strFileInputPath);
                }
            }
            System.out.println();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("");
            System.out.println("");
            System.out.println(
                    "Fail to process(" + e.getMessage() + "), please refer to above exception for detail!");
            System.out.println();
        }
    }
}