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