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