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