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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2008 Sun Microsystems, Inc. 025 * Portions Copyright 2013-2015 ForgeRock AS. 026 */ 027package org.opends.server.extensions; 028 029 030 031import org.forgerock.i18n.LocalizableMessage; 032import org.opends.server.admin.std.server.TripleDESPasswordStorageSchemeCfg; 033import org.opends.server.api.PasswordStorageScheme; 034import org.forgerock.opendj.config.server.ConfigException; 035import org.opends.server.core.DirectoryServer; 036import org.forgerock.i18n.slf4j.LocalizedLogger; 037import org.opends.server.types.*; 038import org.forgerock.opendj.ldap.ResultCode; 039import org.forgerock.opendj.ldap.ByteString; 040import org.forgerock.opendj.ldap.ByteSequence; 041import org.opends.server.util.Base64; 042 043import java.util.Arrays; 044 045import static org.opends.messages.ExtensionMessages.*; 046import static org.opends.server.extensions.ExtensionsConstants.*; 047import static org.opends.server.util.StaticUtils.*; 048 049 050 051/** 052 * This class defines a Directory Server password storage scheme that will 053 * encode values using the triple-DES (DES/EDE) reversible encryption algorithm. 054 * This implementation supports only the user password syntax and not the auth 055 * password syntax. 056 */ 057public class TripleDESPasswordStorageScheme 058 extends PasswordStorageScheme<TripleDESPasswordStorageSchemeCfg> 059{ 060 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 061 062 063 064 /** 065 * The reference to the Directory Server crypto manager that we will use to 066 * handle the encryption/decryption. 067 */ 068 private CryptoManager cryptoManager; 069 070 071 072 /** 073 * Creates a new instance of this password storage scheme. Note that no 074 * initialization should be performed here, as all initialization should be 075 * done in the {@code initializePasswordStorageScheme} method. 076 */ 077 public TripleDESPasswordStorageScheme() 078 { 079 super(); 080 } 081 082 083 084 /** {@inheritDoc} */ 085 @Override 086 public void initializePasswordStorageScheme( 087 TripleDESPasswordStorageSchemeCfg configuration) 088 throws ConfigException, InitializationException 089 { 090 cryptoManager = DirectoryServer.getCryptoManager(); 091 } 092 093 094 095 /** {@inheritDoc} */ 096 @Override 097 public String getStorageSchemeName() 098 { 099 return STORAGE_SCHEME_NAME_3DES; 100 } 101 102 103 104 /** {@inheritDoc} */ 105 @Override 106 public ByteString encodePassword(ByteSequence plaintext) 107 throws DirectoryException 108 { 109 byte[] plaintextBytes = null; 110 try 111 { 112 // TODO: Can we avoid this copy? 113 plaintextBytes = plaintext.toByteArray(); 114 byte[] encodedBytes = cryptoManager.encrypt(CIPHER_TRANSFORMATION_3DES, 115 KEY_SIZE_3DES, 116 plaintextBytes); 117 return ByteString.valueOfUtf8(Base64.encode(encodedBytes)); 118 } 119 catch (Exception e) 120 { 121 logger.traceException(e); 122 123 LocalizableMessage m = ERR_PWSCHEME_CANNOT_ENCRYPT.get(STORAGE_SCHEME_NAME_3DES, 124 getExceptionMessage(e)); 125 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 126 m, e); 127 } 128 finally 129 { 130 if (plaintextBytes != null) 131 { 132 Arrays.fill(plaintextBytes, (byte) 0); 133 } 134 } 135 } 136 137 138 139 /** {@inheritDoc} */ 140 @Override 141 public ByteString encodePasswordWithScheme(ByteSequence plaintext) 142 throws DirectoryException 143 { 144 StringBuilder buffer = new StringBuilder(); 145 buffer.append('{'); 146 buffer.append(STORAGE_SCHEME_NAME_3DES); 147 buffer.append('}'); 148 byte[] plaintextBytes = null; 149 150 try 151 { 152 // TODO: Can we avoid this copy? 153 plaintextBytes = plaintext.toByteArray(); 154 byte[] encodedBytes = cryptoManager.encrypt(CIPHER_TRANSFORMATION_3DES, 155 KEY_SIZE_3DES, 156 plaintextBytes); 157 buffer.append(Base64.encode(encodedBytes)); 158 } 159 catch (Exception e) 160 { 161 logger.traceException(e); 162 163 LocalizableMessage m = ERR_PWSCHEME_CANNOT_ENCRYPT.get(STORAGE_SCHEME_NAME_3DES, 164 getExceptionMessage(e)); 165 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 166 m, e); 167 } 168 finally 169 { 170 if (plaintextBytes != null) 171 { 172 Arrays.fill(plaintextBytes, (byte) 0); 173 } 174 } 175 176 return ByteString.valueOfUtf8(buffer); 177 } 178 179 180 181 /** {@inheritDoc} */ 182 @Override 183 public boolean passwordMatches(ByteSequence plaintextPassword, 184 ByteSequence storedPassword) 185 { 186 try 187 { 188 ByteString decryptedPassword = 189 ByteString.wrap(cryptoManager.decrypt( 190 Base64.decode(storedPassword.toString()))); 191 return plaintextPassword.equals(decryptedPassword); 192 } 193 catch (Exception e) 194 { 195 logger.traceException(e); 196 197 return false; 198 } 199 } 200 201 202 203 /** {@inheritDoc} */ 204 @Override 205 public boolean isReversible() 206 { 207 return true; 208 } 209 210 211 212 /** {@inheritDoc} */ 213 @Override 214 public ByteString getPlaintextValue(ByteSequence storedPassword) 215 throws DirectoryException 216 { 217 try 218 { 219 byte[] decryptedPassword = 220 cryptoManager.decrypt(Base64.decode(storedPassword.toString())); 221 return ByteString.wrap(decryptedPassword); 222 } 223 catch (Exception e) 224 { 225 logger.traceException(e); 226 227 LocalizableMessage m = ERR_PWSCHEME_CANNOT_DECRYPT.get(STORAGE_SCHEME_NAME_3DES, 228 getExceptionMessage(e)); 229 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 230 m, e); 231 } 232 } 233 234 235 236 /** {@inheritDoc} */ 237 @Override 238 public boolean supportsAuthPasswordSyntax() 239 { 240 // This storage scheme does not support the authentication password syntax. 241 return false; 242 } 243 244 245 246 /** {@inheritDoc} */ 247 @Override 248 public ByteString encodeAuthPassword(ByteSequence plaintext) 249 throws DirectoryException 250 { 251 LocalizableMessage message = 252 ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName()); 253 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 254 } 255 256 257 258 /** {@inheritDoc} */ 259 @Override 260 public boolean authPasswordMatches(ByteSequence plaintextPassword, 261 String authInfo, String authValue) 262 { 263 // This storage scheme does not support the authentication password syntax. 264 return false; 265 } 266 267 268 269 /** {@inheritDoc} */ 270 @Override 271 public ByteString getAuthPasswordPlaintextValue(String authInfo, 272 String authValue) 273 throws DirectoryException 274 { 275 LocalizableMessage message = 276 ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName()); 277 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 278 } 279 280 281 282 /** {@inheritDoc} */ 283 @Override 284 public boolean isStorageSchemeSecure() 285 { 286 // This password storage scheme should be considered secure. 287 return true; 288 } 289} 290