001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2015-2016 ForgeRock AS. 015 */ 016package org.opends.server.extensions; 017 018 019import org.forgerock.i18n.LocalizableMessage; 020import org.forgerock.i18n.slf4j.LocalizedLogger; 021import org.forgerock.opendj.config.server.ConfigChangeResult; 022import org.forgerock.opendj.config.server.ConfigException; 023import org.forgerock.opendj.config.server.ConfigurationChangeListener; 024import org.forgerock.opendj.ldap.ByteSequence; 025import org.forgerock.opendj.ldap.ByteString; 026import org.forgerock.opendj.ldap.ResultCode; 027import org.forgerock.opendj.server.config.server.BcryptPasswordStorageSchemeCfg; 028import org.opends.server.api.PasswordStorageScheme; 029import org.opends.server.types.DirectoryException; 030import org.opends.server.types.InitializationException; 031 032import java.util.List; 033 034import static org.opends.messages.ExtensionMessages.*; 035import static org.opends.server.extensions.ExtensionsConstants.*; 036 037 038/** 039 * This class defines a Directory Server password storage scheme that will 040 * encode values using the Blowfish reversible encryption algorithm. This 041 * implementation supports only the user password syntax and not the auth 042 * password syntax. 043 */ 044public class BcryptPasswordStorageScheme 045 extends PasswordStorageScheme<BcryptPasswordStorageSchemeCfg> 046 implements ConfigurationChangeListener<BcryptPasswordStorageSchemeCfg> 047{ 048 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 049 /** The current configuration for this storage scheme. */ 050 private volatile BcryptPasswordStorageSchemeCfg config; 051 052 /** 053 * Creates a new instance of this password storage scheme. Note that no 054 * initialization should be performed here, as all initialization should be 055 * done in the {@link #initializePasswordStorageScheme(BcryptPasswordStorageSchemeCfg)} method. 056 */ 057 public BcryptPasswordStorageScheme() 058 { 059 } 060 061 062 @Override 063 public void initializePasswordStorageScheme(BcryptPasswordStorageSchemeCfg configuration) 064 throws ConfigException, InitializationException 065 { 066 this.config = configuration; 067 config.addBcryptChangeListener(this); 068 } 069 070 071 @Override 072 public String getStorageSchemeName() 073 { 074 return STORAGE_SCHEME_NAME_BCRYPT; 075 } 076 077 078 @Override 079 public boolean isConfigurationChangeAcceptable(BcryptPasswordStorageSchemeCfg configuration, 080 List<LocalizableMessage> unacceptableReasons) 081 { 082 return true; 083 } 084 085 086 @Override 087 public ConfigChangeResult applyConfigurationChange(BcryptPasswordStorageSchemeCfg configuration) 088 { 089 this.config = configuration; 090 return new ConfigChangeResult(); 091 } 092 093 094 @Override 095 public ByteString encodePassword(ByteSequence plaintext) 096 throws DirectoryException 097 { 098 String salt = BCrypt.gensalt(config.getBcryptCost()); 099 String hashed_password = BCrypt.hashpw(plaintext.toByteArray(), salt); 100 return ByteString.valueOfUtf8(hashed_password); 101 } 102 103 104 @Override 105 public ByteString encodePasswordWithScheme(ByteSequence plaintext) 106 throws DirectoryException 107 { 108 return ByteString.valueOfUtf8('{' + getStorageSchemeName() + '}' + encodePassword(plaintext)); 109 } 110 111 112 @Override 113 public boolean passwordMatches(ByteSequence plaintextPassword, 114 ByteSequence storedPassword) 115 { 116 try 117 { 118 return BCrypt.checkpw(plaintextPassword.toString(), storedPassword.toString()); 119 } 120 catch (IllegalArgumentException e) 121 { 122 logger.traceException(e); 123 logger.error(ERR_PWSCHEME_INVALID_STORED_PASSWORD, e); 124 return false; 125 } 126 } 127 128 129 @Override 130 public boolean isReversible() 131 { 132 return false; 133 } 134 135 136 @Override 137 public ByteString getPlaintextValue(ByteSequence storedPassword) 138 throws DirectoryException 139 { 140 LocalizableMessage message = ERR_PWSCHEME_NOT_REVERSIBLE.get(getStorageSchemeName()); 141 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 142 } 143 144 145 @Override 146 public boolean supportsAuthPasswordSyntax() 147 { 148 return false; 149 } 150 151 152 @Override 153 public ByteString encodeAuthPassword(ByteSequence plaintext) 154 throws DirectoryException 155 { 156 LocalizableMessage message = 157 ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName()); 158 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 159 } 160 161 162 @Override 163 public boolean authPasswordMatches(ByteSequence plaintextPassword, 164 String authInfo, String authValue) 165 { 166 return false; 167 } 168 169 170 @Override 171 public ByteString getAuthPasswordPlaintextValue(String authInfo, 172 String authValue) 173 throws DirectoryException 174 { 175 LocalizableMessage message = 176 ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName()); 177 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 178 } 179 180 181 @Override 182 public boolean isStorageSchemeSecure() 183 { 184 return true; 185 } 186} 187