diff --git a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/IdentityRecoveryConstants.java b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/IdentityRecoveryConstants.java index 3e7c7700fb..3f438671f8 100644 --- a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/IdentityRecoveryConstants.java +++ b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/IdentityRecoveryConstants.java @@ -304,6 +304,7 @@ public enum ErrorMessages { ERROR_CODE_ERROR_GETTING_CONNECTOR_CONFIG("20062", "Error while getting connector configurations"), ERROR_CODE_STORING_RECOVERY_FLOW_DATA("20063", "Error while storing recovery data."), ERROR_CODE_UPDATING_RECOVERY_FLOW_DATA("20064", "Error while updating recovery data."), + ERROR_CODE_NO_HASHING_ALGO_FOR_CODE("20065", "Error while hashing the code."), ERROR_CODE_ERROR_RETRIVING_CLAIM("18004", "Error when retrieving the locale claim of user '%s' of '%s' domain."), ERROR_CODE_RECOVERY_DATA_NOT_FOUND_FOR_USER("18005", "Recovery data not found."), diff --git a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/confirmation/ResendConfirmationManager.java b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/confirmation/ResendConfirmationManager.java index 65b185ab2c..248746a5d4 100644 --- a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/confirmation/ResendConfirmationManager.java +++ b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/confirmation/ResendConfirmationManager.java @@ -50,6 +50,7 @@ import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; +import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.UUID; @@ -169,6 +170,7 @@ public ResendConfirmationDTO resendConfirmation(String tenantDomain, String rese String notificationChannel = validateNotificationChannel(userRecoveryData.getRemainingSetIds()); String confirmationCode; + String hashedConfirmationCode; UserRecoveryData confirmationCodeRecoveryData = userRecoveryDataStore.loadWithoutCodeExpiryValidation(user, scenario, step); /* Checking whether the existing confirmation code can be used based on the email confirmation code tolerance @@ -180,8 +182,14 @@ public ResendConfirmationDTO resendConfirmation(String tenantDomain, String rese confirmationCode = Utils.generateSecretKey(notificationChannel, user.getTenantDomain(), recoveryScenario); confirmationCode = Utils.concatRecoveryFlowIdWithSecretKey(recoveryFlowId, notificationChannel, confirmationCode); + try { + hashedConfirmationCode = Utils.hashCode(confirmationCode); + } catch (NoSuchAlgorithmException e) { + throw Utils.handleServerException( + IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_NO_HASHING_ALGO_FOR_CODE, null); + } // Store new confirmation code. - addRecoveryDataObject(confirmationCode, recoveryFlowId, notificationChannel, scenario, step, user); + addRecoveryDataObject(hashedConfirmationCode, recoveryFlowId, notificationChannel, scenario, step, user); } ResendConfirmationDTO resendConfirmationDTO = new ResendConfirmationDTO(); diff --git a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/internal/service/impl/password/PasswordRecoveryManagerImpl.java b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/internal/service/impl/password/PasswordRecoveryManagerImpl.java index da23fb6fc8..203d76d89c 100644 --- a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/internal/service/impl/password/PasswordRecoveryManagerImpl.java +++ b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/internal/service/impl/password/PasswordRecoveryManagerImpl.java @@ -55,6 +55,7 @@ import org.wso2.carbon.identity.user.functionality.mgt.exception.UserFunctionalityManagementException; import org.wso2.carbon.identity.user.functionality.mgt.model.FunctionalityLockStatus; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Map; import java.util.UUID; @@ -192,8 +193,18 @@ public PasswordResetCodeDTO confirm(String confirmationCode, String tenantDomain validateTenantDomain(tenantDomain); UserAccountRecoveryManager userAccountRecoveryManager = UserAccountRecoveryManager.getInstance(); // Get Recovery data. - UserRecoveryData userRecoveryData = userAccountRecoveryManager - .getUserRecoveryData(confirmationCode, RecoverySteps.UPDATE_PASSWORD); + UserRecoveryData userRecoveryData; + try { + String hashedConfirmationCode = Utils.hashCode(confirmationCode); + userRecoveryData = userAccountRecoveryManager + .getUserRecoveryData(hashedConfirmationCode, RecoverySteps.UPDATE_PASSWORD); + } catch (NoSuchAlgorithmException e) { + throw Utils.handleServerException( + IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_NO_HASHING_ALGO_FOR_CODE, null); + } catch (IdentityRecoveryException e) { + userRecoveryData = userAccountRecoveryManager + .getUserRecoveryData(confirmationCode, RecoverySteps.UPDATE_PASSWORD); + } if (!tenantDomain.equals(userRecoveryData.getUser().getTenantDomain())) { throw Utils.handleClientException( IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_USER_TENANT_DOMAIN_MISS_MATCH_WITH_CONTEXT, @@ -247,7 +258,15 @@ public PasswordResetCodeDTO confirm(String otp, String confirmationCode, String } String domainQualifiedName = IdentityUtil.addDomainToName(userRecoveryData.getUser().getUserName(), userRecoveryData.getUser().getUserStoreDomain()); - if (StringUtils.equals(code, userRecoveryData.getSecret())) { + String hashedCode; + try { + hashedCode = Utils.hashCode(code); + } catch (NoSuchAlgorithmException e) { + throw Utils.handleServerException( + IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_NO_HASHING_ALGO_FOR_CODE, null); + } + if (StringUtils.equals(hashedCode, userRecoveryData.getSecret()) || StringUtils.equals(code, + userRecoveryData.getSecret())) { if (log.isDebugEnabled()) { log.debug("Valid confirmation code for user: " + domainQualifiedName); } diff --git a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/password/NotificationPasswordRecoveryManager.java b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/password/NotificationPasswordRecoveryManager.java index 8117a42f80..196811355a 100644 --- a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/password/NotificationPasswordRecoveryManager.java +++ b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/password/NotificationPasswordRecoveryManager.java @@ -65,6 +65,7 @@ import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; +import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Map; @@ -251,6 +252,7 @@ private UserRecoveryData generateNewConfirmationCode(User user, String notificat throws IdentityRecoveryException { String recoveryFlowId = null; + String hashedSecretKey; UserRecoveryDataStore userRecoveryDataStore = JDBCRecoveryDataStore.getInstance(); UserRecoveryData userRecoveryData = userRecoveryDataStore.loadWithoutCodeExpiryValidation(user); if (userRecoveryData != null) { @@ -260,12 +262,22 @@ private UserRecoveryData generateNewConfirmationCode(User user, String notificat String secretKey = Utils.generateSecretKey(notificationChannel, user.getTenantDomain(), RecoveryScenarios.NOTIFICATION_BASED_PW_RECOVERY.name()); secretKey = Utils.concatRecoveryFlowIdWithSecretKey(recoveryFlowId, notificationChannel, secretKey); + try { + hashedSecretKey = Utils.hashCode(secretKey); + } catch (NoSuchAlgorithmException e) { + throw Utils.handleServerException( + IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_NO_HASHING_ALGO_FOR_CODE, null); + } UserRecoveryData recoveryDataDO = new UserRecoveryData(user, recoveryFlowId, secretKey, RecoveryScenarios.NOTIFICATION_BASED_PW_RECOVERY, RecoverySteps.UPDATE_PASSWORD); + UserRecoveryData hashedRecoveryDataDO = new UserRecoveryData(user, recoveryFlowId, hashedSecretKey, + RecoveryScenarios.NOTIFICATION_BASED_PW_RECOVERY, RecoverySteps.UPDATE_PASSWORD); + // Store the notified channel in the recovery object for future reference. recoveryDataDO.setRemainingSetIds(notificationChannel); - userRecoveryDataStore.storeConfirmationCode(recoveryDataDO); + hashedRecoveryDataDO.setRemainingSetIds(notificationChannel); + userRecoveryDataStore.storeConfirmationCode(hashedRecoveryDataDO); return recoveryDataDO; } @@ -570,7 +582,16 @@ public User updateUserPassword(String code, String password, Property[] properti UserRecoveryDataStore userRecoveryDataStore = JDBCRecoveryDataStore.getInstance(); - UserRecoveryData userRecoveryData = userRecoveryDataStore.load(code); + UserRecoveryData userRecoveryData; + try { + String hashedCode = Utils.hashCode(code); + userRecoveryData = userRecoveryDataStore.load(hashedCode); + } catch (NoSuchAlgorithmException e) { + throw Utils.handleServerException( + IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_NO_HASHING_ALGO_FOR_CODE, null); + } catch (IdentityRecoveryException e) { + userRecoveryData = userRecoveryDataStore.load(code); + } validateCallback(properties, userRecoveryData.getUser().getTenantDomain()); publishEvent(userRecoveryData.getUser(), null, code, password, properties, IdentityEventConstants.Event.PRE_ADD_NEW_PASSWORD, userRecoveryData); @@ -670,7 +691,15 @@ public User updateUserPassword(String code, String confirmationCode, String pass validateTenantDomain(userRecoveryData.getUser()); int failedAttempts = userRecoveryData.getFailedAttempts(); - if (!StringUtils.equals(code, userRecoveryData.getSecret())) { + String hashedCode; + try { + hashedCode = Utils.hashCode(code); + } catch (NoSuchAlgorithmException e) { + throw Utils.handleServerException( + IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_NO_HASHING_ALGO_FOR_CODE, null); + } + if (!(StringUtils.equals(hashedCode, userRecoveryData.getSecret()) || StringUtils.equals(code, + userRecoveryData.getSecret()))) { if ((failedAttempts + 1) >= Integer.parseInt(Utils.getRecoveryConfigs(IdentityRecoveryConstants. ConnectorConfig.RECOVERY_OTP_PASSWORD_MAX_FAILED_ATTEMPTS, userRecoveryData.getUser(). getTenantDomain()))) { @@ -1046,7 +1075,16 @@ public void validateConfirmationCode(String code, String recoveryStep) throws Id public User getValidatedUser(String code, String recoveryStep) throws IdentityRecoveryException { UserRecoveryDataStore userRecoveryDataStore = JDBCRecoveryDataStore.getInstance(); - UserRecoveryData userRecoveryData = userRecoveryDataStore.load(code); + UserRecoveryData userRecoveryData; + try { + String hashedCode = Utils.hashCode(code); + userRecoveryData = userRecoveryDataStore.load(hashedCode); + } catch (NoSuchAlgorithmException e) { + throw Utils.handleServerException( + IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_NO_HASHING_ALGO_FOR_CODE, null); + } catch (IdentityRecoveryException e) { + userRecoveryData = userRecoveryDataStore.load(code); + } String contextTenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); String userTenantDomain = userRecoveryData.getUser().getTenantDomain(); if (!StringUtils.equals(contextTenantDomain, userTenantDomain)) { diff --git a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/util/Utils.java b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/util/Utils.java index ac4d242ab2..36692ba562 100644 --- a/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/util/Utils.java +++ b/components/org.wso2.carbon.identity.recovery/src/main/java/org/wso2/carbon/identity/recovery/util/Utils.java @@ -414,19 +414,30 @@ public static IdentityRecoveryClientException handleClientException(String error * @return * @throws UserStoreException */ + @Deprecated public static String doHash(String value) throws UserStoreException { try { - String digsestFunction = "SHA-256"; - MessageDigest dgst = MessageDigest.getInstance(digsestFunction); - byte[] byteValue = dgst.digest(value.getBytes(StandardCharsets.UTF_8)); - return Base64.encode(byteValue); + return hashCode(value); } catch (NoSuchAlgorithmException e) { log.error(e.getMessage(), e); throw new UserStoreException(e.getMessage(), e); } } + /** + * @param value Value to be hashed + * @return Hashed value + * @throws NoSuchAlgorithmException If the algorithm is not found. + */ + public static String hashCode(String value) throws NoSuchAlgorithmException { + + String digsestFunction = "SHA-256"; + MessageDigest dgst = MessageDigest.getInstance(digsestFunction); + byte[] byteValue = dgst.digest(value.getBytes(StandardCharsets.UTF_8)); + return Base64.encode(byteValue); + } + /** * Set claim to user store manager *