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-2016 ForgeRock AS. 016 */ 017package org.opends.server.extensions; 018 019import org.forgerock.i18n.LocalizableMessage; 020import org.forgerock.opendj.server.config.server.RC4PasswordStorageSchemeCfg; 021import org.opends.server.api.PasswordStorageScheme; 022import org.forgerock.opendj.config.server.ConfigException; 023import org.opends.server.core.DirectoryServer; 024import org.forgerock.i18n.slf4j.LocalizedLogger; 025import org.opends.server.types.*; 026import org.forgerock.opendj.ldap.ResultCode; 027import org.forgerock.opendj.ldap.ByteString; 028import org.forgerock.opendj.ldap.ByteSequence; 029import org.opends.server.util.Base64; 030 031import java.util.Arrays; 032 033import static org.opends.messages.ExtensionMessages.*; 034import static org.opends.server.extensions.ExtensionsConstants.*; 035import static org.opends.server.util.StaticUtils.*; 036 037/** 038 * This class defines a Directory Server password storage scheme that will 039 * encode values using the RC4 reversible encryption algorithm. This 040 * implementation supports only the user password syntax and not the auth 041 * password syntax. 042 */ 043public class RC4PasswordStorageScheme 044 extends PasswordStorageScheme<RC4PasswordStorageSchemeCfg> 045{ 046 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 047 048 /** 049 * The reference to the Directory Server crypto manager that we will use to 050 * handle the encryption/decryption. 051 */ 052 private CryptoManager cryptoManager; 053 054 /** 055 * Creates a new instance of this password storage scheme. Note that no 056 * initialization should be performed here, as all initialization should be 057 * done in the {@code initializePasswordStorageScheme} method. 058 */ 059 public RC4PasswordStorageScheme() 060 { 061 super(); 062 } 063 064 @Override 065 public void initializePasswordStorageScheme( 066 RC4PasswordStorageSchemeCfg configuration) 067 throws ConfigException, InitializationException 068 { 069 cryptoManager = DirectoryServer.getCryptoManager(); 070 } 071 072 @Override 073 public String getStorageSchemeName() 074 { 075 return STORAGE_SCHEME_NAME_RC4; 076 } 077 078 @Override 079 public ByteString encodePassword(ByteSequence plaintext) 080 throws DirectoryException 081 { 082 byte[] plaintextBytes = null; 083 try 084 { 085 // TODO: Can we avoid this copy? 086 plaintextBytes = plaintext.toByteArray(); 087 byte[] encodedBytes = cryptoManager.encrypt(CIPHER_TRANSFORMATION_RC4, 088 KEY_SIZE_RC4, 089 plaintextBytes); 090 return ByteString.valueOfUtf8(Base64.encode(encodedBytes)); 091 } 092 catch (Exception e) 093 { 094 logger.traceException(e); 095 096 LocalizableMessage m = ERR_PWSCHEME_CANNOT_ENCRYPT.get(STORAGE_SCHEME_NAME_RC4, 097 getExceptionMessage(e)); 098 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 099 m, e); 100 } 101 finally 102 { 103 if (plaintextBytes != null) 104 { 105 Arrays.fill(plaintextBytes, (byte) 0); 106 } 107 } 108 } 109 110 @Override 111 public ByteString encodePasswordWithScheme(ByteSequence plaintext) 112 throws DirectoryException 113 { 114 StringBuilder buffer = new StringBuilder(); 115 buffer.append('{'); 116 buffer.append(STORAGE_SCHEME_NAME_RC4); 117 buffer.append('}'); 118 byte[] plaintextBytes = null; 119 120 try 121 { 122 // TODO: Can we avoid this copy? 123 plaintextBytes = plaintext.toByteArray(); 124 byte[] encodedBytes = cryptoManager.encrypt(CIPHER_TRANSFORMATION_RC4, 125 KEY_SIZE_RC4, 126 plaintextBytes); 127 buffer.append(Base64.encode(encodedBytes)); 128 } 129 catch (Exception e) 130 { 131 logger.traceException(e); 132 133 LocalizableMessage m = ERR_PWSCHEME_CANNOT_ENCRYPT.get(STORAGE_SCHEME_NAME_RC4, 134 getExceptionMessage(e)); 135 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 136 m, e); 137 } 138 finally 139 { 140 if (plaintextBytes != null) 141 { 142 Arrays.fill(plaintextBytes, (byte) 0); 143 } 144 } 145 146 return ByteString.valueOfUtf8(buffer); 147 } 148 149 @Override 150 public boolean passwordMatches(ByteSequence plaintextPassword, 151 ByteSequence storedPassword) 152 { 153 try 154 { 155 ByteString decryptedPassword = 156 ByteString.wrap(cryptoManager.decrypt( 157 Base64.decode(storedPassword.toString()))); 158 return plaintextPassword.equals(decryptedPassword); 159 } 160 catch (Exception e) 161 { 162 logger.traceException(e); 163 164 return false; 165 } 166 } 167 168 @Override 169 public boolean isReversible() 170 { 171 return true; 172 } 173 174 @Override 175 public ByteString getPlaintextValue(ByteSequence storedPassword) 176 throws DirectoryException 177 { 178 try 179 { 180 byte[] decryptedPassword = 181 cryptoManager.decrypt(Base64.decode(storedPassword.toString())); 182 return ByteString.wrap(decryptedPassword); 183 } 184 catch (Exception e) 185 { 186 logger.traceException(e); 187 188 LocalizableMessage m = ERR_PWSCHEME_CANNOT_DECRYPT.get(STORAGE_SCHEME_NAME_RC4, 189 getExceptionMessage(e)); 190 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 191 m, e); 192 } 193 } 194 195 @Override 196 public boolean supportsAuthPasswordSyntax() 197 { 198 // This storage scheme does not support the authentication password syntax. 199 return false; 200 } 201 202 @Override 203 public ByteString encodeAuthPassword(ByteSequence plaintext) 204 throws DirectoryException 205 { 206 LocalizableMessage message = 207 ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName()); 208 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 209 } 210 211 @Override 212 public boolean authPasswordMatches(ByteSequence plaintextPassword, 213 String authInfo, String authValue) 214 { 215 // This storage scheme does not support the authentication password syntax. 216 return false; 217 } 218 219 @Override 220 public ByteString getAuthPasswordPlaintextValue(String authInfo, 221 String authValue) 222 throws DirectoryException 223 { 224 LocalizableMessage message = 225 ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName()); 226 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 227 } 228 229 @Override 230 public boolean isStorageSchemeSecure() 231 { 232 // This password storage scheme should be considered secure. 233 return true; 234 } 235}