Thursday, July 11, 2013

Shiro with Spring: Remember Me - Custom Cipher Key

Recently was working with Shiro apache security library. Nice library with good inbuilt methods like “Remember Me” function.
You just need to set the value at the time of login and it will take care of rest. There is a small caveat, this functionality uses a hardcoded AES key to encrypt the user name. More information here: http://shiro.apache.org/configuration.html#Configuration-ByteArrayValues

As the article rightly mentioned you can specify your own key in the shiro configuration ini file. Ok, I did the same but in my project we were using Spring to configure Shiro so instead of specifying the key in shiro.ini, I specified it in my properties file, which was read by the spring config file. Sample code: In spring config file:

<bean id="securityManager"  class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm"/>
<property name="subjectFactory" ref="mySubjectFactory"/>
<property name="rememberMeManager.cipherKey">
<value>Your Key</value>
</property>
</bean>

All worked fine, the key was picked up but my test cases started failing. 

Downloaded the source for Shiro, debugged the flow and found that Key picked properly, also getting read in the AbstractRememberMeManager. After lots of wasted hours, found that there was exception from a Java class while encrypting user name. (invalid key length) . Come on I was using the same code to generate the key as mentioned in the comments of default cipher key.
(More wasted hours)
Finally did something basic, provided the default shiro cipher key in the properties file and again error. That made one thing clear – There was some issue in how the key was read/set.
(why I didn't find that while debugging, because the key is actually set as byte array and me being proficient only in English and Hindi couldn't notice the difference in byte representation :))
Ok, now where is it getting corrupted. Downloaded the spring libs’ code and found that the key received by the shiro class was getting corrupted in the spring code itself. On further investigation found that instead of using Base64.decode, spring was converting the key (which was encoded to string with Base64) to bytes directly (but of-course).
Looks like while reading from Shiro.ini file this is taken care of by shiro, but when using spring, one needs to make sure that key is set in the security manager after getting decoded to byte using Base64 only.

Huff, problem found, solution was much simpler. (no you cannot use spring's ByteArrayPropertyEditor it again does not use Base64)

Create a custom property editor.
1) Create a java class:

import java.beans.PropertyEditorSupport;
import com.ibm.xml.crypto.util.Base64;
public class BytesPropertyEditor extends PropertyEditorSupport {
    public void setAsText(String text) throws IllegalArgumentException {
        byte[] bytes = Base64.decode(text);
        setValue(bytes);
    }
}

2) Add following in your spring config:
<bean id="customEditorConfigure2"
    class="org.springframework.beans.factory.config.CustomEditorConfigurer">
    <property name="customEditors">
        <map>
            <entry key="byte[]">
                <bean class="com.amit.BytesPropertyEditor">
                </bean>
            </entry>
        </map>
    </property>
</bean>
And you are done. Happy remembering me... *_^

No comments:

Post a Comment