001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008 Sun Microsystems, Inc.
025 *      Portions Copyright 2013-2015 ForgeRock AS
026 */
027package org.opends.server.extensions;
028
029
030
031import org.forgerock.i18n.LocalizableMessage;
032import org.opends.server.admin.std.server.BlowfishPasswordStorageSchemeCfg;
033import org.opends.server.api.PasswordStorageScheme;
034import org.forgerock.opendj.config.server.ConfigException;
035import org.opends.server.core.DirectoryServer;
036import org.forgerock.i18n.slf4j.LocalizedLogger;
037import org.opends.server.types.*;
038import org.forgerock.opendj.ldap.ResultCode;
039import org.forgerock.opendj.ldap.ByteString;
040import org.forgerock.opendj.ldap.ByteSequence;
041import org.opends.server.util.Base64;
042
043import java.util.Arrays;
044
045import static org.opends.messages.ExtensionMessages.*;
046import static org.opends.server.extensions.ExtensionsConstants.*;
047import static org.opends.server.util.StaticUtils.*;
048
049
050
051/**
052 * This class defines a Directory Server password storage scheme that will
053 * encode values using the Blowfish reversible encryption algorithm.  This
054 * implementation supports only the user password syntax and not the auth
055 * password syntax.
056 */
057public class BlowfishPasswordStorageScheme
058       extends PasswordStorageScheme<BlowfishPasswordStorageSchemeCfg>
059{
060  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
061
062
063
064  /**
065   * The reference to the Directory Server crypto manager that we will use to
066   * handle the encryption/decryption.
067   */
068  private CryptoManager cryptoManager;
069
070
071
072  /**
073   * Creates a new instance of this password storage scheme.  Note that no
074   * initialization should be performed here, as all initialization should be
075   * done in the {@code initializePasswordStorageScheme} method.
076   */
077  public BlowfishPasswordStorageScheme()
078  {
079    super();
080  }
081
082
083
084  /** {@inheritDoc} */
085  @Override
086  public void initializePasswordStorageScheme(
087                   BlowfishPasswordStorageSchemeCfg configuration)
088         throws ConfigException, InitializationException
089  {
090    cryptoManager = DirectoryServer.getCryptoManager();
091  }
092
093
094
095  /** {@inheritDoc} */
096  @Override
097  public String getStorageSchemeName()
098  {
099    return STORAGE_SCHEME_NAME_BLOWFISH;
100  }
101
102
103
104  /** {@inheritDoc} */
105  @Override
106  public ByteString encodePassword(ByteSequence plaintext)
107         throws DirectoryException
108  {
109    byte[] plaintextBytes = null;
110    try
111    {
112      // TODO: Can we avoid this copy?
113      plaintextBytes = plaintext.toByteArray();
114      byte[] encodedBytes =
115           cryptoManager.encrypt(CIPHER_TRANSFORMATION_BLOWFISH,
116                                 KEY_SIZE_BLOWFISH, plaintextBytes);
117      return ByteString.valueOfUtf8(Base64.encode(encodedBytes));
118    }
119    catch (Exception e)
120    {
121      logger.traceException(e);
122
123      LocalizableMessage m = ERR_PWSCHEME_CANNOT_ENCRYPT.get(STORAGE_SCHEME_NAME_BLOWFISH,
124                                                  getExceptionMessage(e));
125      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
126                                   m, e);
127    }
128    finally
129    {
130      if (plaintextBytes != null)
131      {
132        Arrays.fill(plaintextBytes, (byte) 0);
133      }
134    }
135  }
136
137
138
139  /** {@inheritDoc} */
140  @Override
141  public ByteString encodePasswordWithScheme(ByteSequence plaintext)
142         throws DirectoryException
143  {
144    StringBuilder buffer = new StringBuilder();
145    buffer.append('{');
146    buffer.append(STORAGE_SCHEME_NAME_BLOWFISH);
147    buffer.append('}');
148    byte[] plaintextBytes = null;
149
150    try
151    {
152      // TODO: Can we avoid this copy?
153      plaintextBytes = plaintext.toByteArray();
154      byte[] encodedBytes =
155           cryptoManager.encrypt(CIPHER_TRANSFORMATION_BLOWFISH,
156                                 KEY_SIZE_BLOWFISH, plaintextBytes);
157      buffer.append(Base64.encode(encodedBytes));
158    }
159    catch (Exception e)
160    {
161      logger.traceException(e);
162
163      LocalizableMessage m = ERR_PWSCHEME_CANNOT_ENCRYPT.get(STORAGE_SCHEME_NAME_BLOWFISH,
164                                                  getExceptionMessage(e));
165      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
166                                   m, e);
167    }
168    finally
169    {
170      if (plaintextBytes != null)
171      {
172        Arrays.fill(plaintextBytes, (byte) 0);
173      }
174    }
175
176    return ByteString.valueOfUtf8(buffer);
177  }
178
179
180
181  /** {@inheritDoc} */
182  @Override
183  public boolean passwordMatches(ByteSequence plaintextPassword,
184                                 ByteSequence storedPassword)
185  {
186    try
187    {
188      ByteString decryptedPassword =
189          ByteString.wrap(cryptoManager.decrypt(
190               Base64.decode(storedPassword.toString())));
191      return plaintextPassword.equals(decryptedPassword);
192    }
193    catch (Exception e)
194    {
195      logger.traceException(e);
196
197      return false;
198    }
199  }
200
201
202
203  /** {@inheritDoc} */
204  @Override
205  public boolean isReversible()
206  {
207    return true;
208  }
209
210
211
212  /** {@inheritDoc} */
213  @Override
214  public ByteString getPlaintextValue(ByteSequence storedPassword)
215         throws DirectoryException
216  {
217    try
218    {
219      byte[] decryptedPassword =
220           cryptoManager.decrypt(Base64.decode(storedPassword.toString()));
221      return ByteString.wrap(decryptedPassword);
222    }
223    catch (Exception e)
224    {
225      logger.traceException(e);
226
227      LocalizableMessage m = ERR_PWSCHEME_CANNOT_DECRYPT.get(STORAGE_SCHEME_NAME_BLOWFISH,
228                                                  getExceptionMessage(e));
229      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
230                                   m, e);
231    }
232  }
233
234
235
236  /** {@inheritDoc} */
237  @Override
238  public boolean supportsAuthPasswordSyntax()
239  {
240    // This storage scheme does not support the authentication password syntax.
241    return false;
242  }
243
244
245
246  /** {@inheritDoc} */
247  @Override
248  public ByteString encodeAuthPassword(ByteSequence plaintext)
249         throws DirectoryException
250  {
251    LocalizableMessage message =
252        ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName());
253    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
254  }
255
256
257
258  /** {@inheritDoc} */
259  @Override
260  public boolean authPasswordMatches(ByteSequence plaintextPassword,
261                                     String authInfo, String authValue)
262  {
263    // This storage scheme does not support the authentication password syntax.
264    return false;
265  }
266
267
268
269  /** {@inheritDoc} */
270  @Override
271  public ByteString getAuthPasswordPlaintextValue(String authInfo,
272                                                  String authValue)
273         throws DirectoryException
274  {
275    LocalizableMessage message =
276        ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName());
277    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
278  }
279
280
281
282  /** {@inheritDoc} */
283  @Override
284  public boolean isStorageSchemeSecure()
285  {
286    // This password storage scheme should be considered secure.
287    return true;
288  }
289}
290