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
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.extensions;
028
029
030
031 import java.security.MessageDigest;
032 import java.util.Arrays;
033
034 import org.opends.messages.Message;
035 import org.opends.server.admin.std.server.SHA1PasswordStorageSchemeCfg;
036 import org.opends.server.api.PasswordStorageScheme;
037 import org.opends.server.config.ConfigException;
038 import org.opends.server.core.DirectoryServer;
039 import org.opends.server.loggers.debug.DebugTracer;
040 import org.opends.server.types.ByteString;
041 import org.opends.server.types.ByteStringFactory;
042 import org.opends.server.types.DebugLogLevel;
043 import org.opends.server.types.DirectoryException;
044 import org.opends.server.types.InitializationException;
045 import org.opends.server.types.ResultCode;
046 import org.opends.server.util.Base64;
047
048 import static org.opends.messages.ExtensionMessages.*;
049 import static org.opends.server.extensions.ExtensionsConstants.*;
050 import static org.opends.server.loggers.ErrorLogger.*;
051 import static org.opends.server.loggers.debug.DebugLogger.*;
052 import static org.opends.server.util.StaticUtils.*;
053
054
055
056 /**
057 * This class defines a Directory Server password storage scheme based on the
058 * SHA-1 algorithm defined in FIPS 180-1. This is a one-way digest algorithm
059 * so there is no way to retrieve the original clear-text version of the
060 * password from the hashed value (although this means that it is not suitable
061 * for things that need the clear-text password like DIGEST-MD5). This
062 * implementation does not perform any salting, which means that it is more
063 * vulnerable to dictionary attacks than salted variants.
064 */
065 public class SHA1PasswordStorageScheme
066 extends PasswordStorageScheme<SHA1PasswordStorageSchemeCfg>
067 {
068 /**
069 * The tracer object for the debug logger.
070 */
071 private static final DebugTracer TRACER = getTracer();
072
073 /**
074 * The fully-qualified name of this class.
075 */
076 private static final String CLASS_NAME =
077 "org.opends.server.extensions.SHA1PasswordStorageScheme";
078
079
080
081 // The message digest that will actually be used to generate the SHA-1 hashes.
082 private MessageDigest messageDigest;
083
084 // The lock used to provide threadsafe access to the message digest.
085 private Object digestLock;
086
087
088
089 /**
090 * Creates a new instance of this password storage scheme. Note that no
091 * initialization should be performed here, as all initialization should be
092 * done in the <CODE>initializePasswordStorageScheme</CODE> method.
093 */
094 public SHA1PasswordStorageScheme()
095 {
096 super();
097 }
098
099
100
101 /**
102 * {@inheritDoc}
103 */
104 @Override()
105 public void initializePasswordStorageScheme(
106 SHA1PasswordStorageSchemeCfg configuration)
107 throws ConfigException, InitializationException
108 {
109 try
110 {
111 messageDigest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM_SHA_1);
112 }
113 catch (Exception e)
114 {
115 if (debugEnabled())
116 {
117 TRACER.debugCaught(DebugLogLevel.ERROR, e);
118 }
119
120 Message message = ERR_PWSCHEME_CANNOT_INITIALIZE_MESSAGE_DIGEST.get(
121 MESSAGE_DIGEST_ALGORITHM_SHA_1, String.valueOf(e));
122 throw new InitializationException(message, e);
123 }
124
125 digestLock = new Object();
126 }
127
128
129
130 /**
131 * {@inheritDoc}
132 */
133 @Override()
134 public String getStorageSchemeName()
135 {
136 return STORAGE_SCHEME_NAME_SHA_1;
137 }
138
139
140
141 /**
142 * {@inheritDoc}
143 */
144 @Override()
145 public ByteString encodePassword(ByteString plaintext)
146 throws DirectoryException
147 {
148 byte[] digestBytes;
149
150 synchronized (digestLock)
151 {
152 try
153 {
154 digestBytes = messageDigest.digest(plaintext.value());
155 }
156 catch (Exception e)
157 {
158 if (debugEnabled())
159 {
160 TRACER.debugCaught(DebugLogLevel.ERROR, e);
161 }
162
163 Message message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get(
164 CLASS_NAME, getExceptionMessage(e));
165 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
166 message, e);
167 }
168 }
169
170 return ByteStringFactory.create(Base64.encode(digestBytes));
171 }
172
173
174
175 /**
176 * {@inheritDoc}
177 */
178 @Override()
179 public ByteString encodePasswordWithScheme(ByteString plaintext)
180 throws DirectoryException
181 {
182 StringBuilder buffer = new StringBuilder();
183 buffer.append('{');
184 buffer.append(STORAGE_SCHEME_NAME_SHA_1);
185 buffer.append('}');
186
187 byte[] digestBytes;
188
189 synchronized (digestLock)
190 {
191 try
192 {
193 digestBytes = messageDigest.digest(plaintext.value());
194 }
195 catch (Exception e)
196 {
197 if (debugEnabled())
198 {
199 TRACER.debugCaught(DebugLogLevel.ERROR, e);
200 }
201
202 Message message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get(
203 CLASS_NAME, getExceptionMessage(e));
204 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
205 message, e);
206 }
207 }
208
209 buffer.append(Base64.encode(digestBytes));
210
211 return ByteStringFactory.create(buffer.toString());
212 }
213
214
215
216 /**
217 * {@inheritDoc}
218 */
219 @Override()
220 public boolean passwordMatches(ByteString plaintextPassword,
221 ByteString storedPassword)
222 {
223 byte[] userPWDigestBytes;
224
225 synchronized (digestLock)
226 {
227 try
228 {
229 userPWDigestBytes = messageDigest.digest(plaintextPassword.value());
230 }
231 catch (Exception e)
232 {
233 if (debugEnabled())
234 {
235 TRACER.debugCaught(DebugLogLevel.ERROR, e);
236 }
237
238 return false;
239 }
240 }
241
242 byte[] storedPWDigestBytes;
243 try
244 {
245 storedPWDigestBytes = Base64.decode(storedPassword.stringValue());
246 }
247 catch (Exception e)
248 {
249 if (debugEnabled())
250 {
251 TRACER.debugCaught(DebugLogLevel.ERROR, e);
252 }
253
254 logError(ERR_PWSCHEME_CANNOT_BASE64_DECODE_STORED_PASSWORD.get(
255 storedPassword.stringValue(), String.valueOf(e)));
256
257 return false;
258 }
259
260 return Arrays.equals(userPWDigestBytes, storedPWDigestBytes);
261 }
262
263
264
265 /**
266 * {@inheritDoc}
267 */
268 @Override()
269 public boolean supportsAuthPasswordSyntax()
270 {
271 // This storage scheme does not support the authentication password syntax.
272 return false;
273 }
274
275
276
277 /**
278 * {@inheritDoc}
279 */
280 @Override()
281 public ByteString encodeAuthPassword(ByteString plaintext)
282 throws DirectoryException
283 {
284 Message message =
285 ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName());
286 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
287 }
288
289
290
291 /**
292 * {@inheritDoc}
293 */
294 @Override()
295 public boolean authPasswordMatches(ByteString plaintextPassword,
296 String authInfo, String authValue)
297 {
298 // This storage scheme does not support the authentication password syntax.
299 return false;
300 }
301
302
303
304 /**
305 * {@inheritDoc}
306 */
307 @Override()
308 public boolean isReversible()
309 {
310 return false;
311 }
312
313
314
315 /**
316 * {@inheritDoc}
317 */
318 @Override()
319 public ByteString getPlaintextValue(ByteString storedPassword)
320 throws DirectoryException
321 {
322 Message message =
323 ERR_PWSCHEME_NOT_REVERSIBLE.get(STORAGE_SCHEME_NAME_SHA_1);
324 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
325 }
326
327
328
329 /**
330 * {@inheritDoc}
331 */
332 @Override()
333 public ByteString getAuthPasswordPlaintextValue(String authInfo,
334 String authValue)
335 throws DirectoryException
336 {
337 Message message =
338 ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName());
339 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
340 }
341
342
343
344 /**
345 * {@inheritDoc}
346 */
347 @Override()
348 public boolean isStorageSchemeSecure()
349 {
350 // SHA-1 should be considered secure.
351 return true;
352 }
353 }
354