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.ldap.ByteSequence; 024import org.forgerock.opendj.ldap.ByteString; 025import org.forgerock.opendj.ldap.ResultCode; 026import org.opends.server.admin.server.ConfigurationChangeListener; 027import org.opends.server.admin.std.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 private static final String CLASS_NAME = BcryptPasswordStorageScheme.class.getName();; 050 /** The current configuration for this storage scheme. */ 051 private volatile BcryptPasswordStorageSchemeCfg config; 052 053 /** 054 * Creates a new instance of this password storage scheme. Note that no 055 * initialization should be performed here, as all initialization should be 056 * done in the {@link #initializePasswordStorageScheme(BcryptPasswordStorageSchemeCfg)} method. 057 */ 058 public BcryptPasswordStorageScheme() 059 { 060 } 061 062 063 @Override 064 public void initializePasswordStorageScheme(BcryptPasswordStorageSchemeCfg configuration) 065 throws ConfigException, InitializationException 066 { 067 this.config = configuration; 068 config.addBcryptChangeListener(this); 069 } 070 071 072 @Override 073 public String getStorageSchemeName() 074 { 075 return STORAGE_SCHEME_NAME_BCRYPT; 076 } 077 078 079 @Override 080 public boolean isConfigurationChangeAcceptable(BcryptPasswordStorageSchemeCfg configuration, 081 List<LocalizableMessage> unacceptableReasons) 082 { 083 return true; 084 } 085 086 087 @Override 088 public ConfigChangeResult applyConfigurationChange(BcryptPasswordStorageSchemeCfg configuration) 089 { 090 this.config = configuration; 091 return new ConfigChangeResult(); 092 } 093 094 095 @Override 096 public ByteString encodePassword(ByteSequence plaintext) 097 throws DirectoryException 098 { 099 String salt = BCrypt.gensalt(config.getBcryptCost()); 100 String hashed_password = BCrypt.hashpw(plaintext.toByteArray(), salt); 101 return ByteString.valueOfUtf8(hashed_password); 102 } 103 104 105 @Override 106 public ByteString encodePasswordWithScheme(ByteSequence plaintext) 107 throws DirectoryException 108 { 109 return ByteString.valueOfUtf8('{' + getStorageSchemeName() + '}' + encodePassword(plaintext)); 110 } 111 112 113 @Override 114 public boolean passwordMatches(ByteSequence plaintextPassword, 115 ByteSequence storedPassword) 116 { 117 try 118 { 119 return BCrypt.checkpw(plaintextPassword.toString(), storedPassword.toString()); 120 } 121 catch (IllegalArgumentException e) 122 { 123 logger.traceException(e); 124 logger.error(ERR_PWSCHEME_INVALID_STORED_PASSWORD, e); 125 return false; 126 } 127 } 128 129 130 @Override 131 public boolean isReversible() 132 { 133 return false; 134 } 135 136 137 @Override 138 public ByteString getPlaintextValue(ByteSequence storedPassword) 139 throws DirectoryException 140 { 141 LocalizableMessage message = ERR_PWSCHEME_NOT_REVERSIBLE.get(getStorageSchemeName()); 142 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 143 } 144 145 146 @Override 147 public boolean supportsAuthPasswordSyntax() 148 { 149 return false; 150 } 151 152 153 @Override 154 public ByteString encodeAuthPassword(ByteSequence plaintext) 155 throws DirectoryException 156 { 157 LocalizableMessage message = 158 ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName()); 159 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 160 } 161 162 163 @Override 164 public boolean authPasswordMatches(ByteSequence plaintextPassword, 165 String authInfo, String authValue) 166 { 167 return false; 168 } 169 170 171 @Override 172 public ByteString getAuthPasswordPlaintextValue(String authInfo, 173 String authValue) 174 throws DirectoryException 175 { 176 LocalizableMessage message = 177 ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName()); 178 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 179 } 180 181 182 @Override 183 public boolean isStorageSchemeSecure() 184 { 185 return true; 186 } 187} 188