/* * Copyright (C) 2014 Intel Corporation * All rights reserved. */ package com.intel.mtwilson.shiro.authc.password; import com.intel.dcsg.cpg.crypto.Sha256Digest; import com.intel.dcsg.cpg.util.ByteArray; import com.intel.mtwilson.crypto.password.PasswordUtil; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.util.Arrays; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.credential.CredentialsMatcher; /** * This class is similar to HashedCredentialsMatcher that comes wtih * Apache Shiro but instead of having a static configuration of * the algorithm name and iteration count (which requires downtime * while upgrading passwords on the server for all accounts), * this matcher allows a per-instance configuration using the * corresponding PasswordAuthenticationInfo class used by the * JdbcPasswordRealm * * @author jbuhacoff */ public class PasswordCredentialsMatcher implements CredentialsMatcher { private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PasswordCredentialsMatcher.class); @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { log.debug("doCredentialsMatch with token {} and info {}", token, info); if( token.getCredentials() == null ) { return false; } if( !(info.getCredentials() instanceof HashedPassword)) { return false; } HashedPassword userLoginPasswordHash = (HashedPassword)info.getCredentials(); byte[] tokenBytes = toBytes(token.getCredentials()); if( tokenBytes == null ) { throw new IllegalArgumentException("Null password credential"); // empty passwords length==0 are allowed } byte[] hashedTokenBytes = PasswordUtil.hash(tokenBytes, userLoginPasswordHash); if( Arrays.equals(hashedTokenBytes, userLoginPasswordHash.getPasswordHash()) ) { return true; } return false; } /** * Convert the input password from String or char[] into byte[] * Assumes UTF-8 encoding * * If the input password is already byte[] it is returned as-is * * @param credentials * @return */ protected byte[] toBytes(Object credentials) { if( credentials == null ) { return null; } if( credentials instanceof byte[]) { return (byte[])credentials; } if( credentials instanceof char[]) { //ByteBuffer bytebuffer = Charset.forName("UTF-8").encode(CharBuffer.wrap((char[])credentials)); // this is wrong because the Charset encode() method appends a null terminator which will cause the resulting hash to be wrong // return bytebuffer.array(); return String.valueOf((char[])credentials).getBytes(Charset.forName("UTF-8")); } if( credentials instanceof String ) { return ((String)credentials).getBytes(Charset.forName("UTF-8")); } return null; } }