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.AESPasswordStorageSchemeCfg;
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 AES reversible encryption algorithm.  This
044 * implementation supports only the user password syntax and not the auth
045 * password syntax.
046 */
047public class AESPasswordStorageScheme
048       extends PasswordStorageScheme<AESPasswordStorageSchemeCfg>
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 AESPasswordStorageScheme()
068  {
069    super();
070  }
071
072  /** {@inheritDoc} */
073  @Override
074  public void initializePasswordStorageScheme(
075                   AESPasswordStorageSchemeCfg configuration)
076         throws ConfigException, InitializationException
077  {
078    cryptoManager = DirectoryServer.getCryptoManager();
079  }
080
081  /** {@inheritDoc} */
082  @Override
083  public String getStorageSchemeName()
084  {
085    return STORAGE_SCHEME_NAME_AES;
086  }
087
088  /** {@inheritDoc} */
089  @Override
090  public ByteString encodePassword(ByteSequence plaintext)
091         throws DirectoryException
092  {
093    byte[] plaintextBytes = null;
094    try
095    {
096      // TODO: Can we avoid this copy?
097      plaintextBytes = plaintext.toByteArray();
098      byte[] encodedBytes = cryptoManager.encrypt(CIPHER_TRANSFORMATION_AES,
099                                                  KEY_SIZE_AES,
100                                                  plaintextBytes);
101      return ByteString.valueOfUtf8(Base64.encode(encodedBytes));
102    }
103    catch (Exception e)
104    {
105      logger.traceException(e);
106
107      LocalizableMessage m = ERR_PWSCHEME_CANNOT_ENCRYPT.get(STORAGE_SCHEME_NAME_AES,
108                                                  getExceptionMessage(e));
109      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
110                                   m, e);
111    }
112    finally
113    {
114      if (plaintextBytes != null)
115      {
116        Arrays.fill(plaintextBytes, (byte) 0);
117      }
118    }
119  }
120
121  /** {@inheritDoc} */
122  @Override
123  public ByteString encodePasswordWithScheme(ByteSequence plaintext)
124         throws DirectoryException
125  {
126    StringBuilder buffer = new StringBuilder();
127    buffer.append('{');
128    buffer.append(STORAGE_SCHEME_NAME_AES);
129    buffer.append('}');
130    byte[] plaintextBytes = null;
131    try
132    {
133      // TODO: Can we avoid this copy?
134      plaintextBytes = plaintext.toByteArray();
135      byte[] encodedBytes = cryptoManager.encrypt(CIPHER_TRANSFORMATION_AES,
136                                                  KEY_SIZE_AES,
137                                                  plaintextBytes);
138      buffer.append(Base64.encode(encodedBytes));
139    }
140    catch (Exception e)
141    {
142      logger.traceException(e);
143
144      LocalizableMessage m = ERR_PWSCHEME_CANNOT_ENCRYPT.get(STORAGE_SCHEME_NAME_AES,
145                                                  getExceptionMessage(e));
146      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
147                                   m, e);
148    }
149    finally
150    {
151      if (plaintextBytes != null)
152      {
153        Arrays.fill(plaintextBytes, (byte) 0);
154      }
155    }
156
157    return ByteString.valueOfUtf8(buffer);
158  }
159
160  /** {@inheritDoc} */
161  @Override
162  public boolean passwordMatches(ByteSequence plaintextPassword,
163                                 ByteSequence storedPassword)
164  {
165    try
166    {
167      ByteString decryptedPassword =
168          ByteString.wrap(cryptoManager.decrypt(
169               Base64.decode(storedPassword.toString())));
170      return plaintextPassword.equals(decryptedPassword);
171    }
172    catch (Exception e)
173    {
174      logger.traceException(e);
175
176      return false;
177    }
178  }
179
180  /** {@inheritDoc} */
181  @Override
182  public boolean isReversible()
183  {
184    return true;
185  }
186
187  /** {@inheritDoc} */
188  @Override
189  public ByteString getPlaintextValue(ByteSequence storedPassword)
190         throws DirectoryException
191  {
192    try
193    {
194      byte[] decryptedPassword =
195           cryptoManager.decrypt(Base64.decode(storedPassword.toString()));
196      return ByteString.wrap(decryptedPassword);
197    }
198    catch (Exception e)
199    {
200      logger.traceException(e);
201
202      LocalizableMessage m = ERR_PWSCHEME_CANNOT_DECRYPT.get(STORAGE_SCHEME_NAME_AES,
203                                                  getExceptionMessage(e));
204      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
205                                   m, e);
206    }
207  }
208
209  /** {@inheritDoc} */
210  @Override
211  public boolean supportsAuthPasswordSyntax()
212  {
213    // This storage scheme does not support the authentication password syntax.
214    return false;
215  }
216
217  /** {@inheritDoc} */
218  @Override
219  public ByteString encodeAuthPassword(ByteSequence plaintext)
220         throws DirectoryException
221  {
222    LocalizableMessage message =
223        ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName());
224    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
225  }
226
227  /** {@inheritDoc} */
228  @Override
229  public boolean authPasswordMatches(ByteSequence plaintextPassword,
230                                     String authInfo, String authValue)
231  {
232    // This storage scheme does not support the authentication password syntax.
233    return false;
234  }
235
236  /** {@inheritDoc} */
237  @Override
238  public ByteString getAuthPasswordPlaintextValue(String authInfo,
239                                                  String authValue)
240         throws DirectoryException
241  {
242    LocalizableMessage message =
243        ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName());
244    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
245  }
246
247  /** {@inheritDoc} */
248  @Override
249  public boolean isStorageSchemeSecure()
250  {
251    // This password storage scheme should be considered secure.
252    return true;
253  }
254}
255