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