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