LoginSignup
2
0

More than 5 years have passed since last update.

Javaアプリから外部コマンドを実行した際に、ファイルパスが認識されない

Last updated at Posted at 2017-10-23

JavaでXML署名を実装した際にドン詰まりしたのでメモ。

Javaアプリからkeytool -importkeystoreコマンドを実行

でもインポートされず。。。
実行時に関係するメソッドは以下の3つで、importProcessExec ⇒ getImportCommand ⇒ processExecの順で実行される。

sample.java
    /**
     * 証明書インポートコマンドを実行する.
     *
     * @param certFilePath 証明書ファイル
     * @param destAlias 識別名
     * @param keyStoreFilePath キーストアファイル
     * @param certificatePass 証明書パスワード
     * @param keyStorePass キーストアパスワード
     * @return true: 正常終了, false: 異常終了
     * @throws InterruptedException
     */
    private boolean importProcessExec(File keyStoreFile, String destAlias, File certFile,
            String certificatePass,
            String keyStorePass) throws InterruptedException {
        // 証明書のインポートを行うコマンド
        List<String> command = getImportCommand(keyStoreFile, destAlias, certFile, certificatePass,
                keyStorePass);
        // インポートコマンド実行
        return processExec(command);
    }

    /**
     * 証明書インポートコマンドを取得する.
     *
     * @param certFilePath 証明書ファイル
     * @param destAlias 識別名
     * @param keyStoreFilePath キーストアファイル
     * @param certificatePass 証明書パスワード
     * @param keyStorePass キーストアパスワード
     * @return resultList インポートコマンド
     */
    private List<String> getImportCommand(File keyStoreFile, String destAlias, File certFile,
            String certificatePass, String keyStorePass) {
        // 証明書のインポートを行うコマンド
        String command = "keytool -importkeystore -keystore %keyStoreFilePath% -srckeystore %certFilePath% -srcstoretype PKCS12 -srcalias 1 -destalias %destalias% -srcstorepass %certificatePass% -deststorepass %keyStorePass%";

        // コマンドを文字列配列に分割
        String[] commandList = command.split(" ");

        List<String> resultList = new ArrayList<String>();

        for (String cmd : commandList) {
            switch (cmd) {
            case "%keyStoreFilePath%":
                cmd = cmd.replace("%keyStoreFilePath%", includeDoubleQuotes(keyStoreFile.getPath()));
                break;
            case "%certFilePath%":
                cmd = cmd.replace("%certFilePath%", includeDoubleQuotes(certFile.getPath()));
                break;
            case "%destalias%":
                cmd = cmd.replace("%destalias%", destAlias);
                break;
            case "%certificatePass%":
                cmd = cmd.replace("%certificatePass%", certificatePass);
                break;
            case "%keyStorePass%":
                cmd = cmd.replace("%keyStorePass%", keyStorePass);
                break;
            }
            resultList.add(cmd);
        }

        return resultList;
    }

    /**
     * 外部プロセスを実行する.
     *
     * @param command コマンド内容
     * @return true: 正常終了, false: 異常終了
     */
    private boolean processExec(List<String> command) {
        // 処理結果
        boolean result = false;

        try {
            ProcessBuilder processBuilder = new ProcessBuilder(command);
            Process Process = processBuilder.start();

            // プロセスの正常終了まで待機させる
            if (Process.waitFor() == 0) {
                result = true;
                log.info("Process Success: " + command.toString());
            } else {
                log.warn("Process Failed: " + command.toString());
            }

            // 標準出力
            String strInput;
            BufferedReader ipbr = new BufferedReader(new InputStreamReader(Process.getInputStream()));
            while((strInput = ipbr.readLine()) != null) {
                log.info(strInput);
            }
            ipbr.close();

            // エラー出力
            String strErr;
            BufferedReader erbr = new BufferedReader(new InputStreamReader(Process.getErrorStream()));
            while((strErr = erbr.readLine()) != null) {
                log.info(strErr);
            }
            erbr.close();

            //ProcessBuilderを使用後、バックグラウンドでInputStream, OutputStream, ErrorStreamがオープンされる.
            // リソース不足を回避するため全てのストリームをクローズする.
            Process.getInputStream().close();
            Process.getOutputStream().close();
            Process.getErrorStream().close();
        } catch (InterruptedException | IOException e) {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
        }

        return result;
    }

    /**
     * 文字列をダブルクォーテーションで括る.
     *
     * @param str 文字列
     * @return ダブルクォーテーションで括られた文字列
     */
    private String includeDoubleQuotes(String str) {
        return "\"" + str + "\""; 
    }

【原因】証明書ファイルパスを「"」で括っていたため

メソッド:includeDoubleQuotesで、証明書ファイルパスを「"」で括っており、Javaアプリから実行されるコマンド

keytool -importkeystore -keystore "/.../XXXX.keystore" -srckeystore "/.../XXXX.p12" -srcstoretype PKCS12 -srcstorepass root -deststorepass changeit

が正常に実行されていなかったようです。
「"」を付与する関数を除外することで正しく動作しました。

開発環境がWindowsで、証明書の配置場所をProgram Files配下にしていました。
ファイルパスに半角スペースが含まれており、証明書のファイルパスを「"」で括らないと正常にコマンド実行されなかったので、関数をかませて「"」をつけるようにしていたのです。

こういう外部プロセス実行系の処理は要注意ですね。
たとえば、windowsにlinuxの仮想環境を作って、そこにデプロイして動作確認とかしないと危ないかも。。。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0