blob: d2de846aff4ee9d0ebabecd881ab0f7fd6e6f602 [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>Hasher.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Apache Shiro :: Tools :: Hasher</a> &gt; <a href="index.source.html" class="el_package">org.apache.shiro.tools.hasher</a> &gt; <span class="el_source">Hasher.java</span></div><h1>Hasher.java</h1><pre class="source lang-java linenums">/*
* 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
* &quot;License&quot;); 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
* &quot;AS IS&quot; 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.shiro.tools.hasher;
import org.apache.commons.cli.*;
import org.apache.shiro.authc.credential.DefaultPasswordService;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.codec.Hex;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.UnknownAlgorithmException;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.crypto.hash.format.*;
import org.apache.shiro.io.ResourceUtils;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.util.JavaEnvironment;
import org.apache.shiro.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
/**
* Commandline line utility to hash data such as strings, passwords, resources (files, urls, etc).
* &lt;p/&gt;
* Usage:
* &lt;pre&gt;
* java -jar shiro-tools-hasher&lt;em&gt;-version&lt;/em&gt;-cli.jar
* &lt;/pre&gt;
* This will print out all supported options with documentation.
*
* @since 1.2
*/
<span class="nc bnc" id="L49" title="All 2 branches missed.">public final class Hasher {</span>
private static final String HEX_PREFIX = &quot;0x&quot;;
private static final String DEFAULT_ALGORITHM_NAME = &quot;MD5&quot;;
private static final String DEFAULT_PASSWORD_ALGORITHM_NAME = DefaultPasswordService.DEFAULT_HASH_ALGORITHM;
private static final int DEFAULT_GENERATED_SALT_SIZE = 128;
private static final int DEFAULT_NUM_ITERATIONS = 1;
private static final int DEFAULT_PASSWORD_NUM_ITERATIONS = DefaultPasswordService.DEFAULT_HASH_ITERATIONS;
<span class="nc" id="L58"> private static final Option ALGORITHM = new Option(&quot;a&quot;, &quot;algorithm&quot;, true, &quot;hash algorithm name. Defaults to SHA-256 when password hashing, MD5 otherwise.&quot;);</span>
<span class="nc" id="L59"> private static final Option DEBUG = new Option(&quot;d&quot;, &quot;debug&quot;, false, &quot;show additional error (stack trace) information.&quot;);</span>
<span class="nc" id="L60"> private static final Option FORMAT = new Option(&quot;f&quot;, &quot;format&quot;, true, &quot;hash output format. Defaults to 'shiro1' when password hashing, 'hex' otherwise. See below for more information.&quot;);</span>
<span class="nc" id="L61"> private static final Option HELP = new Option(&quot;help&quot;, &quot;help&quot;, false, &quot;show this help message.&quot;);</span>
<span class="nc" id="L62"> private static final Option ITERATIONS = new Option(&quot;i&quot;, &quot;iterations&quot;, true, &quot;number of hash iterations. Defaults to &quot; + DEFAULT_PASSWORD_NUM_ITERATIONS + &quot; when password hashing, 1 otherwise.&quot;);</span>
<span class="nc" id="L63"> private static final Option PASSWORD = new Option(&quot;p&quot;, &quot;password&quot;, false, &quot;hash a password (disable typing echo)&quot;);</span>
<span class="nc" id="L64"> private static final Option PASSWORD_NC = new Option(&quot;pnc&quot;, &quot;pnoconfirm&quot;, false, &quot;hash a password (disable typing echo) but disable password confirmation prompt.&quot;);</span>
<span class="nc" id="L65"> private static final Option RESOURCE = new Option(&quot;r&quot;, &quot;resource&quot;, false, &quot;read and hash the resource located at &lt;value&gt;. See below for more information.&quot;);</span>
<span class="nc" id="L66"> private static final Option SALT = new Option(&quot;s&quot;, &quot;salt&quot;, true, &quot;use the specified salt. &lt;arg&gt; is plaintext.&quot;);</span>
<span class="nc" id="L67"> private static final Option SALT_BYTES = new Option(&quot;sb&quot;, &quot;saltbytes&quot;, true, &quot;use the specified salt bytes. &lt;arg&gt; is hex or base64 encoded text.&quot;);</span>
<span class="nc" id="L68"> private static final Option SALT_GEN = new Option(&quot;gs&quot;, &quot;gensalt&quot;, false, &quot;generate and use a random salt. Defaults to true when password hashing, false otherwise.&quot;);</span>
<span class="nc" id="L69"> private static final Option NO_SALT_GEN = new Option(&quot;ngs&quot;, &quot;nogensalt&quot;, false, &quot;do NOT generate and use a random salt (valid during password hashing).&quot;);</span>
<span class="nc" id="L70"> private static final Option SALT_GEN_SIZE = new Option(&quot;gss&quot;, &quot;gensaltsize&quot;, true, &quot;the number of salt bits (not bytes!) to generate. Defaults to 128.&quot;);</span>
<span class="nc" id="L72"> private static final String SALT_MUTEX_MSG = createMutexMessage(SALT, SALT_BYTES);</span>
<span class="nc" id="L74"> private static final HashFormatFactory HASH_FORMAT_FACTORY = new DefaultHashFormatFactory();</span>
static {
<span class="nc" id="L77"> ALGORITHM.setArgName(&quot;name&quot;);</span>
<span class="nc" id="L78"> SALT_GEN_SIZE.setArgName(&quot;numBits&quot;);</span>
<span class="nc" id="L79"> ITERATIONS.setArgName(&quot;num&quot;);</span>
<span class="nc" id="L80"> SALT.setArgName(&quot;sval&quot;);</span>
<span class="nc" id="L81"> SALT_BYTES.setArgName(&quot;encTxt&quot;);</span>
<span class="nc" id="L82"> }</span>
public static void main(String[] args) {
<span class="nc" id="L86"> CommandLineParser parser = new PosixParser();</span>
<span class="nc" id="L88"> Options options = new Options();</span>
<span class="nc" id="L89"> options.addOption(HELP).addOption(DEBUG).addOption(ALGORITHM).addOption(ITERATIONS);</span>
<span class="nc" id="L90"> options.addOption(RESOURCE).addOption(PASSWORD).addOption(PASSWORD_NC);</span>
<span class="nc" id="L91"> options.addOption(SALT).addOption(SALT_BYTES).addOption(SALT_GEN).addOption(SALT_GEN_SIZE).addOption(NO_SALT_GEN);</span>
<span class="nc" id="L92"> options.addOption(FORMAT);</span>
<span class="nc" id="L94"> boolean debug = false;</span>
<span class="nc" id="L95"> String algorithm = null; //user unspecified</span>
<span class="nc" id="L96"> int iterations = 0; //0 means unspecified by the end-user</span>
<span class="nc" id="L97"> boolean resource = false;</span>
<span class="nc" id="L98"> boolean password = false;</span>
<span class="nc" id="L99"> boolean passwordConfirm = true;</span>
<span class="nc" id="L100"> String saltString = null;</span>
<span class="nc" id="L101"> String saltBytesString = null;</span>
<span class="nc" id="L102"> boolean generateSalt = false;</span>
<span class="nc" id="L103"> int generatedSaltSize = DEFAULT_GENERATED_SALT_SIZE;</span>
<span class="nc" id="L105"> String formatString = null;</span>
<span class="nc" id="L107"> char[] passwordChars = null;</span>
try {
<span class="nc" id="L110"> CommandLine line = parser.parse(options, args);</span>
<span class="nc bnc" id="L112" title="All 2 branches missed."> if (line.hasOption(HELP.getOpt())) {</span>
<span class="nc" id="L113"> printHelpAndExit(options, null, debug, 0);</span>
}
<span class="nc bnc" id="L115" title="All 2 branches missed."> if (line.hasOption(DEBUG.getOpt())) {</span>
<span class="nc" id="L116"> debug = true;</span>
}
<span class="nc bnc" id="L118" title="All 2 branches missed."> if (line.hasOption(ALGORITHM.getOpt())) {</span>
<span class="nc" id="L119"> algorithm = line.getOptionValue(ALGORITHM.getOpt());</span>
}
<span class="nc bnc" id="L121" title="All 2 branches missed."> if (line.hasOption(ITERATIONS.getOpt())) {</span>
<span class="nc" id="L122"> iterations = getRequiredPositiveInt(line, ITERATIONS);</span>
}
<span class="nc bnc" id="L124" title="All 2 branches missed."> if (line.hasOption(PASSWORD.getOpt())) {</span>
<span class="nc" id="L125"> password = true;</span>
<span class="nc" id="L126"> generateSalt = true;</span>
}
<span class="nc bnc" id="L128" title="All 2 branches missed."> if (line.hasOption(RESOURCE.getOpt())) {</span>
<span class="nc" id="L129"> resource = true;</span>
}
<span class="nc bnc" id="L131" title="All 2 branches missed."> if (line.hasOption(PASSWORD_NC.getOpt())) {</span>
<span class="nc" id="L132"> password = true;</span>
<span class="nc" id="L133"> generateSalt = true;</span>
<span class="nc" id="L134"> passwordConfirm = false;</span>
}
<span class="nc bnc" id="L136" title="All 2 branches missed."> if (line.hasOption(SALT.getOpt())) {</span>
<span class="nc" id="L137"> saltString = line.getOptionValue(SALT.getOpt());</span>
}
<span class="nc bnc" id="L139" title="All 2 branches missed."> if (line.hasOption(SALT_BYTES.getOpt())) {</span>
<span class="nc" id="L140"> saltBytesString = line.getOptionValue(SALT_BYTES.getOpt());</span>
}
<span class="nc bnc" id="L142" title="All 2 branches missed."> if (line.hasOption(NO_SALT_GEN.getOpt())) {</span>
<span class="nc" id="L143"> generateSalt = false;</span>
}
<span class="nc bnc" id="L145" title="All 2 branches missed."> if (line.hasOption(SALT_GEN.getOpt())) {</span>
<span class="nc" id="L146"> generateSalt = true;</span>
}
<span class="nc bnc" id="L148" title="All 2 branches missed."> if (line.hasOption(SALT_GEN_SIZE.getOpt())) {</span>
<span class="nc" id="L149"> generateSalt = true;</span>
<span class="nc" id="L150"> generatedSaltSize = getRequiredPositiveInt(line, SALT_GEN_SIZE);</span>
<span class="nc bnc" id="L151" title="All 2 branches missed."> if (generatedSaltSize % 8 != 0) {</span>
<span class="nc" id="L152"> throw new IllegalArgumentException(&quot;Generated salt size must be a multiple of 8 (e.g. 128, 192, 256, 512, etc).&quot;);</span>
}
}
<span class="nc bnc" id="L155" title="All 2 branches missed."> if (line.hasOption(FORMAT.getOpt())) {</span>
<span class="nc" id="L156"> formatString = line.getOptionValue(FORMAT.getOpt());</span>
}
String sourceValue;
Object source;
<span class="nc bnc" id="L163" title="All 2 branches missed."> if (password) {</span>
<span class="nc" id="L164"> passwordChars = readPassword(passwordConfirm);</span>
<span class="nc" id="L165"> source = passwordChars;</span>
} else {
<span class="nc" id="L167"> String[] remainingArgs = line.getArgs();</span>
<span class="nc bnc" id="L168" title="All 4 branches missed."> if (remainingArgs == null || remainingArgs.length != 1) {</span>
<span class="nc" id="L169"> printHelpAndExit(options, null, debug, -1);</span>
}
<span class="nc bnc" id="L172" title="All 4 branches missed."> assert remainingArgs != null;</span>
<span class="nc" id="L173"> sourceValue = toString(remainingArgs);</span>
<span class="nc bnc" id="L175" title="All 2 branches missed."> if (resource) {</span>
<span class="nc bnc" id="L176" title="All 2 branches missed."> if (!ResourceUtils.hasResourcePrefix(sourceValue)) {</span>
<span class="nc" id="L177"> source = toFile(sourceValue);</span>
} else {
<span class="nc" id="L179"> source = ResourceUtils.getInputStreamForPath(sourceValue);</span>
}
} else {
<span class="nc" id="L182"> source = sourceValue;</span>
}
}
<span class="nc bnc" id="L186" title="All 2 branches missed."> if (algorithm == null) {</span>
<span class="nc bnc" id="L187" title="All 2 branches missed."> if (password) {</span>
<span class="nc" id="L188"> algorithm = DEFAULT_PASSWORD_ALGORITHM_NAME;</span>
} else {
<span class="nc" id="L190"> algorithm = DEFAULT_ALGORITHM_NAME;</span>
}
}
<span class="nc bnc" id="L194" title="All 2 branches missed."> if (iterations &lt; DEFAULT_NUM_ITERATIONS) {</span>
//Iterations were not specified. Default to 350,000 when password hashing, and 1 for everything else:
<span class="nc bnc" id="L196" title="All 2 branches missed."> if (password) {</span>
<span class="nc" id="L197"> iterations = DEFAULT_PASSWORD_NUM_ITERATIONS;</span>
} else {
<span class="nc" id="L199"> iterations = DEFAULT_NUM_ITERATIONS;</span>
}
}
<span class="nc" id="L203"> ByteSource salt = getSalt(saltString, saltBytesString, generateSalt, generatedSaltSize);</span>
<span class="nc" id="L205"> SimpleHash hash = new SimpleHash(algorithm, source, salt, iterations);</span>
<span class="nc bnc" id="L207" title="All 2 branches missed."> if (formatString == null) {</span>
//Output format was not specified. Default to 'shiro1' when password hashing, and 'hex' for
//everything else:
<span class="nc bnc" id="L210" title="All 2 branches missed."> if (password) {</span>
<span class="nc" id="L211"> formatString = Shiro1CryptFormat.class.getName();</span>
} else {
<span class="nc" id="L213"> formatString = HexFormat.class.getName();</span>
}
}
<span class="nc" id="L217"> HashFormat format = HASH_FORMAT_FACTORY.getInstance(formatString);</span>
<span class="nc bnc" id="L219" title="All 2 branches missed."> if (format == null) {</span>
<span class="nc" id="L220"> throw new IllegalArgumentException(&quot;Unrecognized hash format '&quot; + formatString + &quot;'.&quot;);</span>
}
<span class="nc" id="L223"> String output = format.format(hash);</span>
<span class="nc" id="L225"> System.out.println(output);</span>
<span class="nc" id="L227"> } catch (IllegalArgumentException iae) {</span>
<span class="nc" id="L228"> exit(iae, debug);</span>
<span class="nc" id="L229"> } catch (UnknownAlgorithmException uae) {</span>
<span class="nc" id="L230"> exit(uae, debug);</span>
<span class="nc" id="L231"> } catch (IOException ioe) {</span>
<span class="nc" id="L232"> exit(ioe, debug);</span>
<span class="nc" id="L233"> } catch (Exception e) {</span>
<span class="nc" id="L234"> printHelpAndExit(options, e, debug, -1);</span>
} finally {
<span class="nc bnc" id="L236" title="All 24 branches missed."> if (passwordChars != null &amp;&amp; passwordChars.length &gt; 0) {</span>
<span class="nc bnc" id="L237" title="All 12 branches missed."> for (int i = 0; i &lt; passwordChars.length; i++) {</span>
<span class="nc" id="L238"> passwordChars[i] = ' ';</span>
}
}
}
<span class="nc" id="L242"> }</span>
private static String createMutexMessage(Option... options) {
<span class="nc" id="L245"> StringBuilder sb = new StringBuilder();</span>
<span class="nc" id="L246"> sb.append(&quot;The &quot;);</span>
<span class="nc bnc" id="L248" title="All 2 branches missed."> for (int i = 0; i &lt; options.length; i++) {</span>
<span class="nc bnc" id="L249" title="All 2 branches missed."> if (i &gt; 0) {</span>
<span class="nc" id="L250"> sb.append(&quot;, &quot;);</span>
}
<span class="nc" id="L252"> Option o = options[0];</span>
<span class="nc" id="L253"> sb.append(&quot;-&quot;).append(o.getOpt()).append(&quot;/--&quot;).append(o.getLongOpt());</span>
}
<span class="nc" id="L255"> sb.append(&quot; and generated salt options are mutually exclusive. Only one of them may be used at a time&quot;);</span>
<span class="nc" id="L256"> return sb.toString();</span>
}
private static void exit(Exception e, boolean debug) {
<span class="nc" id="L260"> printException(e, debug);</span>
<span class="nc" id="L261"> System.exit(-1);</span>
<span class="nc" id="L262"> }</span>
private static int getRequiredPositiveInt(CommandLine line, Option option) {
<span class="nc" id="L265"> String iterVal = line.getOptionValue(option.getOpt());</span>
try {
<span class="nc" id="L267"> return Integer.parseInt(iterVal);</span>
<span class="nc" id="L268"> } catch (NumberFormatException e) {</span>
<span class="nc" id="L269"> String msg = &quot;'&quot; + option.getLongOpt() + &quot;' value must be a positive integer.&quot;;</span>
<span class="nc" id="L270"> throw new IllegalArgumentException(msg, e);</span>
}
}
private static ByteSource getSalt(String saltString, String saltBytesString, boolean generateSalt, int generatedSaltSize) {
<span class="nc bnc" id="L276" title="All 2 branches missed."> if (saltString != null) {</span>
<span class="nc bnc" id="L277" title="All 4 branches missed."> if (generateSalt || (saltBytesString != null)) {</span>
<span class="nc" id="L278"> throw new IllegalArgumentException(SALT_MUTEX_MSG);</span>
}
<span class="nc" id="L280"> return ByteSource.Util.bytes(saltString);</span>
}
<span class="nc bnc" id="L283" title="All 2 branches missed."> if (saltBytesString != null) {</span>
<span class="nc bnc" id="L284" title="All 2 branches missed."> if (generateSalt) {</span>
<span class="nc" id="L285"> throw new IllegalArgumentException(SALT_MUTEX_MSG);</span>
}
<span class="nc" id="L288"> String value = saltBytesString;</span>
<span class="nc" id="L289"> boolean base64 = true;</span>
<span class="nc bnc" id="L290" title="All 2 branches missed."> if (saltBytesString.startsWith(HEX_PREFIX)) {</span>
//hex:
<span class="nc" id="L292"> base64 = false;</span>
<span class="nc" id="L293"> value = value.substring(HEX_PREFIX.length());</span>
}
byte[] bytes;
<span class="nc bnc" id="L296" title="All 2 branches missed."> if (base64) {</span>
<span class="nc" id="L297"> bytes = Base64.decode(value);</span>
} else {
<span class="nc" id="L299"> bytes = Hex.decode(value);</span>
}
<span class="nc" id="L301"> return ByteSource.Util.bytes(bytes);</span>
}
<span class="nc bnc" id="L304" title="All 2 branches missed."> if (generateSalt) {</span>
<span class="nc" id="L305"> SecureRandomNumberGenerator generator = new SecureRandomNumberGenerator();</span>
<span class="nc" id="L306"> int byteSize = generatedSaltSize / 8; //generatedSaltSize is in *bits* - convert to byte size:</span>
<span class="nc" id="L307"> return generator.nextBytes(byteSize);</span>
}
//no salt used:
<span class="nc" id="L311"> return null;</span>
}
private static void printException(Exception e, boolean debug) {
<span class="nc bnc" id="L315" title="All 2 branches missed."> if (e != null) {</span>
<span class="nc" id="L316"> System.out.println();</span>
<span class="nc bnc" id="L317" title="All 2 branches missed."> if (debug) {</span>
<span class="nc" id="L318"> System.out.println(&quot;Error: &quot;);</span>
<span class="nc" id="L319"> e.printStackTrace(System.out);</span>
<span class="nc" id="L320"> System.out.println(e.getMessage());</span>
} else {
<span class="nc" id="L323"> System.out.println(&quot;Error: &quot; + e.getMessage());</span>
<span class="nc" id="L324"> System.out.println();</span>
<span class="nc" id="L325"> System.out.println(&quot;Specify -d or --debug for more information.&quot;);</span>
}
}
<span class="nc" id="L328"> }</span>
private static void printHelp(Options options, Exception e, boolean debug) {
<span class="nc" id="L331"> HelpFormatter help = new HelpFormatter();</span>
<span class="nc" id="L332"> String command = &quot;java -jar shiro-tools-hasher-&lt;version&gt;.jar [options] [&lt;value&gt;]&quot;;</span>
<span class="nc" id="L333"> String header = &quot;\nPrint a cryptographic hash (aka message digest) of the specified &lt;value&gt;.\n--\nOptions:&quot;;</span>
<span class="nc" id="L334"> String footer = &quot;\n&quot; +</span>
&quot;&lt;value&gt; is optional only when hashing passwords (see below). It is\n&quot; +
&quot;required all other times.&quot; +
&quot;\n\n&quot; +
&quot;Password Hashing:\n&quot; +
&quot;---------------------------------\n&quot; +
&quot;Specify the -p/--password option and DO NOT enter a &lt;value&gt;. You will\n&quot; +
&quot;be prompted for a password and characters will not echo as you type.&quot; +
&quot;\n\n&quot; +
&quot;Salting:\n&quot; +
&quot;---------------------------------\n&quot; +
&quot;Specifying a salt:&quot; +
&quot;\n\n&quot; +
&quot;You may specify a salt using the -s/--salt option followed by the salt\n&quot; +
&quot;value. If the salt value is a base64 or hex string representing a\n&quot; +
&quot;byte array, you must specify the -sb/--saltbytes option to indicate this,\n&quot; +
&quot;otherwise the text value bytes will be used directly.&quot; +
&quot;\n\n&quot; +
&quot;When using -sb/--saltbytes, the -s/--salt value is expected to be a\n&quot; +
&quot;base64-encoded string by default. If the value is a hex-encoded string,\n&quot; +
&quot;you must prefix the string with 0x (zero x) to indicate a hex value.&quot; +
&quot;\n\n&quot; +
&quot;Generating a salt:&quot; +
&quot;\n\n&quot; +
&quot;Use the -sg/--saltgenerated option if you don't want to specify a salt,\n&quot; +
&quot;but want a strong random salt to be generated and used during hashing.\n&quot; +
&quot;The generated salt size defaults to 128 bits. You may specify\n&quot; +
&quot;a different size by using the -sgs/--saltgeneratedsize option followed by\n&quot; +
&quot;a positive integer (size is in bits, not bytes).&quot; +
&quot;\n\n&quot; +
&quot;Because a salt must be specified if computing the\n&quot; +
&quot;hash later, generated salts will be printed, defaulting to base64\n&quot; +
&quot;encoding. If you prefer to use hex encoding, additionally use the\n&quot; +
&quot;-sgh/--saltgeneratedhex option.&quot; +
&quot;\n\n&quot; +
&quot;Files, URLs and classpath resources:\n&quot; +
&quot;---------------------------------\n&quot; +
&quot;If using the -r/--resource option, the &lt;value&gt; represents a resource path.\n&quot; +
&quot;By default this is expected to be a file path, but you may specify\n&quot; +
&quot;classpath or URL resources by using the classpath: or url: prefix\n&quot; +
&quot;respectively.&quot; +
&quot;\n\n&quot; +
&quot;Some examples:&quot; +
&quot;\n\n&quot; +
&quot;&lt;command&gt; -r fileInCurrentDirectory.txt\n&quot; +
&quot;&lt;command&gt; -r ../../relativePathFile.xml\n&quot; +
&quot;&lt;command&gt; -r ~/documents/myfile.pdf\n&quot; +
&quot;&lt;command&gt; -r /usr/local/logs/absolutePathFile.log\n&quot; +
&quot;&lt;command&gt; -r url:http://foo.com/page.html\n&quot; +
&quot;&lt;command&gt; -r classpath:/WEB-INF/lib/something.jar&quot; +
&quot;\n\n&quot; +
&quot;Output Format:\n&quot; +
&quot;---------------------------------\n&quot; +
&quot;Specify the -f/--format option followed by either 1) the format ID (as defined\n&quot; +
<span class="nc" id="L388"> &quot;by the &quot; + DefaultHashFormatFactory.class.getName() + &quot;\n&quot; +</span>
<span class="nc" id="L389"> &quot;JavaDoc) or 2) the fully qualified &quot; + HashFormat.class.getName() + &quot;\n&quot; +</span>
&quot;implementation class name to instantiate and use for formatting.\n\n&quot; +
&quot;The default output format is 'shiro1' which is a Modular Crypt Format (MCF)\n&quot; +
&quot;that shows all relevant information as a dollar-sign ($) delimited string.\n&quot; +
&quot;This format is ideal for use in Shiro's text-based user configuration (e.g.\n&quot; +
&quot;shiro.ini or a properties file).&quot;;
<span class="nc" id="L396"> printException(e, debug);</span>
<span class="nc" id="L398"> System.out.println();</span>
<span class="nc" id="L399"> help.printHelp(command, header, options, null);</span>
<span class="nc" id="L400"> System.out.println(footer);</span>
<span class="nc" id="L401"> }</span>
private static void printHelpAndExit(Options options, Exception e, boolean debug, int exitCode) {
<span class="nc" id="L404"> printHelp(options, e, debug);</span>
<span class="nc" id="L405"> System.exit(exitCode);</span>
<span class="nc" id="L406"> }</span>
private static char[] readPassword(boolean confirm) {
<span class="nc bnc" id="L409" title="All 2 branches missed."> if (!JavaEnvironment.isAtLeastVersion16()) {</span>
<span class="nc" id="L410"> String msg = &quot;Password hashing (prompt without echo) uses the java.io.Console to read passwords &quot; +</span>
&quot;safely. This is only available on Java 1.6 platforms and later.&quot;;
<span class="nc" id="L412"> throw new IllegalArgumentException(msg);</span>
}
<span class="nc" id="L414"> java.io.Console console = System.console();</span>
<span class="nc bnc" id="L415" title="All 2 branches missed."> if (console == null) {</span>
<span class="nc" id="L416"> throw new IllegalStateException(&quot;java.io.Console is not available on the current JVM. Cannot read passwords.&quot;);</span>
}
<span class="nc" id="L418"> char[] first = console.readPassword(&quot;%s&quot;, &quot;Password to hash: &quot;);</span>
<span class="nc bnc" id="L419" title="All 4 branches missed."> if (first == null || first.length == 0) {</span>
<span class="nc" id="L420"> throw new IllegalArgumentException(&quot;No password specified.&quot;);</span>
}
<span class="nc bnc" id="L422" title="All 2 branches missed."> if (confirm) {</span>
<span class="nc" id="L423"> char[] second = console.readPassword(&quot;%s&quot;, &quot;Password to hash (confirm): &quot;);</span>
<span class="nc bnc" id="L424" title="All 2 branches missed."> if (!Arrays.equals(first, second)) {</span>
<span class="nc" id="L425"> String msg = &quot;Password entries do not match.&quot;;</span>
<span class="nc" id="L426"> throw new IllegalArgumentException(msg);</span>
}
}
<span class="nc" id="L429"> return first;</span>
}
private static File toFile(String path) {
<span class="nc" id="L433"> String resolved = path;</span>
<span class="nc bnc" id="L434" title="All 4 branches missed."> if (path.startsWith(&quot;~/&quot;) || path.startsWith((&quot;~\\&quot;))) {</span>
<span class="nc" id="L435"> resolved = path.replaceFirst(&quot;\\~&quot;, System.getProperty(&quot;user.home&quot;));</span>
}
<span class="nc" id="L437"> return new File(resolved);</span>
}
private static String toString(String[] strings) {
<span class="nc bnc" id="L441" title="All 2 branches missed."> int len = strings != null ? strings.length : 0;</span>
<span class="nc bnc" id="L442" title="All 2 branches missed."> if (len == 0) {</span>
<span class="nc" id="L443"> return null;</span>
}
<span class="nc" id="L445"> return StringUtils.toDelimitedString(strings, &quot; &quot;);</span>
}
}
</pre><div class="footer"><span class="right">Created with <a href="http://www.eclemma.org/jacoco">JaCoCo</a> 0.7.7.201606060606</span></div></body></html>