Author: lhazlewood
Date: Mon Mar 28 02:55:23 2011
New Revision: 1086106

URL: http://svn.apache.org/viewvc?rev=1086106&view=rev
Log:
SHIRO-279: Implemented command-line/console hashing program.  It will hash 
strings, resources (files, urls, classpath entries), and passwords.  It allows 
specification of the hash algorithm and number of hash iterations, defaulting 
to MD5 and 1 respectively.  Salting is not performed at the moment.

Added:
    
shiro/trunk/core/src/main/java/org/apache/shiro/crypto/UnknownAlgorithmException.java
    shiro/trunk/tools/   (with props)
    shiro/trunk/tools/hasher/   (with props)
    shiro/trunk/tools/hasher/pom.xml
    shiro/trunk/tools/hasher/src/
    shiro/trunk/tools/hasher/src/main/
    shiro/trunk/tools/hasher/src/main/java/
    shiro/trunk/tools/hasher/src/main/java/org/
    shiro/trunk/tools/hasher/src/main/java/org/apache/
    shiro/trunk/tools/hasher/src/main/java/org/apache/shiro/
    shiro/trunk/tools/hasher/src/main/java/org/apache/shiro/tools/
    shiro/trunk/tools/hasher/src/main/java/org/apache/shiro/tools/hasher/
    
shiro/trunk/tools/hasher/src/main/java/org/apache/shiro/tools/hasher/Hasher.java
    shiro/trunk/tools/pom.xml
Modified:
    
shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/AbstractHash.java
    shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/SimpleHash.java
    shiro/trunk/core/src/main/java/org/apache/shiro/util/JavaEnvironment.java
    shiro/trunk/pom.xml

Added: 
shiro/trunk/core/src/main/java/org/apache/shiro/crypto/UnknownAlgorithmException.java
URL: 
http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/crypto/UnknownAlgorithmException.java?rev=1086106&view=auto
==============================================================================
--- 
shiro/trunk/core/src/main/java/org/apache/shiro/crypto/UnknownAlgorithmException.java
 (added)
+++ 
shiro/trunk/core/src/main/java/org/apache/shiro/crypto/UnknownAlgorithmException.java
 Mon Mar 28 02:55:23 2011
@@ -0,0 +1,40 @@
+/*
+ * 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.shiro.crypto;
+
+/**
+ * Exception thrown when attempting to lookup or use a cryptographic algorithm 
that does not exist in the current
+ * JVM environment.
+ *
+ * @since 1.2
+ */
+public class UnknownAlgorithmException extends CryptoException {
+
+    public UnknownAlgorithmException(String message) {
+        super(message);
+    }
+
+    public UnknownAlgorithmException(Throwable cause) {
+        super(cause);
+    }
+
+    public UnknownAlgorithmException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

Modified: 
shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/AbstractHash.java
URL: 
http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/AbstractHash.java?rev=1086106&r1=1086105&r2=1086106&view=diff
==============================================================================
--- 
shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/AbstractHash.java 
(original)
+++ 
shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/AbstractHash.java 
Mon Mar 28 02:55:23 2011
@@ -22,6 +22,7 @@ import org.apache.shiro.codec.Base64;
 import org.apache.shiro.codec.CodecException;
 import org.apache.shiro.codec.CodecSupport;
 import org.apache.shiro.codec.Hex;
+import org.apache.shiro.crypto.UnknownAlgorithmException;
 
 import java.io.Serializable;
 import java.security.MessageDigest;
@@ -166,13 +167,14 @@ public abstract class AbstractHash exten
      *
      * @param algorithmName the algorithm to use for the hash, provided by 
subclasses.
      * @return the MessageDigest object for the specified {@code algorithm}.
+     * @throws UnknownAlgorithmException if the specified algorithm name is 
not available.
      */
-    protected MessageDigest getDigest(String algorithmName) {
+    protected MessageDigest getDigest(String algorithmName) throws 
UnknownAlgorithmException {
         try {
             return MessageDigest.getInstance(algorithmName);
         } catch (NoSuchAlgorithmException e) {
             String msg = "No native '" + algorithmName + "' MessageDigest 
instance available on the current JVM.";
-            throw new IllegalStateException(msg, e);
+            throw new UnknownAlgorithmException(msg, e);
         }
     }
 
@@ -204,8 +206,9 @@ public abstract class AbstractHash exten
      * @param salt           the salt to use for the initial hash
      * @param hashIterations the number of times the the {@code bytes} will be 
hashed (for attack resiliency).
      * @return the hashed bytes.
+     * @throws UnknownAlgorithmException if the {@link #getAlgorithmName() 
algorithmName} is not available.
      */
-    protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) {
+    protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) 
throws UnknownAlgorithmException {
         MessageDigest digest = getDigest(getAlgorithmName());
         if (salt != null) {
             digest.reset();

Modified: 
shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/SimpleHash.java
URL: 
http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/SimpleHash.java?rev=1086106&r1=1086105&r2=1086106&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/SimpleHash.java 
(original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/SimpleHash.java 
Mon Mar 28 02:55:23 2011
@@ -21,6 +21,7 @@ package org.apache.shiro.crypto.hash;
 import org.apache.shiro.codec.Base64;
 import org.apache.shiro.codec.CodecException;
 import org.apache.shiro.codec.Hex;
+import org.apache.shiro.crypto.UnknownAlgorithmException;
 import org.apache.shiro.util.StringUtils;
 
 import java.security.MessageDigest;
@@ -67,9 +68,13 @@ public class SimpleHash extends Abstract
      * constructor is useful in scenarios when you have a byte array that you 
know is already hashed and
      * just want to set the bytes in their raw form directly on an instance.  
After using this constructor,
      * you can then immediately call {@link #setBytes setBytes} to have a 
fully-initialized instance.
+     * <p/>
+     * <b>N.B.</b>The algorithm identified by the {@code algorithmName} 
parameter must be available on the JVM.  If it
+     * is not, a {@link UnknownAlgorithmException} will be thrown when the 
hash is performed (not at instantiation).
      *
      * @param algorithmName the {@link java.security.MessageDigest 
MessageDigest} algorithm name to use when
      *                      performing the hash.
+     * @see UnknownAlgorithmException
      */
     public SimpleHash(String algorithmName) {
         this.algorithmName = algorithmName;
@@ -90,9 +95,10 @@ public class SimpleHash extends Abstract
      *                      performing the hash.
      * @param source        the object to be hashed.
      * @throws org.apache.shiro.codec.CodecException
-     *          if the specified {@code source} cannot be converted into a 
byte array (byte[]).
+     *                                   if the specified {@code source} 
cannot be converted into a byte array (byte[]).
+     * @throws UnknownAlgorithmException if the {@code algorithmName} is not 
available.
      */
-    public SimpleHash(String algorithmName, Object source) throws 
CodecException {
+    public SimpleHash(String algorithmName, Object source) throws 
CodecException, UnknownAlgorithmException {
         this(algorithmName, source, null, 1);
     }
 
@@ -111,9 +117,10 @@ public class SimpleHash extends Abstract
      *                      performing the hash.
      * @param source        the source object to be hashed.
      * @param salt          the salt to use for the hash
-     * @throws CodecException if either constructor argument cannot be 
converted into a byte array.
+     * @throws CodecException            if either constructor argument cannot 
be converted into a byte array.
+     * @throws UnknownAlgorithmException if the {@code algorithmName} is not 
available.
      */
-    public SimpleHash(String algorithmName, Object source, Object salt) throws 
CodecException {
+    public SimpleHash(String algorithmName, Object source, Object salt) throws 
CodecException, UnknownAlgorithmException {
         this(algorithmName, source, salt, 1);
     }
 
@@ -137,9 +144,11 @@ public class SimpleHash extends Abstract
      * @param source         the source object to be hashed.
      * @param salt           the salt to use for the hash
      * @param hashIterations the number of times the {@code source} argument 
hashed for attack resiliency.
-     * @throws CodecException if either Object constructor argument cannot be 
converted into a byte array.
+     * @throws CodecException            if either Object constructor argument 
cannot be converted into a byte array.
+     * @throws UnknownAlgorithmException if the {@code algorithmName} is not 
available.
      */
-    public SimpleHash(String algorithmName, Object source, Object salt, int 
hashIterations) throws CodecException {
+    public SimpleHash(String algorithmName, Object source, Object salt, int 
hashIterations)
+            throws CodecException, UnknownAlgorithmException {
         if (!StringUtils.hasText(algorithmName)) {
             throw new NullPointerException("algorithmName argument cannot be 
null or empty.");
         }
@@ -147,7 +156,7 @@ public class SimpleHash extends Abstract
         hash(source, salt, hashIterations);
     }
 
-    private void hash(Object source, Object salt, int hashIterations) throws 
CodecException {
+    private void hash(Object source, Object salt, int hashIterations) throws 
CodecException, UnknownAlgorithmException {
         byte[] sourceBytes = toBytes(source);
         byte[] saltBytes = null;
         if (salt != null) {
@@ -189,13 +198,14 @@ public class SimpleHash extends Abstract
      *
      * @param algorithmName the algorithm to use for the hash, provided by 
subclasses.
      * @return the MessageDigest object for the specified {@code algorithm}.
+     * @throws UnknownAlgorithmException if the specified algorithm name is 
not available.
      */
-    protected MessageDigest getDigest(String algorithmName) {
+    protected MessageDigest getDigest(String algorithmName) throws 
UnknownAlgorithmException {
         try {
             return MessageDigest.getInstance(algorithmName);
         } catch (NoSuchAlgorithmException e) {
             String msg = "No native '" + algorithmName + "' MessageDigest 
instance available on the current JVM.";
-            throw new IllegalStateException(msg, e);
+            throw new UnknownAlgorithmException(msg, e);
         }
     }
 
@@ -204,8 +214,9 @@ public class SimpleHash extends Abstract
      *
      * @param bytes the bytes to hash.
      * @return the hashed bytes.
+     * @throws UnknownAlgorithmException if the configured {@link 
#getAlgorithmName() algorithmName} is not available.
      */
-    protected byte[] hash(byte[] bytes) {
+    protected byte[] hash(byte[] bytes) throws UnknownAlgorithmException {
         return hash(bytes, null, 1);
     }
 
@@ -215,8 +226,9 @@ public class SimpleHash extends Abstract
      * @param bytes the bytes to hash
      * @param salt  the salt to use for the initial hash
      * @return the hashed bytes
+     * @throws UnknownAlgorithmException if the configured {@link 
#getAlgorithmName() algorithmName} is not available.
      */
-    protected byte[] hash(byte[] bytes, byte[] salt) {
+    protected byte[] hash(byte[] bytes, byte[] salt) throws 
UnknownAlgorithmException {
         return hash(bytes, salt, 1);
     }
 
@@ -227,8 +239,9 @@ public class SimpleHash extends Abstract
      * @param salt           the salt to use for the initial hash
      * @param hashIterations the number of times the the {@code bytes} will be 
hashed (for attack resiliency).
      * @return the hashed bytes.
+     * @throws UnknownAlgorithmException if the {@link #getAlgorithmName() 
algorithmName} is not available.
      */
-    protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) {
+    protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) 
throws UnknownAlgorithmException {
         MessageDigest digest = getDigest(getAlgorithmName());
         if (salt != null) {
             digest.reset();

Modified: 
shiro/trunk/core/src/main/java/org/apache/shiro/util/JavaEnvironment.java
URL: 
http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/util/JavaEnvironment.java?rev=1086106&r1=1086105&r2=1086106&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/util/JavaEnvironment.java 
(original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/util/JavaEnvironment.java 
Mon Mar 28 02:55:23 2011
@@ -144,4 +144,20 @@ public abstract class JavaEnvironment {
     public static boolean isAtLeastVersion15() {
         return getMajorVersion() >= JAVA_15;
     }
+
+    /**
+     * Convenience method to determine if the current JVM is at least
+     * Java 1.6 (Java 6).
+     *
+     * @return <code>true</code> if the current JVM is at least Java 1.6
+     * @see #getMajorVersion()
+     * @see #JAVA_15
+     * @see #JAVA_16
+     * @see #JAVA_17
+     *
+     * @since 1.2
+     */
+    public static boolean isAtLeastVersion16() {
+        return getMajorVersion() >= JAVA_16;
+    }
 }

Modified: shiro/trunk/pom.xml
URL: 
http://svn.apache.org/viewvc/shiro/trunk/pom.xml?rev=1086106&r1=1086105&r2=1086106&view=diff
==============================================================================
--- shiro/trunk/pom.xml (original)
+++ shiro/trunk/pom.xml Mon Mar 28 02:55:23 2011
@@ -71,6 +71,7 @@
         <!-- Don't change this version without also changing the shiro-aspect 
and shiro-features
              modules' OSGi metadata: -->
         <aspectj.version>1.6.9</aspectj.version>
+        <commons.cli.version>1.2</commons.cli.version>
         <crowd.version>1.5.2</crowd.version>
         <!-- Don't change this version without also changing the shiro-ehcache 
and shiro-features
              modules' OSGi metadata: -->
@@ -168,6 +169,7 @@
         <module>web</module>
         <module>support</module>
         <module>samples</module>
+        <module>tools</module>
         <module>all</module>
     </modules>
 
@@ -378,6 +380,7 @@
                 <version>${project.version}</version>
             </dependency>
 
+
             <!-- Intra project test dependencies: -->
             <dependency>
                 <groupId>org.apache.shiro</groupId>
@@ -389,6 +392,12 @@
 
             <!-- 3rd party dependencies -->
             <dependency>
+                <!-- used for the 'hashpass' command line tool: -->
+                <groupId>commons-cli</groupId>
+                <artifactId>commons-cli</artifactId>
+                <version>${commons.cli.version}</version>
+            </dependency>
+            <dependency>
                 <groupId>org.aspectj</groupId>
                 <artifactId>aspectjrt</artifactId>
                 <version>${aspectj.version}</version>

Propchange: shiro/trunk/tools/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Mon Mar 28 02:55:23 2011
@@ -0,0 +1 @@
+*.iml

Propchange: shiro/trunk/tools/hasher/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Mon Mar 28 02:55:23 2011
@@ -0,0 +1 @@
+*.iml

Added: shiro/trunk/tools/hasher/pom.xml
URL: 
http://svn.apache.org/viewvc/shiro/trunk/tools/hasher/pom.xml?rev=1086106&view=auto
==============================================================================
--- shiro/trunk/tools/hasher/pom.xml (added)
+++ shiro/trunk/tools/hasher/pom.xml Mon Mar 28 02:55:23 2011
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+
+    <parent>
+        <groupId>org.apache.shiro.tools</groupId>
+        <artifactId>shiro-tools</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>shiro-tools-hasher</artifactId>
+    <name>Apache Shiro :: Tools :: Hasher</name>
+    <packaging>bundle</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-core</artifactId>
+            <exclusions>
+                <!-- BeanUtils is for ini config - this simple command line 
program
+                     does not init a shiro environment, so we don't need it: 
-->
+                <exclusion>
+                    <groupId>commons-beanutils</groupId>
+                    <artifactId>commons-beanutils</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>commons-cli</groupId>
+            <artifactId>commons-cli</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <version>2.2.1</version>
+                <configuration>
+                    <descriptorRefs>
+                        <descriptorRef>jar-with-dependencies</descriptorRef>
+                    </descriptorRefs>
+                    <!-- use the default artifact name, overwriting the 
original.  This is only
+                         for Command Line use and won't be referenced as a 
library: -->
+                    
<finalName>${project.artifactId}-${project.version}-cli</finalName>
+                    <appendAssemblyId>false</appendAssemblyId>
+                    <archive>
+                        <manifest>
+                            
<mainClass>org.apache.shiro.tools.hasher.Hasher</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+                <executions>
+                    <execution>
+                        <!-- this is used for inheritance merges: -->
+                        <id>make-assembly</id>
+                        <!-- bind to the packaging phase: -->
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        
<Bundle-SymbolicName>org.apache.shiro.tools.hasher</Bundle-SymbolicName>
+                        
<Export-Package>org.apache.shiro.tools.hasher*;version=${project.version}</Export-Package>
+                        <Import-Package>
+                            
org.apache.shiro*;version="${shiro.osgi.importRange}",
+                            *
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

Added: 
shiro/trunk/tools/hasher/src/main/java/org/apache/shiro/tools/hasher/Hasher.java
URL: 
http://svn.apache.org/viewvc/shiro/trunk/tools/hasher/src/main/java/org/apache/shiro/tools/hasher/Hasher.java?rev=1086106&view=auto
==============================================================================
--- 
shiro/trunk/tools/hasher/src/main/java/org/apache/shiro/tools/hasher/Hasher.java
 (added)
+++ 
shiro/trunk/tools/hasher/src/main/java/org/apache/shiro/tools/hasher/Hasher.java
 Mon Mar 28 02:55:23 2011
@@ -0,0 +1,272 @@
+/*
+ * 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.shiro.tools.hasher;
+
+import org.apache.commons.cli.*;
+import org.apache.shiro.crypto.UnknownAlgorithmException;
+import org.apache.shiro.crypto.hash.SimpleHash;
+import org.apache.shiro.io.ResourceUtils;
+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).
+ * <p/>
+ * Usage:
+ * <pre>
+ * java -jar shiro-tools-hasher<em>-version</em>.jar
+ * </pre>
+ * This will print out all supported options with documentation.
+ *
+ * @since 1.2
+ */
+public final class Hasher {
+
+
+    private static final String DEFAULT_ALGORITHM_NAME = "MD5";
+    private static final int DEFAULT_NUM_ITERATIONS = 1;
+
+    private static final String ALG_OPT = "a";
+    private static final String ALG_OPT_LONG = "algorithm";
+    private static final String DEBUG_OPT = "d";
+    private static final String DEBUG_OPT_LONG = "debug"; //show stack traces 
if there are any.
+    private static final String ITER_OPT = "i";
+    private static final String ITER_OPT_LONG = "iterations";
+    private static final String HEX_OPT = "h";
+    private static final String HEX_OPT_LONG = "hex";
+    private static final String HELP_OPT = "help";
+    private static final String HELP_OPT_LONG = "help";
+    private static final String PASSWORD_OPT = "p";
+    private static final String PASSWORD_OPT_LONG = "password";
+    private static final String PASSWORD_OPT_NOCONFIRM = "pnc";
+    private static final String PASSWORD_OPT_NOCONFIRM_LONG = "pnoconfirm";
+    private static final String RESOURCE_OPT = "r";
+    private static final String RESOURCE_OPT_LONG = "resource";
+
+
+    public static void main(String[] args) {
+
+        CommandLineParser parser = new PosixParser();
+
+        Options options = new Options();
+        options.addOption(ALG_OPT, ALG_OPT_LONG, true, "hash algorithm name.  
Defaults to MD5.");
+        options.addOption(ITER_OPT, ITER_OPT_LONG, true, "number of hash 
iterations.  Defaults to 1.");
+        options.addOption(HEX_OPT, HEX_OPT_LONG, false, "print hex value.  
Defaults to Base64.");
+        options.addOption(HELP_OPT, HELP_OPT_LONG, false, "print this help 
message.");
+        options.addOption(DEBUG_OPT, DEBUG_OPT_LONG, false, "show additional 
error (stack trace) information.");
+        options.addOption(PASSWORD_OPT, PASSWORD_OPT_LONG, false, "hash a 
password (do not echo).");
+        options.addOption(RESOURCE_OPT, RESOURCE_OPT_LONG, false, "read and 
hash the resource located at <value>. See below for more information.");
+        options.addOption(PASSWORD_OPT_NOCONFIRM, PASSWORD_OPT_NOCONFIRM_LONG, 
false, "disable confirmation prompt for password hashing.");
+
+        boolean debug = false;
+        String algorithm = DEFAULT_ALGORITHM_NAME;
+        int iterations = DEFAULT_NUM_ITERATIONS;
+        boolean base64 = true;
+        boolean resource = false;
+        boolean password = false;
+        boolean passwordConfirm = true;
+
+        char[] passwordChars = null;
+
+        try {
+            CommandLine line = parser.parse(options, args);
+
+            if (line.hasOption(HELP_OPT)) {
+                printHelpAndExit(options, null, debug, 0);
+            }
+            if (line.hasOption(DEBUG_OPT)) {
+                debug = true;
+            }
+            if (line.hasOption(ALG_OPT)) {
+                algorithm = line.getOptionValue(ALG_OPT);
+            }
+            if (line.hasOption(ITER_OPT)) {
+                iterations = getRequiredPositiveInt(line, ITER_OPT, 
ITER_OPT_LONG);
+            }
+            if (line.hasOption(HEX_OPT)) {
+                base64 = false;
+            }
+            if (line.hasOption(PASSWORD_OPT)) {
+                password = true;
+            }
+            if (line.hasOption(RESOURCE_OPT)) {
+                resource = true;
+            }
+            if (line.hasOption(PASSWORD_OPT_NOCONFIRM)) {
+                passwordConfirm = false;
+            }
+
+            Object source;
+
+            if (password) {
+                passwordChars = readPassword(passwordConfirm);
+                source = passwordChars;
+            } else {
+                String[] remainingArgs = line.getArgs();
+                if (remainingArgs == null || remainingArgs.length != 1) {
+                    printHelpAndExit(options, null, debug, -1);
+                }
+
+                assert remainingArgs != null;
+                String value = toString(remainingArgs);
+
+                if (resource) {
+                    if (!ResourceUtils.hasResourcePrefix(value)) {
+                        source = toFile(value);
+                    } else {
+                        source = ResourceUtils.getInputStreamForPath(value);
+                    }
+                } else {
+                    source = value;
+                }
+            }
+
+            SimpleHash hash = new SimpleHash(algorithm, source, /* salt not 
supported yet*/ null, iterations);
+            if (base64) {
+                System.out.println(hash.toBase64());
+            } else {
+                System.out.println(hash.toHex());
+            }
+        } catch (IllegalArgumentException iae) {
+            exit(iae, debug);
+        } catch (UnknownAlgorithmException uae) {
+            exit(uae, debug);
+        } catch (IOException ioe) {
+            exit(ioe, debug);
+        } catch (Exception e) {
+            printHelpAndExit(options, e, debug, -1);
+        } finally {
+            if (passwordChars != null && passwordChars.length > 0) {
+                for (int i = 0; i < passwordChars.length; i++) {
+                    passwordChars[i] = ' ';
+                }
+            }
+        }
+    }
+
+    private static String toString(String[] strings) {
+        int len = strings != null ? strings.length : 0;
+        if (len == 0) {
+            return null;
+        }
+        return StringUtils.toDelimitedString(strings, " ");
+    }
+
+    private static int getRequiredPositiveInt(CommandLine line, String opt, 
String optLong) {
+        String iterVal = line.getOptionValue(opt);
+        try {
+            return Integer.parseInt(iterVal);
+        } catch (NumberFormatException e) {
+            String msg = "'" + optLong + "' value must be a positive integer.";
+            throw new IllegalArgumentException(msg, e);
+        }
+    }
+
+    private static File toFile(String path) {
+        String resolved = path;
+        if (path.startsWith("~/") || path.startsWith(("~\\"))) {
+            resolved = path.replaceFirst("\\~", 
System.getProperty("user.home"));
+        }
+        return new File(resolved);
+    }
+
+    private static char[] readPassword(boolean confirm) {
+        if (!JavaEnvironment.isAtLeastVersion16()) {
+            String msg = "Password hashing (prompt without echo) uses the 
java.io.Console to read passwords " +
+                    "safely.  This is only available on Java 1.6 platforms and 
later.";
+            throw new IllegalArgumentException(msg);
+        }
+        java.io.Console console = System.console();
+        if (console == null) {
+            throw new IllegalStateException("java.io.Console is not available 
on the current JVM.  Cannot read passwords.");
+        }
+        char[] first = console.readPassword("%s", "Password to hash: ");
+        if (first == null || first.length == 0) {
+            throw new IllegalArgumentException("No password specified.");
+        }
+        if (confirm) {
+            char[] second = console.readPassword("%s", "Password to hash 
(confirm): ");
+            if (!Arrays.equals(first, second)) {
+                String msg = "Password entries do not match.";
+                throw new IllegalArgumentException(msg);
+            }
+        }
+        return first;
+    }
+
+    private static void printHelp(Options options, Exception e, boolean debug) 
{
+        HelpFormatter help = new HelpFormatter();
+        String command = "java -jar shiro-tools-hasher-<version>.jar [options] 
[<value>]";
+        String header = "\nPrint a cryptographic hash (aka message digest) of 
the specified <value>.\n--\nOptions:";
+        String footer = "\n" +
+                "<value> is optional only when hashing passwords (see below).  
It is\n" +
+                "required all other times.\n\n" +
+                "Password Hashing:\n--\n" +
+                "Specify the -p/--password option and DO NOT enter a <value>.  
You will\n" +
+                "be prompted for a password and characters will not echo as 
you type.\n\n" +
+                "Files, URLs and classpath resources:\n--\n" +
+                "If using the -r/--resource option, the <value> represents a 
resource path.\n" +
+                "By default this is expected to be a file path, but you may 
specify\n" +
+                "classpath or URL resources by using the classpath: or url: 
prefix\n" +
+                "respectively.\n\n" +
+                "Some examples:\n\n" +
+                "<command> -r fileInCurrentDirectory.txt\n" +
+                "<command> -r ../../relativePathFile.xml\n" +
+                "<command> -r ~/documents/myfile.pdf\n"+
+                "<command> -r /usr/local/logs/absolutePathFile.log\n" +
+                "<command> -r url:http://foo.com/page.html\n"; +
+                "<command> -r classpath:/WEB-INF/lib/something.jar";
+
+        printException(e, debug);
+
+        System.out.println();
+        help.printHelp(command, header, options, null);
+        System.out.println(footer);
+    }
+
+    private static void exit(Exception e, boolean debug) {
+        printException(e, debug);
+        System.exit(-1);
+    }
+
+    private static void printException(Exception e, boolean debug) {
+        if (e != null) {
+            System.out.println();
+            if (debug) {
+                System.out.println("Error: ");
+                e.printStackTrace(System.out);
+                System.out.println(e.getMessage());
+
+            } else {
+                System.out.println("Error: " + e.getMessage());
+                System.out.println();
+                System.out.println("Specify -d or --debug for more 
information.");
+            }
+        }
+    }
+
+    private static void printHelpAndExit(Options options, Exception e, boolean 
debug, int exitCode) {
+        printHelp(options, e, debug);
+        System.exit(exitCode);
+    }
+}

Added: shiro/trunk/tools/pom.xml
URL: 
http://svn.apache.org/viewvc/shiro/trunk/tools/pom.xml?rev=1086106&view=auto
==============================================================================
--- shiro/trunk/tools/pom.xml (added)
+++ shiro/trunk/tools/pom.xml Mon Mar 28 02:55:23 2011
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+
+    <parent>
+        <groupId>org.apache.shiro</groupId>
+        <artifactId>shiro-root</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.apache.shiro.tools</groupId>
+    <artifactId>shiro-tools</artifactId>
+    <name>Apache Shiro :: Tools</name>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>hasher</module>
+    </modules>
+
+</project>
+


Reply via email to