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