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.AESPasswordStorageSchemeCfg; 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 AES reversible encryption algorithm. This 040 * implementation supports only the user password syntax and not the auth 041 * password syntax. 042 */ 043public class AESPasswordStorageScheme 044 extends PasswordStorageScheme<AESPasswordStorageSchemeCfg> 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 AESPasswordStorageScheme() 060 { 061 super(); 062 } 063 064 @Override 065 public void initializePasswordStorageScheme( 066 AESPasswordStorageSchemeCfg configuration) 067 throws ConfigException, InitializationException 068 { 069 cryptoManager = DirectoryServer.getCryptoManager(); 070 } 071 072 @Override 073 public String getStorageSchemeName() 074 { 075 return STORAGE_SCHEME_NAME_AES; 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_AES, 088 KEY_SIZE_AES, 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_AES, 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_AES); 117 buffer.append('}'); 118 byte[] plaintextBytes = null; 119 try 120 { 121 // TODO: Can we avoid this copy? 122 plaintextBytes = plaintext.toByteArray(); 123 byte[] encodedBytes = cryptoManager.encrypt(CIPHER_TRANSFORMATION_AES, 124 KEY_SIZE_AES, 125 plaintextBytes); 126 buffer.append(Base64.encode(encodedBytes)); 127 } 128 catch (Exception e) 129 { 130 logger.traceException(e); 131 132 LocalizableMessage m = ERR_PWSCHEME_CANNOT_ENCRYPT.get(STORAGE_SCHEME_NAME_AES, 133 getExceptionMessage(e)); 134 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 135 m, e); 136 } 137 finally 138 { 139 if (plaintextBytes != null) 140 { 141 Arrays.fill(plaintextBytes, (byte) 0); 142 } 143 } 144 145 return ByteString.valueOfUtf8(buffer); 146 } 147 148 @Override 149 public boolean passwordMatches(ByteSequence plaintextPassword, 150 ByteSequence storedPassword) 151 { 152 try 153 { 154 ByteString decryptedPassword = 155 ByteString.wrap(cryptoManager.decrypt( 156 Base64.decode(storedPassword.toString()))); 157 return plaintextPassword.equals(decryptedPassword); 158 } 159 catch (Exception e) 160 { 161 logger.traceException(e); 162 163 return false; 164 } 165 } 166 167 @Override 168 public boolean isReversible() 169 { 170 return true; 171 } 172 173 @Override 174 public ByteString getPlaintextValue(ByteSequence storedPassword) 175 throws DirectoryException 176 { 177 try 178 { 179 byte[] decryptedPassword = 180 cryptoManager.decrypt(Base64.decode(storedPassword.toString())); 181 return ByteString.wrap(decryptedPassword); 182 } 183 catch (Exception e) 184 { 185 logger.traceException(e); 186 187 LocalizableMessage m = ERR_PWSCHEME_CANNOT_DECRYPT.get(STORAGE_SCHEME_NAME_AES, 188 getExceptionMessage(e)); 189 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 190 m, e); 191 } 192 } 193 194 @Override 195 public boolean supportsAuthPasswordSyntax() 196 { 197 // This storage scheme does not support the authentication password syntax. 198 return false; 199 } 200 201 @Override 202 public ByteString encodeAuthPassword(ByteSequence plaintext) 203 throws DirectoryException 204 { 205 LocalizableMessage message = 206 ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName()); 207 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 208 } 209 210 @Override 211 public boolean authPasswordMatches(ByteSequence plaintextPassword, 212 String authInfo, String authValue) 213 { 214 // This storage scheme does not support the authentication password syntax. 215 return false; 216 } 217 218 @Override 219 public ByteString getAuthPasswordPlaintextValue(String authInfo, 220 String authValue) 221 throws DirectoryException 222 { 223 LocalizableMessage message = 224 ERR_PWSCHEME_DOES_NOT_SUPPORT_AUTH_PASSWORD.get(getStorageSchemeName()); 225 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); 226 } 227 228 @Override 229 public boolean isStorageSchemeSecure() 230 { 231 // This password storage scheme should be considered secure. 232 return true; 233 } 234}