2 Replies Latest reply on May 22, 2012 5:54 AM by Simon Taylor

    Using hash password in Seam security 3.1.0 with JpaIdentityStore

    Khosro Asgharifard Sharabiani Newbie

      Hi,

      I have checkd out "idmconsole" sample from [https://github.com/seam/examples/tree/master/idmconsole] ,but it stores password in database as a plain text.

      And also i have tried to generate password hash and store it in database in class PopulateDatabase.java in "idmconsole" sample but when demo user wants to log in ,log in fails.

      I chanaged PopulateDatabase.java as follow : 

       

                            IdentityObjectCredential demoPassword = new IdentityObjectCredential();
                          demoPassword.setIdentityObject(demo);
                          demoPassword.setType(PASSWORD);
                          // demoPassword.setValue("demo");
                          try {
      //                              demoPassword.setValue(new PasswordHash().createPasswordKey(
      //                                                  "demo".toCharArray(), "demo".getBytes(), 2));
                                    demoPassword.setValue(new PasswordHash().generateHash("demo",PasswordHash.ALGORITHM_SHA));
                          } catch (Exception e) {
                                    e.printStackTrace();
                          }
                          entityManager.persist(demoPassword);
      
      

       

      I have used org.jboss.seam.security.management.PasswordHash to generate hash password in above code .

       

      I also look at the following method :

       

       public boolean validateCredential(IdentityStoreInvocationContext ctx,
                                            IdentityObject identityObject, IdentityObjectCredential credential)
      
      

      in class JpaIdentityStore.java ,but it seems it does not use PasswordHash class.It seems JpaIdentityStore.java class uses plain text of password to authenticate user.Am i right?

       

      Khosro.

        • 1. Re: Using hash password in Seam security 3.1.0 with JpaIdentityStore
          Jason Porter Master

          I believe you are correct, the hashing check feature hasn't been completed.

          • 2. Re: Using hash password in Seam security 3.1.0 with JpaIdentityStore
            Simon Taylor Newbie

            Guys, not implementing password hashing by default is really poor in my opinion. This is security 101 and leaves me questioning what else is missing from seam security and seam 3 in general. Anyway, I have come up with a work around that I hope will help other people. Please note that this solution only deals with authentication, it doesn't deal with creating users at the moment. I have used the recommended data model and this solution is aimed at that data model - it is not a generic solution as JpaIdentityStore is.

             

            public class JpaIdentityStoreWithHashing extends JpaIdentityStore {
            
                private static final long serialVersionUID = 6538258501489656692L;
            
                private static final String DefaultEncoding = "UTF-8";
                private static final String HashAlgorithm   = "SHA-256";
                private static final String SaltAttribute   = "SALT";
            
            
                private Class<?> credentialClass;
            
                public JpaIdentityStoreWithHashing(String id) {
                    super(id);
                }
            
                @Override
                public void bootstrap(org.picketlink.idm.spi.configuration.IdentityStoreConfigurationContext configurationContext)
                        throws IdentityException {
            
                    String clsName = 
                            configurationContext.getStoreConfigurationMetaData()
                                .getOptionSingleValue(OPTION_CREDENTIAL_CLASS_NAME);
            
            
                    if (clsName != null) {
                        try {
                            credentialClass = Class.forName(clsName);
                        } catch (ClassNotFoundException e) {
                            throw new IdentityException("Error bootstrapping JpaIdentityStoreWithHashing - invalid credential entity class: " + clsName);
                        }
                    }
                    super.bootstrap(configurationContext);
                }
            
                @Override
                public boolean validateCredential(org.picketlink.idm.spi.store.IdentityStoreInvocationContext ctx,
                        org.picketlink.idm.spi.model.IdentityObject identityObject, org.picketlink.idm.spi.model.IdentityObjectCredential credential)
                                throws IdentityException {
                    EntityManager em = getEntityManager(ctx);
            
            
                    if ( credentialClass == com.greenlifewater.entity.IdentityObjectCredential.class) {
                        CriteriaBuilder builder = em.getCriteriaBuilder();
                        CriteriaQuery<IdentityObjectCredential> criteria = builder.createQuery(IdentityObjectCredential.class);
                        Root<IdentityObjectCredential> root = criteria.from(IdentityObjectCredential.class);
            
            
                        List<Predicate> predicates = new ArrayList<Predicate>();
            
            
                        predicates.add(builder.equal(root.get(IdentityObjectCredential_.identityObject),
                                lookupIdentity(identityObject, em)));
            
            
                        predicates.add(builder.equal(root.get(IdentityObjectCredential_.type), lookupCredentialTypeEntity(credential.getType().getName(), em)));
            
            
                        criteria.where(predicates.toArray(new Predicate[0]));
            
            
                        List<IdentityObjectCredential> results = em.createQuery(criteria).getResultList();
            
            
                        if (results.isEmpty()) return false;
            
                        final String salt = getSalt(ctx, identityObject);
            
                        final byte[] bSalt          = Base64.decodeBase64(salt);
                        final byte[] proposedDigest = getHash(credential.getValue().toString(), bSalt);
            
                        for (IdentityObjectCredential result : results) {
                            final byte[] bDigest = Base64.decodeBase64(result.getValue());
            
                            if (Arrays.equals(proposedDigest, bDigest)) {
                                return true;
                            }
                        }
                    }
                    return super.validateCredential(ctx, identityObject, credential);
                }
            
                /**
                 * Gets the salt to use to create the hash of a password.
                 * @param ctx IdentityStoreInvocationContext the current context.
                 * @param identityObject IdentityObject the identity to get the salt for.
                 * @return the salt.
                 * @throws IdentityException if the default encoding doesn't exist.
                 */
                private String getSalt(org.picketlink.idm.spi.store.IdentityStoreInvocationContext ctx,
                                       org.picketlink.idm.spi.model.IdentityObject identityObject) throws IdentityException {
                    EntityManager em = getEntityManager(ctx);
            
                    CriteriaBuilder builder = em.getCriteriaBuilder();
                    CriteriaQuery<IdentityObjectAttribute> criteria = builder.createQuery(IdentityObjectAttribute.class);
                    Root<IdentityObjectAttribute> root = criteria.from(IdentityObjectAttribute.class);
            
                    final List<Predicate> predicates = new ArrayList<Predicate>();
            
                    predicates.add(builder.equal(root.get(IdentityObjectAttribute_.identityObject), lookupIdentity(identityObject, em)));
                    predicates.add(builder.equal(root.get(IdentityObjectAttribute_.name), SaltAttribute));
            
                    criteria.where(predicates.toArray(new Predicate[0]));
            
                    final List<IdentityObjectAttribute> results = em.createQuery(criteria).getResultList();
            
                    return results.size() > 0 ? results.get(0).getValue() : "";
                }
            
                /**
                 * From a password and a salt returns the corresponding digest
                 * @param password String The password to encrypt
                 * @param salt byte[] The salt
                 * @return byte[] The digested password
                 * @throws IdentityException If the hash algorithm doesn't exist or
                 * the default encoding doesn't exist
                 */
                private byte[] getHash(String password, byte[] salt) throws IdentityException {
                    try {
                        final MessageDigest digest = MessageDigest.getInstance(HashAlgorithm);
            
                        digest.reset();
                        digest.update(salt);
            
                        return digest.digest(password.getBytes(DefaultEncoding));
                    }
                    catch (NoSuchAlgorithmException ex) {
                        throw new IdentityException(ex);
                    }
                    catch (UnsupportedEncodingException ex) {
                        throw new IdentityException(ex);
                    }
                }
            }
            
            

             

            So that the new identity store is used in place of the default, you need to tell the identity store config to use it. I am doing that as follows:

             

            public class Initializer {
            
                @Inject
                private JpaIdentityStoreConfiguration identityStoreConfig;
            
                public void initialize(@Observes @Initialized WebApplication webapp) {
                    identityStoreConfig.setIdentityStoreClass(JpaIdentityStoreWithHashing.class);
                }
            }
            
            

             

            Other than than that the configuration etc from the seam security documentation stays the same.

             

            For completeness the following script will create a user with a username/password combination of sysadmin/password123.

             

            insert into identity_object_role_type
            (id, name)
            values (1, 'sysadmin');
            
            insert into identity_object_type
            (id, name)
            values (1, 'USER');
            
            insert into identity_object_type
            (id, name)
            VALUES (2, 'GROUP');
            
            insert into identity_object
            (id, name, identity_object_type_id)
            VALUES (1, 'sysadmin', 1);
            
            insert into identity_object_attribute
            (attributeId, name, value, identity_object_id)
            VALUES (1, 'SALT', 'VGhpcy1Jcy1NeS1TYWx0
            ', 1);
            
            insert into identity_object
            (id, name, identity_object_type_id)
            VALUES (2, 'system', 2);
            
            insert into identity_object_credential_type
            (id, name)
            VALUES (1, 'PASSWORD');
            
            insert into identity_object_credential
            (id, value, identity_object_id, credential_type_id)
            VALUES (1, 'IPuynTwsY0vMJui1kamAsdwI33i+//hB3n7T+F1NhfU=
            ', 1, 1);
            
            insert into identity_object_relationship_type
            (id, name)
            VALUES (1, 'JBOSS_IDENTITY_ROLE');
            
            insert into identity_object_relationship
            (id, name, from_identity_id, relationship_type_id, to_identity_id)
            VALUES (1, 'sysadmin', 2, 1, 1);