You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shiro.apache.org by lh...@apache.org on 2010/10/20 05:15:07 UTC
svn commit: r1024510 - in /shiro/trunk/core/src:
main/java/org/apache/shiro/authc/
main/java/org/apache/shiro/authc/credential/
main/java/org/apache/shiro/crypto/ main/java/org/apache/shiro/crypto/hash/
main/java/org/apache/shiro/util/ test/java/org/ap...
Author: lhazlewood
Date: Wed Oct 20 03:15:06 2010
New Revision: 1024510
URL: http://svn.apache.org/viewvc?rev=1024510&view=rev
Log:
SHIRO-186 - initial implementation complete. Up for peer review
Added:
shiro/trunk/core/src/main/java/org/apache/shiro/authc/SaltedAuthenticationInfo.java
shiro/trunk/core/src/main/java/org/apache/shiro/crypto/RandomNumberGenerator.java
shiro/trunk/core/src/main/java/org/apache/shiro/crypto/SecureRandomNumberGenerator.java
shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/AbstractHashedCredentialsMatcherTest.java (contents, props changed)
- copied, changed from r1022243, shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/HashedCredentialsMatcherTest.java
shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/HashedCredentialsMatcherTest.java
shiro/trunk/core/src/test/java/org/apache/shiro/crypto/SecureRandomNumberGeneratorTest.java
Modified:
shiro/trunk/core/src/main/java/org/apache/shiro/authc/SimpleAccount.java
shiro/trunk/core/src/main/java/org/apache/shiro/authc/SimpleAuthenticationInfo.java
shiro/trunk/core/src/main/java/org/apache/shiro/authc/credential/HashedCredentialsMatcher.java
shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/Hash.java
shiro/trunk/core/src/main/java/org/apache/shiro/util/ByteSource.java
shiro/trunk/core/src/main/java/org/apache/shiro/util/SimpleByteSource.java
shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Md2CredentialsMatcherTest.java
shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Md5CredentialsMatcherTest.java
shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha1CredentialsMatcherTest.java
shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha256CredentialsMatcherTest.java
shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha384CredentialsMatcherTest.java
shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha512CredentialsMatcherTest.java
shiro/trunk/core/src/test/java/org/apache/shiro/crypto/JcaCipherServiceTest.java
Added: shiro/trunk/core/src/main/java/org/apache/shiro/authc/SaltedAuthenticationInfo.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/authc/SaltedAuthenticationInfo.java?rev=1024510&view=auto
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/authc/SaltedAuthenticationInfo.java (added)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/authc/SaltedAuthenticationInfo.java Wed Oct 20 03:15:06 2010
@@ -0,0 +1,48 @@
+/*
+ * 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.authc;
+
+import org.apache.shiro.util.ByteSource;
+
+/**
+ * Interface representing account information that may use a salt when hashing credentials. This interface
+ * exists primarily to support environments that hash user credentials (e.g. passwords).
+ * <p/>
+ * Salts should typically be generated from a secure pseudo-random number generator so they are effectively
+ * impossible to guess. The salt value should be safely stored along side the account information to ensure
+ * it is maintained along with the account's credentials.
+ * <p/>
+ * This interface exists as a way for Shiro to acquire that salt so it can correctly perform
+ * {@link org.apache.shiro.authc.credential.CredentialsMatcher credentials matching} during login attempts.
+ * See the {@link org.apache.shiro.authc.credential.HashedCredentialsMatcher HashedCredentialsMatcher} JavaDoc for
+ * more information on hashing credentials with salts.
+ *
+ * @see org.apache.shiro.authc.credential.HashedCredentialsMatcher
+ *
+ * @since 1.1
+ */
+public interface SaltedAuthenticationInfo extends AuthenticationInfo {
+
+ /**
+ * Returns the salt used to salt the account's credentials or {@code null} if no salt was used.
+ *
+ * @return the salt used to salt the account's credentials or {@code null} if no salt was used.
+ */
+ ByteSource getCredentialsSalt();
+}
Modified: shiro/trunk/core/src/main/java/org/apache/shiro/authc/SimpleAccount.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/authc/SimpleAccount.java?rev=1024510&r1=1024509&r2=1024510&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/authc/SimpleAccount.java (original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/authc/SimpleAccount.java Wed Oct 20 03:15:06 2010
@@ -22,6 +22,7 @@ import org.apache.shiro.authz.Permission
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
+import org.apache.shiro.util.ByteSource;
import java.io.Serializable;
import java.util.Collection;
@@ -35,7 +36,7 @@ import java.util.Set;
*
* @since 0.1
*/
-public class SimpleAccount implements Account, MergableAuthenticationInfo, Serializable {
+public class SimpleAccount implements Account, MergableAuthenticationInfo, SaltedAuthenticationInfo, Serializable {
/*--------------------------------------------
| I N S T A N C E V A R I A B L E S |
@@ -65,6 +66,7 @@ public class SimpleAccount implements Ac
/*--------------------------------------------
| C O N S T R U C T O R S |
============================================*/
+
/**
* Default no-argument constructor.
*/
@@ -83,6 +85,22 @@ public class SimpleAccount implements Ac
}
/**
+ * Constructs a SimpleAccount instance for the specified realm with the given principals, hashedCredentials and
+ * credentials salt used when hashing the credentials.
+ *
+ * @param principal the 'primary' identifying attribute of the account, for example, a user id or username.
+ * @param hashedCredentials the credentials that verify identity for the account
+ * @param credentialsSalt the salt used when hashing the credentials
+ * @param realmName the name of the realm that accesses this account data
+ * @see org.apache.shiro.authc.credential.HashedCredentialsMatcher HashedCredentialsMatcher
+ * @since 1.1
+ */
+ public SimpleAccount(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName) {
+ this(principal instanceof PrincipalCollection ? (PrincipalCollection) principal : new SimplePrincipalCollection(principal, realmName),
+ hashedCredentials, credentialsSalt);
+ }
+
+ /**
* Constructs a SimpleAccount instance for the specified realm with the given principals and credentials.
*
* @param principals the identifying attributes of the account, at least one of which should be considered the
@@ -107,6 +125,21 @@ public class SimpleAccount implements Ac
}
/**
+ * Constructs a SimpleAccount instance for the specified principals and credentials.
+ *
+ * @param principals the identifying attributes of the account, at least one of which should be considered the
+ * account's 'primary' identifying attribute, for example, a user id or username.
+ * @param hashedCredentials the hashed credentials that verify identity for the account
+ * @param credentialsSalt the salt used when hashing the credentials
+ * @see org.apache.shiro.authc.credential.HashedCredentialsMatcher HashedCredentialsMatcher
+ * @since 1.1
+ */
+ public SimpleAccount(PrincipalCollection principals, Object hashedCredentials, ByteSource credentialsSalt) {
+ this.authcInfo = new SimpleAuthenticationInfo(principals, hashedCredentials, credentialsSalt);
+ this.authzInfo = new SimpleAuthorizationInfo();
+ }
+
+ /**
* Constructs a SimpleAccount instance for the specified principals and credentials, with the assigned roles.
*
* @param principals the identifying attributes of the account, at least one of which should be considered the
@@ -216,6 +249,30 @@ public class SimpleAccount implements Ac
}
/**
+ * Returns the salt used to hash this Account's credentials (eg for password hashing), or {@code null} if no salt
+ * was used or credentials were not hashed at all.
+ *
+ * @return the salt used to hash this Account's credentials (eg for password hashing), or {@code null} if no salt
+ * was used or credentials were not hashed at all.
+ * @since 1.1
+ */
+ public ByteSource getCredentialsSalt() {
+ return this.authcInfo.getCredentialsSalt();
+ }
+
+ /**
+ * Sets the salt to use to hash this Account's credentials (eg for password hashing), or {@code null} if no salt
+ * is used or credentials are not hashed at all.
+ *
+ * @param salt the salt to use to hash this Account's credentials (eg for password hashing), or {@code null} if no
+ * salt is used or credentials are not hashed at all.
+ * @since 1.1
+ */
+ public void setSalt(ByteSource salt) {
+ this.authcInfo.setSalt(salt);
+ }
+
+ /**
* Returns <code>this.authzInfo.getRoles();</code>
*
* @return the Account's assigned roles.
Modified: shiro/trunk/core/src/main/java/org/apache/shiro/authc/SimpleAuthenticationInfo.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/authc/SimpleAuthenticationInfo.java?rev=1024510&r1=1024509&r2=1024510&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/authc/SimpleAuthenticationInfo.java (original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/authc/SimpleAuthenticationInfo.java Wed Oct 20 03:15:06 2010
@@ -21,6 +21,7 @@ package org.apache.shiro.authc;
import org.apache.shiro.subject.MutablePrincipalCollection;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
+import org.apache.shiro.util.ByteSource;
import java.util.Collection;
import java.util.HashSet;
@@ -34,7 +35,7 @@ import java.util.Set;
* @see org.apache.shiro.realm.AuthenticatingRealm
* @since 0.9
*/
-public class SimpleAuthenticationInfo implements MergableAuthenticationInfo {
+public class SimpleAuthenticationInfo implements MergableAuthenticationInfo, SaltedAuthenticationInfo {
/**
* The principals identifying the account associated with this AuthenticationInfo instance.
@@ -46,6 +47,13 @@ public class SimpleAuthenticationInfo im
protected Object credentials;
/**
+ * Any salt used in hashing the credentials.
+ *
+ * @since 1.1
+ */
+ protected ByteSource salt;
+
+ /**
* Default no-argument constructor.
*/
public SimpleAuthenticationInfo() {
@@ -56,7 +64,7 @@ public class SimpleAuthenticationInfo im
* associated with the specified realm.
* <p/>
* This is a convenience constructor and will construct a {@link PrincipalCollection PrincipalCollection} based
- * on the <code>principal</code> and <code>realmName</code> argument.
+ * on the {@code principal} and {@code realmName} argument.
*
* @param principal the 'primary' principal associated with the specified realm.
* @param credentials the credentials that verify the given principal.
@@ -68,6 +76,26 @@ public class SimpleAuthenticationInfo im
}
/**
+ * Constructor that takes in a single 'primary' principal of the account, its corresponding hashed credentials,
+ * the salt used to hash the credentials, and the name of the realm to associate with the principals.
+ * <p/>
+ * This is a convenience constructor and will construct a {@link PrincipalCollection PrincipalCollection} based
+ * on the <code>principal</code> and <code>realmName</code> argument.
+ *
+ * @param principal the 'primary' principal associated with the specified realm.
+ * @param hashedCredentials the hashed credentials that verify the given principal.
+ * @param credentialsSalt the salt used when hashing the given hashedCredentials
+ * @param realmName the realm from where the principal and credentials were acquired.
+ * @see org.apache.shiro.authc.credential.HashedCredentialsMatcher HashedCredentialsMatcher
+ * @since 1.1
+ */
+ public SimpleAuthenticationInfo(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName) {
+ this.principals = new SimplePrincipalCollection(principal, realmName);
+ this.credentials = hashedCredentials;
+ this.salt = credentialsSalt;
+ }
+
+ /**
* Constructor that takes in an account's identifying principal(s) and its corresponding credentials that verify
* the principals.
*
@@ -79,6 +107,22 @@ public class SimpleAuthenticationInfo im
this.credentials = credentials;
}
+ /**
+ * Constructor that takes in an account's identifying principal(s), hashed credentials used to verify the
+ * principals, and the salt used when hashing the credentials.
+ *
+ * @param principals a Realm's account's identifying principal(s)
+ * @param hashedCredentials the hashed credentials that verify the principals.
+ * @param credentialsSalt the salt used when hashing the hashedCredentials.
+ * @see org.apache.shiro.authc.credential.HashedCredentialsMatcher HashedCredentialsMatcher
+ * @since 1.1
+ */
+ public SimpleAuthenticationInfo(PrincipalCollection principals, Object hashedCredentials, ByteSource credentialsSalt) {
+ this.principals = new SimplePrincipalCollection(principals);
+ this.credentials = hashedCredentials;
+ this.salt = credentialsSalt;
+ }
+
public PrincipalCollection getPrincipals() {
return principals;
@@ -107,6 +151,40 @@ public class SimpleAuthenticationInfo im
}
/**
+ * Returns the salt used to hash the credentials, or {@code null} if no salt was used or credentials were not
+ * hashed at all.
+ * <p/>
+ * Note that this attribute is <em>NOT</em> handled in the
+ * {@link #merge(AuthenticationInfo) merge} method - a hash salt is only useful within a single realm (as each
+ * realm will perform it's own Credentials Matching logic), and once finished in that realm, Shiro has no further
+ * use for salts. Therefore it doesn't make sense to 'merge' salts in a multi-realm scenario.
+ *
+ * @return the salt used to hash the credentials, or {@code null} if no salt was used or credentials were not
+ * hashed at all.
+ * @since 1.1
+ */
+ public ByteSource getCredentialsSalt() {
+ return salt;
+ }
+
+ /**
+ * Sets the salt used to hash the credentials, or {@code null} if no salt was used or credentials were not
+ * hashed at all.
+ * <p/>
+ * Note that this attribute is <em>NOT</em> handled in the
+ * {@link #merge(AuthenticationInfo) merge} method - a hash salt is only useful within a single realm (as each
+ * realm will perform it's own Credentials Matching logic), and once finished in that realm, Shiro has no further
+ * use for salts. Therefore it doesn't make sense to 'merge' salts in a multi-realm scenario.
+ *
+ * @param salt the salt used to hash the credentials, or {@code null} if no salt was used or credentials were not
+ * hashed at all.
+ * @since 1.1
+ */
+ public void setSalt(ByteSource salt) {
+ this.salt = salt;
+ }
+
+ /**
* Takes the specified <code>info</code> argument and adds its principals and credentials into this instance.
*
* @param info the <code>AuthenticationInfo</code> to add into this instance.
@@ -124,7 +202,17 @@ public class SimpleAuthenticationInfo im
this.principals = new SimplePrincipalCollection(this.principals);
}
((MutablePrincipalCollection) this.principals).addAll(info.getPrincipals());
- }
+ }
+
+ //only mess with a salt value if we don't have one yet. It doesn't make snese
+ //to merge salt values from different realms because a salt is used only within
+ //the realm's credential matching process. But if the current instance's salt
+ //is null, then it can't hurt to pull in a non-null value if one exists.
+ //
+ //since 1.1:
+ if (this.salt == null && info instanceof SaltedAuthenticationInfo) {
+ this.salt = ((SaltedAuthenticationInfo) info).getCredentialsSalt();
+ }
Object thisCredentials = getCredentials();
Object otherCredentials = info.getCredentials();
Modified: shiro/trunk/core/src/main/java/org/apache/shiro/authc/credential/HashedCredentialsMatcher.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/authc/credential/HashedCredentialsMatcher.java?rev=1024510&r1=1024509&r2=1024510&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/authc/credential/HashedCredentialsMatcher.java (original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/authc/credential/HashedCredentialsMatcher.java Wed Oct 20 03:15:06 2010
@@ -20,6 +20,7 @@ package org.apache.shiro.authc.credentia
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.SaltedAuthenticationInfo;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.codec.Hex;
import org.apache.shiro.crypto.hash.AbstractHash;
@@ -34,36 +35,76 @@ import org.apache.shiro.crypto.hash.Hash
* anyone, so they often hash the users' credentials before they are saved in the data store.
* <p/>
* This class (and its subclasses) function as follows:
- * <p/>
- * It first hashes the {@code AuthenticationToken} credentials supplied by the user during their login. It then
- * compares this hashed value directly with the {@code AuthenticationInfo} credentials stored in the system. The stored account
- * credentials are expected to already be in hashed form. If these two values are equal, the submitted credentials
- * match.
- * <h3>Salting and Multiple Hash Iterations</h3>
- * Because simple hashing is sometimes not good enough for many applications, this class also supports 'salting'
+ * <ol>
+ * <li>Hash the {@code AuthenticationToken} credentials supplied by the user during their login.</li>
+ * <li>Compare this hashed value directly with the {@code AuthenticationInfo} credentials stored in the system
+ * (the stored account credentials are expected to already be in hashed form).</li>
+ * <li>If these two values are {@link #equals(Object, Object) equal}, the submitted credentials match, otherwise
+ * they do not.</li>
+ * </ol>
+ * <h2>Salting and Multiple Hash Iterations</h2>
+ * Because simple hashing is usually not good enough for secure applications, this class also supports 'salting'
* and multiple hash iterations. Please read this excellent
* <a href="http://www.owasp.org/index.php/Hashing_Java" _target="blank">Hashing Java article</a> to learn about
* salting and multiple iterations and why you might want to use them. (Note of sections 5
- * "Why add salt?" and 6 "Hardening against the attacker's attack").
+ * "Why add salt?" and 6 "Hardening against the attacker's attack"). We should also note here that all of
+ * Shiro's Hash implementations (for example, {@link org.apache.shiro.crypto.hash.Md5Hash Md5Hash},
+ * {@link org.apache.shiro.crypto.hash.Sha1Hash Sha1Hash}, etc) support salting and multiple hash iterations via
+ * overloaded constructors.
+ * <h4>Real World Case Study</h4>
+ * In April 2010, some public Atlassian Jira and Confluence
+ * installations (Apache Software Foundation, Codehaus, etc) were the target of account attacks and user accounts
+ * were compromised. The reason? Jira and Confluence at the time did not salt user passwords and attackers were
+ * able to use dictionary attacks to compromise user accounts (Atlassian has since
+ * <a href="http://blogs.atlassian.com/news/2010/04/oh_man_what_a_day_an_update_on_our_security_breach.html">
+ * fixed the problem</a> of course).
* <p/>
- * We should also note here that all of Shiro's Hash implementations (for example,
- * {@link org.apache.shiro.crypto.hash.Md5Hash Md5Hash}, {@link org.apache.shiro.crypto.hash.Sha1Hash Sha1Hash}, etc)
- * support salting and multiple hash iterations via overloaded constructors.
- * <h4>Salting</h4>
- * Salting of the authentication token's credentials hash is disabled by default, but you may enable it by setting
- * {@link #setHashSalted hashSalted} to
- * {@code true}. If you do enable it, the value used to salt the hash will be
- * obtained from {@link #getSalt(org.apache.shiro.authc.AuthenticationToken) getSalt(authenticationToken)}.
- * <p/>
- * The default {@code getSalt} implementation merely returns
- * {@code token.getPrincipal()}, effectively using the user's identity as the salt, a most common
- * technique. If you wish to provide the authentication token's salt another way, you may override this
- * {@code getSalt} method.
- * <h4>Multiple Hash Iterations</h4>
- * If you hash your users' credentials multiple times before persisting to the data store, you will also need to
- * set this class's {@link #setHashIterations(int) hashIterations} property.
+ * The lesson?
+ * <p/>
+ * <b>ALWAYS, ALWAYS, ALWAYS SALT USER PASSWORDS!</b>
+ * <p/>
+ * <h3>Salting</h3>
+ * Prior to Shiro 1.1, salts could be obtained based on the end-user submitted
+ * {@link AuthenticationToken AuthenticationToken} via the now-deprecated
+ * {@link #getSalt(org.apache.shiro.authc.AuthenticationToken) getSalt(AuthenticationToken)} method. This however
+ * could constitute a security hole since ideally salts should never be obtained based on what a user can submit.
+ * User-submitted salt mechanisms are <em>much</em> more susceptible to dictionary attacks and <b>SHOULD NOT</b> be
+ * used in secure systems. Instead salts should ideally be a secure randomly-generated number that is generated when
+ * the user account is created. The secure number should never be disseminated to the user and always kept private
+ * by the application.
+ * <h4>Shiro 1.1</h4>
+ * As of Shiro 1.1, it is expected that any salt used to hash the submitted credentials will be obtained from the
+ * stored account information (represented as an {@link AuthenticationInfo AuthenticationInfo} instance). This is much
+ * more secure because the salt value remains private to the application (Shiro will never store this value).
+ * <p/>
+ * To enable this, {@code Realm}s should return {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} instances
+ * during authentication. {@code HashedCredentialsMatcher} implementations will then use the provided
+ * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt credentialsSalt} for hashing. To avoid
+ * security risks,
+ * <b>it is highly recommended that any existing {@code Realm} implementations that support hashed credentials are
+ * updated to return {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} instances as soon as possible</b>.
+ * <h4>Shiro 1.0 Backwards Compatibility</h4>
+ * Because of the identified security risk, {@code Realm} implementations that support credentials hashing should
+ * be updated to return {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} instances as
+ * soon as possible.
* <p/>
- * <b>Note:</b> <a href="http://en.wikipedia.org/wiki/MD5">MD5</a> and
+ * If this is not possible for some reason, this class will retain 1.0 backwards-compatible behavior of obtaining
+ * the salt via the now-deprecated {@link #getSalt(AuthenticationToken) getSalt(AuthenticationToken)} method. This
+ * method will only be invoked if a {@code Realm} <em>does not</em> return
+ * {@link SaltedAuthenticationInfo SaltedAutenticationInfo} instances and {@link #isHashSalted() hashSalted} is
+ * {@code true}.
+ * But please note that the {@link #isHashSalted() hashSalted} property and the
+ * {@link #getSalt(AuthenticationToken) getSalt(AuthenticationToken)} methods will be removed before the Shiro 2.0
+ * release.
+ * <h3>Multiple Hash Iterations</h3>
+ * If you hash your users' credentials multiple times before persisting to the data store, you will also need to
+ * set this class's {@link #setHashIterations(int) hashIterations} property. See the
+ * <a href="http://www.owasp.org/index.php/Hashing_Java" _target="blank">Hashing Java article</a>'s
+ * <a href="http://www.owasp.org/index.php/Hashing_Java#Hardening_against_the_attacker.27s_attack">
+ * "Hardening against the attacker's attack"</a> section to learn more about why you might want to use
+ * multiple hash iterations.
+ * <h2>MD5 & SHA-1 Notice</h2>
+ * <a href="http://en.wikipedia.org/wiki/MD5">MD5</a> and
* <a href="http://en.wikipedia.org/wiki/SHA_hash_functions">SHA-1</a> algorithms are now known to be vulnerable to
* compromise and/or collisions (read the linked pages for more). While most applications are ok with either of these
* two, if your application mandates high security, use the SHA-256 (or higher) hashing algorithms and their
@@ -122,7 +163,20 @@ public abstract class HashedCredentialsM
*
* @return {@code true} if a submitted {@code AuthenticationToken}'s credentials should be salted when hashing,
* {@code false} if it should not be salted.
+ * @deprecated since Shiro 1.1. Hash salting is now expected to be based on if the {@link AuthenticationInfo}
+ * returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its
+ * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method returns a non-null value.
+ * This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return
+ * {@code SaltedAuthenticationInfo} instances, but <b>it is highly recommended that {@code Realm} implementations
+ * that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo}
+ * instances as soon as possible</b>.
+ * <p/>
+ * This is because salts should always be obtained from the stored account information and
+ * never be interpreted based on user/Subject-entered data. User-entered data is easier to compromise for
+ * attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user
+ * are almost impossible to break. This method will be removed in Shiro 2.0.
*/
+ @Deprecated
public boolean isHashSalted() {
return hashSalted;
}
@@ -130,12 +184,25 @@ public abstract class HashedCredentialsM
/**
* Sets whether or not to salt a submitted {@code AuthenticationToken}'s credentials when hashing.
* <p/>
- * If enabled, the salt used will be obtained via the {@link #getSalt(org.apache.shiro.authc.AuthenticationToken) getSalt} method.
+ * If enabled, the salt used will be obtained via the {@link #getSalt(org.apache.shiro.authc.AuthenticationToken) getCredentialsSalt} method.
* </p>
* The default value is {@code false}.
*
* @param hashSalted whether or not to salt a submitted {@code AuthenticationToken}'s credentials when hashing.
+ * @deprecated since Shiro 1.1. Hash salting is now expected to be based on if the {@link AuthenticationInfo}
+ * returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its
+ * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method returns a non-null value.
+ * This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return
+ * {@code SaltedAuthenticationInfo} instances, but <b>it is highly recommended that {@code Realm} implementations
+ * that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo}
+ * instances as soon as possible</b>.
+ * <p/>
+ * This is because salts should always be obtained from the stored account information and
+ * never be interpreted based on user/Subject-entered data. User-entered data is easier to compromise for
+ * attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user
+ * are almost impossible to break. This method will be removed in Shiro 2.0.
*/
+ @Deprecated
public void setHashSalted(boolean hashSalted) {
this.hashSalted = hashSalted;
}
@@ -181,29 +248,24 @@ public abstract class HashedCredentialsM
*
* @param token the AuthenticationToken submitted during the authentication attempt.
* @return a salt value to use to hash the authentication token's credentials.
+ * @deprecated since Shiro 1.1. Hash salting is now expected to be based on if the {@link AuthenticationInfo}
+ * returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its
+ * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method returns a non-null value.
+ * This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return
+ * {@code SaltedAuthenticationInfo} instances, but <b>it is highly recommended that {@code Realm} implementations
+ * that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo}
+ * instances as soon as possible</b>.<p/>
+ * This is because salts should always be obtained from the stored account information and
+ * never be interpreted based on user/Subject-entered data. User-entered data is easier to compromise for
+ * attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user
+ * are almost impossible to break. This method will be removed in Shiro 2.0.
*/
+ @Deprecated
protected Object getSalt(AuthenticationToken token) {
return token.getPrincipal();
}
/**
- * As this is a HashedCredentialMatcher, this method overrides the parent method by returning a hashed value
- * of the submitted token's credentials.
- * <p/>
- * Based on this class's configuration, the return value may be salted and/or
- * hashed multiple times (see the class-level JavaDoc for more information on salting and
- * multiple hash iterations).
- *
- * @param token the authentication token submitted during the authentication attempt.
- * @return the hashed value of the authentication token's credentials.
- */
- protected Object getCredentials(AuthenticationToken token) {
- Object credentials = token.getCredentials();
- Object salt = isHashSalted() ? getSalt(token) : null;
- return hashProvidedCredentials(credentials, salt, getHashIterations());
- }
-
- /**
* Returns a {@link Hash Hash} instance representing the already-hashed AuthenticationInfo credentials stored in the system.
* <p/>
* This method reconstructs a {@link Hash Hash} instance based on a {@code info.getCredentials} call,
@@ -242,6 +304,58 @@ public abstract class HashedCredentialsM
}
/**
+ * This implementation first hashes the {@code token}'s credentials, potentially using a
+ * {@code salt} if the {@code info} argument is a
+ * {@link org.apache.shiro.authc.SaltedAuthenticationInfo SaltedAuthenticationInfo}. It then compares the hash
+ * against the {@code AuthenticationInfo}'s
+ * {@link #getCredentials(org.apache.shiro.authc.AuthenticationInfo) already-hashed credentials}. This method
+ * returns {@code true} if those two values are {@link #equals(Object, Object) equal}, {@code false} otherwise.
+ *
+ * @param token the {@code AuthenticationToken} submitted during the authentication attempt.
+ * @param info the {@code AuthenticationInfo} stored in the system matching the token principal
+ * @return {@code true} if the provided token credentials hash match to the stored account credentials hash,
+ * {@code false} otherwise
+ * @since 1.1
+ */
+ @Override
+ public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
+ Object tokenHashedCredentials = hashProvidedCredentials(token, info);
+ Object accountCredentials = getCredentials(info);
+ return equals(tokenHashedCredentials, accountCredentials);
+ }
+
+ /**
+ * Hash the provided {@code token}'s credentials using the salt stored with the account if the
+ * {@code info} instance is an {@code instanceof} {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} (see
+ * the class-level JavaDoc for why this is the preferred approach).
+ * <p/>
+ * If the {@code info} instance is <em>not</em>
+ * an {@code instanceof} {@code SaltedAuthenticationInfo}, the logic will fall back to Shiro 1.0
+ * backwards-compatible logic: it will first check to see {@link #isHashSalted() isHashSalted} and if so, will try
+ * to acquire the salt from {@link #getSalt(AuthenticationToken) getSalt(AuthenticationToken)}. See the class-level
+ * JavaDoc for why this is not recommended. This 'fallback' logic exists only for backwards-compatibility.
+ * {@code Realm}s should be updated as soon as possible to return {@code SaltedAuthenticationInfo} instances
+ * if account credentials salting is enabled (highly recommended for password-based systems).
+ *
+ * @param token the submitted authentication token from which its credentials will be hashed
+ * @param info the stored account data, potentially used to acquire a salt
+ * @return the token credentials hash
+ * @since 1.1
+ */
+ protected Object hashProvidedCredentials(AuthenticationToken token, AuthenticationInfo info) {
+ Object salt = null;
+ if (info instanceof SaltedAuthenticationInfo) {
+ salt = ((SaltedAuthenticationInfo) info).getCredentialsSalt();
+ } else {
+ //retain 1.0 backwards compatibility:
+ if (isHashSalted()) {
+ salt = getSalt(token);
+ }
+ }
+ return hashProvidedCredentials(token.getCredentials(), salt, getHashIterations());
+ }
+
+ /**
* Hashes the provided credentials a total of {@code hashIterations} times, using the given salt. The hash
* implementation/algorithm used is left to subclasses.
*
Added: shiro/trunk/core/src/main/java/org/apache/shiro/crypto/RandomNumberGenerator.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/crypto/RandomNumberGenerator.java?rev=1024510&view=auto
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/crypto/RandomNumberGenerator.java (added)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/crypto/RandomNumberGenerator.java Wed Oct 20 03:15:06 2010
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+import org.apache.shiro.util.ByteSource;
+
+/**
+ * A component that can generate random number/byte values as needed. Useful in cryptography or security scenarios
+ * where random byte arrays are needed, such as for password salts, nonces, initialization vectors and other seeds.
+ * <p/>
+ * This is essentially the same as a {@link java.security.SecureRandom SecureRandom}, and indeed implementations
+ * of this interface will probably all use {@link java.security.SecureRandom SecureRandom} instances, but this
+ * interface provides a few additional benefits to end-users:
+ * <ul>
+ * <li>It is an interface rather than the JDK's {@code SecureRandom} concrete implementation. Implementation details
+ * can be customized as necessary based on the application's needs</li>
+ * <li>Default per-instance behavior can be customized on implementations, typically via JavaBeans mutators.</li>
+ * <li>Perhaps most important for Shiro end-users, tt can more easily be used as a source of cryptographic seed data,
+ * and the data returned is already in a more {@link ByteSource ByteSource} format in case that data needs to be
+ * {@link org.apache.shiro.util.ByteSource#toHex() hex} or
+ * {@link org.apache.shiro.util.ByteSource#toBase64() base64}-encoded.</li>
+ * </ul>
+ * For example, consider the following example generating password salts for new user accounts:
+ *
+ * <pre>
+ * RandomNumberGenerator saltGenerator = new {@link org.apache.shiro.crypto.SecureRandomNumberGenerator SecureRandomNumberGenerator}();
+ * User user = new User();
+ * user.setPasswordSalt(saltGenerator.nextBytes().toBase64());
+ * userDAO.save(user);
+ * </pre>
+ *
+ * @since 1.1
+ */
+public interface RandomNumberGenerator {
+
+ /**
+ * Generates a byte array of fixed length filled with random data, often useful for generating salts,
+ * initialization vectors or other seed data. The length is specified as a configuration
+ * value on the underlying implementation.
+ *
+ * @return a byte array of fixed length filled with random data.
+ */
+ ByteSource nextBytes();
+
+ /**
+ * Generates a byte array of the specified length filled with random data.
+ * @param numBytes the number of bytes to be populated with random data.
+ * @return a byte array of the specified length filled with random data.
+ */
+ ByteSource nextBytes(int numBytes);
+}
Added: shiro/trunk/core/src/main/java/org/apache/shiro/crypto/SecureRandomNumberGenerator.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/crypto/SecureRandomNumberGenerator.java?rev=1024510&view=auto
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/crypto/SecureRandomNumberGenerator.java (added)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/crypto/SecureRandomNumberGenerator.java Wed Oct 20 03:15:06 2010
@@ -0,0 +1,120 @@
+/*
+ * 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;
+
+import org.apache.shiro.util.ByteSource;
+import org.apache.shiro.util.SimpleByteSource;
+
+import java.security.SecureRandom;
+
+/**
+ * Default implementation of the {@link RandomNumberGenerator RandomNumberGenerator} interface, backed by a
+ * {@link SecureRandom SecureRandom} instance.
+ * <p/>
+ * This class is a little easier to use than using the JDK's {@code SecureRandom} class directly. It also
+ * allows for JavaBeans-style of customization, convenient for Shiro's INI configuration or other IoC configuration
+ * mechanism.
+ *
+ * @since 1.1
+ */
+public class SecureRandomNumberGenerator implements RandomNumberGenerator {
+
+ protected static final int DEFAULT_NEXT_BYTES_SIZE = 128;
+
+ private int defaultNextBytesSize;
+ private SecureRandom secureRandom;
+
+ /**
+ * Creates a new instance with a default backing {@link SecureRandom SecureRandom} and a
+ * {@link #getDefaultNextBytesSize() defaultNextBytesSize} of {@code 128}.
+ */
+ public SecureRandomNumberGenerator() {
+ this.defaultNextBytesSize = DEFAULT_NEXT_BYTES_SIZE;
+ this.secureRandom = new SecureRandom();
+ }
+
+ /**
+ * Seeds the backing {@link SecureRandom SecureRandom} instance with additional seed data.
+ *
+ * @param bytes the seed bytes
+ * @see SecureRandom#setSeed(byte[])
+ */
+ public void setSeed(byte[] bytes) {
+ this.secureRandom.setSeed(bytes);
+ }
+
+ /**
+ * Returns the {@link SecureRandom SecureRandom} backing this instance.
+ *
+ * @return the {@link SecureRandom SecureRandom} backing this instance.
+ */
+ public SecureRandom getSecureRandom() {
+ return secureRandom;
+ }
+
+ /**
+ * Sets the {@link SecureRandom SecureRandom} to back this instance.
+ *
+ * @param random the {@link SecureRandom SecureRandom} to back this instance.
+ * @throws NullPointerException if the method argument is null
+ */
+ public void setSecureRandom(SecureRandom random) throws NullPointerException {
+ if (random == null) {
+ throw new NullPointerException("SecureRandom argument cannot be null.");
+ }
+ this.secureRandom = random;
+ }
+
+ /**
+ * Returns the size of the generated byte array for calls to {@link #nextBytes() nextBytes()}. Defaults to
+ * {@code 128}, a commonly used number in cryptographic algorithms.
+ *
+ * @return the size of the generated byte array for calls to {@link #nextBytes() nextBytes()}.
+ */
+ public int getDefaultNextBytesSize() {
+ return defaultNextBytesSize;
+ }
+
+ /**
+ * Sets the size of the generated byte array for calls to {@link #nextBytes() nextBytes()}. Defaults to
+ * {@code 128}, a commonly used number in cryptographic algorithms.
+ *
+ * @param defaultNextBytesSize the size of the generated byte array for calls to {@link #nextBytes() nextBytes()}.
+ * @throws IllegalArgumentException if the argument is 0 or negative
+ */
+ public void setDefaultNextBytesSize(int defaultNextBytesSize) throws IllegalArgumentException {
+ if ( defaultNextBytesSize <= 0) {
+ throw new IllegalArgumentException("size value must be a positive integer (1 or larger)");
+ }
+ this.defaultNextBytesSize = defaultNextBytesSize;
+ }
+
+ public ByteSource nextBytes() {
+ return nextBytes(getDefaultNextBytesSize());
+ }
+
+ public ByteSource nextBytes(int numBytes) {
+ if (numBytes <= 0) {
+ throw new IllegalArgumentException("numBytes argument must be a positive integer (1 or larger)");
+ }
+ byte[] bytes = new byte[numBytes];
+ this.secureRandom.nextBytes(bytes);
+ return new SimpleByteSource(bytes);
+ }
+}
Modified: shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/Hash.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/Hash.java?rev=1024510&r1=1024509&r2=1024510&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/Hash.java (original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/crypto/hash/Hash.java Wed Oct 20 03:15:06 2010
@@ -18,6 +18,8 @@
*/
package org.apache.shiro.crypto.hash;
+import org.apache.shiro.util.ByteSource;
+
/**
* A Cryptographic {@code Hash} represents a one-way conversion algorithm that transforms an input source to an
* underlying byte array.
@@ -31,7 +33,7 @@ package org.apache.shiro.crypto.hash;
* @see Sha512Hash
* @since 0.9
*/
-public interface Hash {
+public interface Hash extends ByteSource {
/**
* Returns this Hash's byte array, that is, the hashed value of the original input source.
Modified: shiro/trunk/core/src/main/java/org/apache/shiro/util/ByteSource.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/util/ByteSource.java?rev=1024510&r1=1024509&r2=1024510&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/util/ByteSource.java (original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/util/ByteSource.java Wed Oct 20 03:15:06 2010
@@ -39,7 +39,6 @@ public interface ByteSource {
* @return the <a href="http://en.wikipedia.org/wiki/Hexadecimal">Hex</a>-formatted String representation of the
* underlying wrapped byte array.
*/
- @SuppressWarnings({"UnusedDeclaration"})
public String toHex();
/**
Modified: shiro/trunk/core/src/main/java/org/apache/shiro/util/SimpleByteSource.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/util/SimpleByteSource.java?rev=1024510&r1=1024509&r2=1024510&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/util/SimpleByteSource.java (original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/util/SimpleByteSource.java Wed Oct 20 03:15:06 2010
@@ -19,35 +19,107 @@
package org.apache.shiro.util;
import org.apache.shiro.codec.Base64;
+import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.codec.Hex;
+import java.io.*;
import java.util.Arrays;
/**
* Very simple {@link ByteSource ByteSource} implementation that maintains an internal {@code byte[]} array and uses the
* {@link Hex Hex} and {@link Base64 Base64} codec classes to support the
* {@link #toHex() toHex()} and {@link #toBase64() toBase64()} implementations.
+ * <p/>
+ * The constructors on this class accept the following implicit byte-backed data types and will convert them to
+ * a byte-array automatically:
+ * <ul>
+ * <li>byte[]</li>
+ * <li>char[]</li>
+ * <li>String</li>
+ * <li>{@link ByteSource ByteSource}</li>
+ * <li>{@link File File}</li>
+ * <li>{@link InputStream InputStream}</li>
+ * </ul>
*
* @since 1.0
*/
public class SimpleByteSource implements ByteSource {
private final byte[] bytes;
+ private String cachedHex;
+ private String cachedBase64;
public SimpleByteSource(byte[] bytes) {
this.bytes = bytes;
}
+ /**
+ * Creates an instance by converting the characters to a byte array (assumes UTF-8 encoding).
+ *
+ * @param chars the source characters to use to create the underlying byte array.
+ * @since 1.1
+ */
+ public SimpleByteSource(char[] chars) {
+ this.bytes = CodecSupport.toBytes(chars);
+ }
+
+ /**
+ * Creates an instance by converting the String to a byte array (assumes UTF-8 encoding).
+ *
+ * @param string the source string to convert to a byte array (assumes UTF-8 encoding).
+ * @since 1.1
+ */
+ public SimpleByteSource(String string) {
+ this.bytes = CodecSupport.toBytes(string);
+ }
+
+ /**
+ * Creates an instance using the sources bytes directly - it does not create a copy of the
+ * argument's byte array.
+ *
+ * @param source the source to use to populate the underlying byte array.
+ * @since 1.1
+ */
+ public SimpleByteSource(ByteSource source) {
+ this.bytes = source.getBytes();
+ }
+
+ /**
+ * Creates an instance by converting the file to a byte array.
+ *
+ * @param file the file from which to acquire bytes.
+ * @since 1.1
+ */
+ public SimpleByteSource(File file) {
+ this.bytes = new BytesHelper().getBytes(file);
+ }
+
+ /**
+ * Creates an instance by converting the stream to a byte array.
+ *
+ * @param stream the stream from which to acquire bytes.
+ * @since 1.1
+ */
+ public SimpleByteSource(InputStream stream) {
+ this.bytes = new BytesHelper().getBytes(stream);
+ }
+
public byte[] getBytes() {
return this.bytes;
}
public String toHex() {
- return Hex.encodeToString(getBytes());
+ if ( this.cachedHex == null ) {
+ this.cachedHex = Hex.encodeToString(getBytes());
+ }
+ return this.cachedHex;
}
public String toBase64() {
- return Base64.encodeToString(getBytes());
+ if ( this.cachedBase64 == null ) {
+ this.cachedBase64 = Base64.encodeToString(getBytes());
+ }
+ return this.cachedBase64;
}
public String toString() {
@@ -55,17 +127,29 @@ public class SimpleByteSource implements
}
public int hashCode() {
- return toBase64().hashCode();
+ return toString().hashCode();
}
public boolean equals(Object o) {
if (o == this) {
return true;
}
- if (o instanceof SimpleByteSource) {
- SimpleByteSource bs = (SimpleByteSource) o;
+ if (o instanceof ByteSource) {
+ ByteSource bs = (ByteSource) o;
return Arrays.equals(getBytes(), bs.getBytes());
}
return false;
}
+
+ //will probably be removed in Shiro 2.0. See SHIRO-203:
+ //https://issues.apache.org/jira/browse/SHIRO-203
+ private static final class BytesHelper extends CodecSupport {
+ public byte[] getBytes(File file) {
+ return toBytes(file);
+ }
+
+ public byte[] getBytes(InputStream stream) {
+ return toBytes(stream);
+ }
+ }
}
Copied: shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/AbstractHashedCredentialsMatcherTest.java (from r1022243, shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/HashedCredentialsMatcherTest.java)
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/AbstractHashedCredentialsMatcherTest.java?p2=shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/AbstractHashedCredentialsMatcherTest.java&p1=shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/HashedCredentialsMatcherTest.java&r1=1022243&r2=1024510&rev=1024510&view=diff
==============================================================================
--- shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/HashedCredentialsMatcherTest.java (original)
+++ shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/AbstractHashedCredentialsMatcherTest.java Wed Oct 20 03:15:06 2010
@@ -28,11 +28,10 @@ import org.apache.shiro.authc.UsernamePa
import org.apache.shiro.crypto.hash.AbstractHash;
import org.apache.shiro.util.ClassUtils;
-
/**
* @since Jun 10, 2008 4:47:09 PM
*/
-public abstract class HashedCredentialsMatcherTest extends TestCase {
+public abstract class AbstractHashedCredentialsMatcherTest extends TestCase {
public abstract Class<? extends HashedCredentialsMatcher> getMatcherClass();
Propchange: shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/AbstractHashedCredentialsMatcherTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/HashedCredentialsMatcherTest.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/HashedCredentialsMatcherTest.java?rev=1024510&view=auto
==============================================================================
--- shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/HashedCredentialsMatcherTest.java (added)
+++ shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/HashedCredentialsMatcherTest.java Wed Oct 20 03:15:06 2010
@@ -0,0 +1,117 @@
+/*
+ * 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.authc.credential;
+
+import org.apache.shiro.authc.*;
+import org.apache.shiro.crypto.SecureRandomNumberGenerator;
+import org.apache.shiro.crypto.hash.Sha1Hash;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.subject.SimplePrincipalCollection;
+import org.apache.shiro.util.ByteSource;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests for the {@link org.apache.shiro.authc.credential.HashedCredentialsMatcher} class.
+ */
+public class HashedCredentialsMatcherTest {
+
+ /**
+ * Test new Shiro 1.1 functionality, where the salt is obtained from the stored account information, as it
+ * should be. See <a href="https://issues.apache.org/jira/browse/SHIRO-186">SHIRO-186</a>
+ */
+ @Test
+ public void testSaltedAuthenticationInfo() {
+ //use SHA-1 hashing in this test:
+ HashedCredentialsMatcher matcher = new Sha1CredentialsMatcher();
+
+ //simulate a user account with a SHA-1 hashed and salted password:
+ ByteSource salt = new SecureRandomNumberGenerator().nextBytes();
+ Object hashedPassword = new Sha1Hash("password", salt);
+ SimpleAuthenticationInfo account = new SimpleAuthenticationInfo("username", hashedPassword, salt, "realmName");
+
+ //simulate a username/password (plaintext) token created in response to a login attempt:
+ AuthenticationToken token = new UsernamePasswordToken("username", "password");
+
+ //verify the hashed token matches what is in the account:
+ assertTrue(matcher.doCredentialsMatch(token, account));
+ }
+
+ /**
+ * Test backwards compatibility of unsalted credentials before
+ * <a href="https://issues.apache.org/jira/browse/SHIRO-186">SHIRO-186</a> edits.
+ */
+ @Test
+ public void testBackwardsCompatibleUnsaltedAuthenticationInfo() {
+ HashedCredentialsMatcher matcher = new Sha1CredentialsMatcher();
+
+ //simulate an account with SHA-1 hashed password (no salt)
+ final String username = "username";
+ final String password = "password";
+ final Object hashedPassword = new Sha1Hash(password).getBytes();
+ AuthenticationInfo account = new AuthenticationInfo() {
+ public PrincipalCollection getPrincipals() {
+ return new SimplePrincipalCollection(username, "realmName");
+ }
+
+ public Object getCredentials() {
+ return hashedPassword;
+ }
+ };
+
+ //simulate a username/password (plaintext) token created in response to a login attempt:
+ AuthenticationToken token = new UsernamePasswordToken("username", "password");
+
+ //verify the hashed token matches what is in the account:
+ assertTrue(matcher.doCredentialsMatch(token, account));
+ }
+
+ /**
+ * Test backwards compatibility of salted credentials before
+ * <a href="https://issues.apache.org/jira/browse/SHIRO-186">SHIRO-186</a> edits.
+ */
+ @Test
+ public void testBackwardsCompatibleSaltedAuthenticationInfo() {
+ HashedCredentialsMatcher matcher = new Sha1CredentialsMatcher();
+ //enable this for Shiro 1.0 backwards compatibility:
+ matcher.setHashSalted(true);
+
+ //simulate an account with SHA-1 hashed password, using the username as the salt
+ //(BAD IDEA, but backwards-compatible):
+ final String username = "username";
+ final String password = "password";
+ final Object hashedPassword = new Sha1Hash(password, username).getBytes();
+ AuthenticationInfo account = new AuthenticationInfo() {
+ public PrincipalCollection getPrincipals() {
+ return new SimplePrincipalCollection(username, "realmName");
+ }
+
+ public Object getCredentials() {
+ return hashedPassword;
+ }
+ };
+
+ //simulate a username/password (plaintext) token created in response to a login attempt:
+ AuthenticationToken token = new UsernamePasswordToken("username", "password");
+
+ //verify the hashed token matches what is in the account:
+ assertTrue(matcher.doCredentialsMatch(token, account));
+ }
+}
Modified: shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Md2CredentialsMatcherTest.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Md2CredentialsMatcherTest.java?rev=1024510&r1=1024509&r2=1024510&view=diff
==============================================================================
--- shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Md2CredentialsMatcherTest.java (original)
+++ shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Md2CredentialsMatcherTest.java Wed Oct 20 03:15:06 2010
@@ -25,7 +25,7 @@ import org.apache.shiro.crypto.hash.Md2H
/**
* @since Jun 10, 2008 4:38:16 PM
*/
-public class Md2CredentialsMatcherTest extends HashedCredentialsMatcherTest {
+public class Md2CredentialsMatcherTest extends AbstractHashedCredentialsMatcherTest {
public Class<? extends HashedCredentialsMatcher> getMatcherClass() {
return Md2CredentialsMatcher.class;
Modified: shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Md5CredentialsMatcherTest.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Md5CredentialsMatcherTest.java?rev=1024510&r1=1024509&r2=1024510&view=diff
==============================================================================
--- shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Md5CredentialsMatcherTest.java (original)
+++ shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Md5CredentialsMatcherTest.java Wed Oct 20 03:15:06 2010
@@ -25,7 +25,7 @@ import org.apache.shiro.crypto.hash.Md5H
/**
* @since Jun 10, 2008 4:59:36 PM
*/
-public class Md5CredentialsMatcherTest extends HashedCredentialsMatcherTest {
+public class Md5CredentialsMatcherTest extends AbstractHashedCredentialsMatcherTest {
public Class<? extends HashedCredentialsMatcher> getMatcherClass() {
return Md5CredentialsMatcher.class;
Modified: shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha1CredentialsMatcherTest.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha1CredentialsMatcherTest.java?rev=1024510&r1=1024509&r2=1024510&view=diff
==============================================================================
--- shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha1CredentialsMatcherTest.java (original)
+++ shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha1CredentialsMatcherTest.java Wed Oct 20 03:15:06 2010
@@ -25,7 +25,7 @@ import org.apache.shiro.crypto.hash.Sha1
/**
* @since Jun 10, 2008 5:00:30 PM
*/
-public class Sha1CredentialsMatcherTest extends HashedCredentialsMatcherTest {
+public class Sha1CredentialsMatcherTest extends AbstractHashedCredentialsMatcherTest {
public Class<? extends HashedCredentialsMatcher> getMatcherClass() {
return Sha1CredentialsMatcher.class;
Modified: shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha256CredentialsMatcherTest.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha256CredentialsMatcherTest.java?rev=1024510&r1=1024509&r2=1024510&view=diff
==============================================================================
--- shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha256CredentialsMatcherTest.java (original)
+++ shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha256CredentialsMatcherTest.java Wed Oct 20 03:15:06 2010
@@ -25,7 +25,7 @@ import org.apache.shiro.crypto.hash.Sha2
/**
* @since Jun 10, 2008 5:01:00 PM
*/
-public class Sha256CredentialsMatcherTest extends HashedCredentialsMatcherTest {
+public class Sha256CredentialsMatcherTest extends AbstractHashedCredentialsMatcherTest {
public Class<? extends HashedCredentialsMatcher> getMatcherClass() {
return Sha256CredentialsMatcher.class;
Modified: shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha384CredentialsMatcherTest.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha384CredentialsMatcherTest.java?rev=1024510&r1=1024509&r2=1024510&view=diff
==============================================================================
--- shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha384CredentialsMatcherTest.java (original)
+++ shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha384CredentialsMatcherTest.java Wed Oct 20 03:15:06 2010
@@ -25,7 +25,7 @@ import org.apache.shiro.crypto.hash.Sha3
/**
* @since Jun 10, 2008 5:02:27 PM
*/
-public class Sha384CredentialsMatcherTest extends HashedCredentialsMatcherTest {
+public class Sha384CredentialsMatcherTest extends AbstractHashedCredentialsMatcherTest {
public Class<? extends HashedCredentialsMatcher> getMatcherClass() {
return Sha384CredentialsMatcher.class;
Modified: shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha512CredentialsMatcherTest.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha512CredentialsMatcherTest.java?rev=1024510&r1=1024509&r2=1024510&view=diff
==============================================================================
--- shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha512CredentialsMatcherTest.java (original)
+++ shiro/trunk/core/src/test/java/org/apache/shiro/authc/credential/Sha512CredentialsMatcherTest.java Wed Oct 20 03:15:06 2010
@@ -25,7 +25,7 @@ import org.apache.shiro.crypto.hash.Sha5
/**
* @since Jun 10, 2008 5:02:58 PM
*/
-public class Sha512CredentialsMatcherTest extends HashedCredentialsMatcherTest {
+public class Sha512CredentialsMatcherTest extends AbstractHashedCredentialsMatcherTest {
public Class<? extends HashedCredentialsMatcher> getMatcherClass() {
return Sha512CredentialsMatcher.class;
Modified: shiro/trunk/core/src/test/java/org/apache/shiro/crypto/JcaCipherServiceTest.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/test/java/org/apache/shiro/crypto/JcaCipherServiceTest.java?rev=1024510&r1=1024509&r2=1024510&view=diff
==============================================================================
--- shiro/trunk/core/src/test/java/org/apache/shiro/crypto/JcaCipherServiceTest.java (original)
+++ shiro/trunk/core/src/test/java/org/apache/shiro/crypto/JcaCipherServiceTest.java Wed Oct 20 03:15:06 2010
@@ -1,3 +1,21 @@
+/*
+ * 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;
import static org.junit.Assert.*;
@@ -6,18 +24,13 @@ import org.junit.Test;
public class JcaCipherServiceTest {
- @Test
+ @Test(expected = CryptoException.class)
public void testDecrypt() {
- JcaCipherService cipherService = new JcaCipherService("AES") {};
- String ciphertext = "iv_helloword";
- String key = "somekey";
- try {
- // This should cause ArrayIndexOutOfBoundsException, at least currently that's what we want
- cipherService.decrypt(ciphertext.getBytes(), key.getBytes());
- } catch (CryptoException e) {
- return;
- }
- fail("CryptoException was expected to be thrown");
+ JcaCipherService cipherService = new JcaCipherService("AES") {
+ };
+ String ciphertext = "iv_helloword";
+ String key = "somekey";
+ cipherService.decrypt(ciphertext.getBytes(), key.getBytes());
}
}
Added: shiro/trunk/core/src/test/java/org/apache/shiro/crypto/SecureRandomNumberGeneratorTest.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/test/java/org/apache/shiro/crypto/SecureRandomNumberGeneratorTest.java?rev=1024510&view=auto
==============================================================================
--- shiro/trunk/core/src/test/java/org/apache/shiro/crypto/SecureRandomNumberGeneratorTest.java (added)
+++ shiro/trunk/core/src/test/java/org/apache/shiro/crypto/SecureRandomNumberGeneratorTest.java Wed Oct 20 03:15:06 2010
@@ -0,0 +1,96 @@
+/*
+ * 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;
+
+import org.apache.shiro.util.ByteSource;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests for the {@link SecureRandomNumberGenerator} class.
+ *
+ * @since 1.1
+ */
+public class SecureRandomNumberGeneratorTest {
+
+ @Test
+ public void testDefaultNextBytesSize() {
+ SecureRandomNumberGenerator rng = new SecureRandomNumberGenerator();
+ boolean negativeThrown = false;
+ boolean zeroThrown = false;
+ try {
+ rng.setDefaultNextBytesSize(-1);
+ } catch (IllegalArgumentException e) {
+ negativeThrown = true;
+ }
+
+ try {
+ rng.setDefaultNextBytesSize(0);
+ } catch (IllegalArgumentException e) {
+ zeroThrown = true;
+ }
+
+ assertTrue(negativeThrown);
+ assertTrue(zeroThrown);
+
+ ByteSource bs = rng.nextBytes();
+ assertNotNull(bs);
+ assertNotNull(bs.getBytes());
+ assertEquals(SecureRandomNumberGenerator.DEFAULT_NEXT_BYTES_SIZE, bs.getBytes().length);
+
+ rng.setDefaultNextBytesSize(64);
+ assertNotNull(bs);
+ bs = rng.nextBytes();
+ assertNotNull(bs.getBytes());
+ assertEquals(64, bs.getBytes().length);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void testInvalidSecureRandomProperty() {
+ SecureRandomNumberGenerator rng = new SecureRandomNumberGenerator();
+ rng.setSecureRandom(null);
+ }
+
+ @Test
+ public void testNextBytesWithSize() {
+ SecureRandomNumberGenerator rng = new SecureRandomNumberGenerator();
+ boolean negativeThrown = false;
+ boolean zeroThrown = false;
+ try {
+ rng.nextBytes(-1);
+ } catch (IllegalArgumentException e) {
+ negativeThrown = true;
+ }
+
+ try {
+ rng.nextBytes(0);
+ } catch (IllegalArgumentException e) {
+ zeroThrown = true;
+ }
+
+ assertTrue(negativeThrown);
+ assertTrue(zeroThrown);
+
+ ByteSource bs = rng.nextBytes(8);
+ assertNotNull(bs);
+ assertNotNull(bs.getBytes());
+ assertEquals(8, bs.getBytes().length);
+ }
+}