/** * ============================================================================= * * ORCID (R) Open Source * http://orcid.org * * Copyright (c) 2012-2014 ORCID, Inc. * Licensed under an MIT-Style License (MIT) * http://orcid.org/open-source-license * * This copyright and license information (including a link to the full license) * shall be included in its entirety in all copies or substantial portion of * the software. * * ============================================================================= */ package org.orcid.core.manager.impl; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.apache.commons.codec.binary.Hex; import org.apache.commons.lang3.time.StopWatch; import org.jasypt.digest.StringDigester; import org.jasypt.encryption.pbe.PBEStringEncryptor; import org.jasypt.exceptions.EncryptionOperationNotPossibleException; import org.orcid.core.crypto.DesEncrypter; import org.orcid.core.manager.EncryptionManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Required; import org.springframework.security.authentication.encoding.PasswordEncoder; public class EncryptionManagerImpl implements EncryptionManager, PasswordEncoder, InitializingBean { private String passPhraseForInternalEncryption; private int iterationCountForInternalEncyrption; private PBEStringEncryptor internalEncryptor; private PBEStringEncryptor externalEncryptor; private PBEStringEncryptor legacyExternalEncryptor; private DesEncrypter legacyEncrypterForInternalUse; private StringDigester passwordEncryptor; private static final Logger LOGGER = LoggerFactory.getLogger(EncryptionManagerImpl.class); @Deprecated public void setPassPhraseForInternalEncryption(String passPhraseForInternalEncryption) { this.passPhraseForInternalEncryption = passPhraseForInternalEncryption; } @Deprecated public void setIterationCountForInternalEncryption(int iterationCountForInternalEncyrption) { this.iterationCountForInternalEncyrption = iterationCountForInternalEncyrption; } @Required public void setInternalEncryptor(PBEStringEncryptor internalEncryptor) { this.internalEncryptor = internalEncryptor; } @Required public void setLegacyExternalEncryptor(PBEStringEncryptor legacyExternalEncryptor) { this.legacyExternalEncryptor = legacyExternalEncryptor; } @Required public void setExternalEncryptor(PBEStringEncryptor externalEncryptor) { this.externalEncryptor = externalEncryptor; } @Required public void setPasswordEncryptor(StringDigester passwordEncryptor) { this.passwordEncryptor = passwordEncryptor; } @Override public void afterPropertiesSet() { legacyEncrypterForInternalUse = new DesEncrypter(passPhraseForInternalEncryption, iterationCountForInternalEncyrption); } @Override public String encryptForInternalUse(String stringToEncrypt) { return internalEncryptor.encrypt(stringToEncrypt); } @Override public String decryptForInternalUse(String stringToDecrypt) { return internalEncryptor.decrypt(stringToDecrypt); } @Override @Deprecated public String legacyEncryptForInternalUse(String stringToEncrypt) { return legacyEncrypterForInternalUse.encrypt(stringToEncrypt); } @Override @Deprecated public String legacyDecryptForInternalUse(String stringToDecrypt) { return legacyEncrypterForInternalUse.decrypt(stringToDecrypt); } @Override public String hashForInternalUse(String raw) { return passwordEncryptor.digest(raw); } @Override public boolean hashMatches(String raw, String hash) { return passwordEncryptor.matches(raw, hash); } @Override public String encryptForExternalUse(String stringToEncrypt) { return externalEncryptor.encrypt(stringToEncrypt); } @Override public String decryptForExternalUse(String stringToDecrypt) { try { return externalEncryptor.decrypt(stringToDecrypt); } catch (EncryptionOperationNotPossibleException e) { return decryptForLegacyExternalUse(stringToDecrypt); } } @Override @Deprecated public String encryptForLegacyExternalUse(String stringToEncrypt) { return legacyExternalEncryptor.encrypt(stringToEncrypt); } @Override @Deprecated public String decryptForLegacyExternalUse(String stringToDecrypt) { return legacyExternalEncryptor.decrypt(stringToDecrypt); } /** * <p> * Encodes the specified raw password with an implementation specific * algorithm. * </p> * <P> * This will generally be a one-way message digest such as MD5 or SHA, but * may also be a plaintext variant which does no encoding at all, but rather * returns the same password it was fed. The latter is useful to plug in * when the original password must be stored as-is. * </p> * <p> * The specified salt will potentially be used by the implementation to * "salt" the initial value before encoding. A salt is usually a * user-specific value which is added to the password before the digest is * computed. This means that computation of digests for common dictionary * words will be different than those in the backend store, because the * dictionary word digests will not reflect the addition of the salt. If a * per-user salt is used (rather than a system-wide salt), it also means * users with the same password will have different digest encoded passwords * in the backend store. * </p> * <P> * If a salt value is provided, the same salt value must be use when calling * the {@link #isPasswordValid(String, String, Object)} method. Note that a * specific implementation may choose to ignore the salt value (via * <code>null</code>), or provide its own. * </p> * * @param rawPass * the password to encode * @param salt * optionally used by the implementation to "salt" the raw * password before encoding. A <code>null</code> value is legal. * @return encoded password */ @Override public String encodePassword(String rawPass, Object salt) { return hashForInternalUse(rawPass); } /** * <p> * Validates a specified "raw" password against an encoded password. * </p> * <P> * The encoded password should have previously been generated by * {@link #encodePassword(String, Object)}. This method will encode the * <code>rawPass</code> (using the optional <code>salt</code>), and then * compared it with the presented <code>encPass</code>. * </p> * <p> * For a discussion of salts, please refer to * {@link #encodePassword(String, Object)}. * </p> * * @param encPass * a pre-encoded password * @param rawPass * a raw password to encode and compare against the pre-encoded * password * @param salt * optionally used by the implementation to "salt" the raw * password before encoding. A <code>null</code> value is legal. * @return true if the password is valid , false otherwise */ @Override public boolean isPasswordValid(String encPass, String rawPass, Object salt) { LOGGER.debug("About to start password check"); StopWatch stopWatch = new StopWatch(); stopWatch.start(); boolean result = hashMatches(rawPass, encPass); stopWatch.stop(); LOGGER.debug("Password check took {} ms", stopWatch.getTime()); return result; } @Override public String sha256Hash(String s) throws NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(s.getBytes()); return Hex.encodeHexString(md.digest()); } }