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 2006-2008 Sun Microsystems, Inc. 015 * Portions Copyright 2011-2016 ForgeRock AS. 016 */ 017package org.opends.server.extensions; 018import org.forgerock.i18n.LocalizableMessage; 019 020 021 022import java.io.BufferedReader; 023import java.io.File; 024import java.io.FileReader; 025import java.io.IOException; 026import java.security.KeyStore; 027import java.util.List; 028import javax.net.ssl.KeyManager; 029import javax.net.ssl.KeyManagerFactory; 030 031import org.opends.server.admin.server.ConfigurationChangeListener; 032import org.opends.server.admin.std.server.PKCS11KeyManagerProviderCfg; 033import org.opends.server.api.KeyManagerProvider; 034import org.forgerock.opendj.config.server.ConfigException; 035import org.opends.server.core.DirectoryServer; 036import org.forgerock.opendj.config.server.ConfigChangeResult; 037import org.opends.server.types.DirectoryException; 038import org.forgerock.opendj.ldap.DN; 039import org.opends.server.types.InitializationException; 040import org.forgerock.opendj.ldap.ResultCode; 041import org.opends.server.util.StaticUtils; 042 043import org.forgerock.i18n.slf4j.LocalizedLogger; 044import static org.opends.messages.ExtensionMessages.*; 045import static org.opends.server.util.StaticUtils.*; 046 047/** 048 * This class defines a key manager provider that will access keys stored on a 049 * PKCS#11 device. It will use the Java PKCS#11 interface, which may need to be 050 * configured on the underlying system. 051 */ 052public class PKCS11KeyManagerProvider 053 extends KeyManagerProvider<PKCS11KeyManagerProviderCfg> 054 implements ConfigurationChangeListener<PKCS11KeyManagerProviderCfg> 055{ 056 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 057 058 059 060 /** 061 * The keystore type to use when accessing the PKCS#11 keystore. 062 */ 063 public static final String PKCS11_KEYSTORE_TYPE = "PKCS11"; 064 065 066 067 /** The DN of the configuration entry for this key manager provider. */ 068 private DN configEntryDN; 069 070 /** The PIN needed to access the keystore. */ 071 private char[] keyStorePIN; 072 073 /** The current configuration for this key manager provider. */ 074 private PKCS11KeyManagerProviderCfg currentConfig; 075 076 077 078 /** 079 * Creates a new instance of this PKCS#11 key manager provider. The 080 * <CODE>initializeKeyManagerProvider</CODE> method must be called on the 081 * resulting object before it may be used. 082 */ 083 public PKCS11KeyManagerProvider() 084 { 085 // No implementation is required. 086 } 087 088 089 090 /** {@inheritDoc} */ 091 @Override 092 public void initializeKeyManagerProvider( 093 PKCS11KeyManagerProviderCfg configuration) 094 throws ConfigException, InitializationException 095 { 096 // Store the DN of the configuration entry and register to be notified of 097 // configuration changes. 098 currentConfig = configuration; 099 configEntryDN = configuration.dn(); 100 configuration.addPKCS11ChangeListener(this); 101 102 // Get the PIN needed to access the contents of the PKCS#11 103 // keystore. We will offer several places to look for the PIN, and 104 // we will do so in the following order: 105 // 106 // - In a specified Java property 107 // - In a specified environment variable 108 // - In a specified file on the server filesystem. 109 // - As the value of a configuration attribute. 110 // 111 // In any case, the PIN must be in the clear. 112 keyStorePIN = null; 113 114 if (configuration.getKeyStorePinProperty() != null) { 115 String propertyName = configuration.getKeyStorePinProperty(); 116 String pinStr = System.getProperty(propertyName); 117 118 if (pinStr == null) { 119 LocalizableMessage message = ERR_PKCS11_KEYMANAGER_PIN_PROPERTY_NOT_SET.get( 120 propertyName, configEntryDN); 121 throw new InitializationException(message); 122 } 123 124 keyStorePIN = pinStr.toCharArray(); 125 } else if (configuration.getKeyStorePinEnvironmentVariable() != null) { 126 String enVarName = configuration 127 .getKeyStorePinEnvironmentVariable(); 128 String pinStr = System.getenv(enVarName); 129 130 if (pinStr == null) { 131 LocalizableMessage message = ERR_PKCS11_KEYMANAGER_PIN_ENVAR_NOT_SET.get( 132 enVarName, configEntryDN); 133 throw new InitializationException(message); 134 } 135 136 keyStorePIN = pinStr.toCharArray(); 137 } else if (configuration.getKeyStorePinFile() != null) { 138 String fileName = configuration.getKeyStorePinFile(); 139 File pinFile = getFileForPath(fileName); 140 141 if (!pinFile.exists()) { 142 LocalizableMessage message = ERR_PKCS11_KEYMANAGER_PIN_NO_SUCH_FILE.get(fileName, configEntryDN); 143 throw new InitializationException(message); 144 } 145 146 String pinStr; 147 try { 148 BufferedReader br = new BufferedReader( 149 new FileReader(pinFile)); 150 pinStr = br.readLine(); 151 br.close(); 152 } catch (IOException ioe) { 153 logger.traceException(ioe); 154 155 LocalizableMessage message = ERR_PKCS11_KEYMANAGER_PIN_FILE_CANNOT_READ. 156 get(fileName, configEntryDN, getExceptionMessage(ioe)); 157 throw new InitializationException(message, ioe); 158 } 159 160 if (pinStr == null) { 161 LocalizableMessage message = ERR_PKCS11_KEYMANAGER_PIN_FILE_EMPTY.get(fileName, configEntryDN); 162 throw new InitializationException(message); 163 } 164 165 keyStorePIN = pinStr.toCharArray(); 166 } else if (configuration.getKeyStorePin() != null) { 167 keyStorePIN = configuration.getKeyStorePin().toCharArray(); 168 } 169 } 170 171 172 173 /** 174 * Performs any finalization that may be necessary for this key 175 * manager provider. 176 */ 177 public void finalizeKeyManagerProvider() 178 { 179 currentConfig.removePKCS11ChangeListener(this); 180 } 181 182 183 184 /** 185 * Retrieves a set of <CODE>KeyManager</CODE> objects that may be used for 186 * interactions requiring access to a key manager. 187 * 188 * @return A set of <CODE>KeyManager</CODE> objects that may be used for 189 * interactions requiring access to a key manager. 190 * 191 * @throws DirectoryException If a problem occurs while attempting to obtain 192 * the set of key managers. 193 */ 194 public KeyManager[] getKeyManagers() 195 throws DirectoryException 196 { 197 KeyStore keyStore; 198 try 199 { 200 keyStore = KeyStore.getInstance(PKCS11_KEYSTORE_TYPE); 201 keyStore.load(null, keyStorePIN); 202 } 203 catch (Exception e) 204 { 205 logger.traceException(e); 206 207 LocalizableMessage message = 208 ERR_PKCS11_KEYMANAGER_CANNOT_LOAD.get(getExceptionMessage(e)); 209 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 210 message, e); 211 } 212 213 214 try 215 { 216 String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); 217 KeyManagerFactory keyManagerFactory = 218 KeyManagerFactory.getInstance(keyManagerAlgorithm); 219 keyManagerFactory.init(keyStore, keyStorePIN); 220 return keyManagerFactory.getKeyManagers(); 221 } 222 catch (Exception e) 223 { 224 logger.traceException(e); 225 226 LocalizableMessage message = ERR_PKCS11_KEYMANAGER_CANNOT_CREATE_FACTORY.get( 227 getExceptionMessage(e)); 228 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 229 message, e); 230 } 231 } 232 233 234 235 /** {@inheritDoc} */ 236 @Override 237 public boolean isConfigurationAcceptable( 238 PKCS11KeyManagerProviderCfg configuration, 239 List<LocalizableMessage> unacceptableReasons) 240 { 241 return isConfigurationChangeAcceptable(configuration, unacceptableReasons); 242 } 243 244 245 246 /** {@inheritDoc} */ 247 public boolean isConfigurationChangeAcceptable( 248 PKCS11KeyManagerProviderCfg configuration, 249 List<LocalizableMessage> unacceptableReasons) 250 { 251 boolean configAcceptable = true; 252 DN cfgEntryDN = configuration.dn(); 253 254 255 // Get the PIN needed to access the contents of the keystore file. 256 // 257 // We will offer several places to look for the PIN, and we will 258 // do so in the following order: 259 // 260 // - In a specified Java property 261 // - In a specified environment variable 262 // - In a specified file on the server filesystem. 263 // - As the value of a configuration attribute. 264 // 265 // In any case, the PIN must be in the clear. 266 // 267 // It is acceptable to have no PIN (OPENDJ-18) 268 if (configuration.getKeyStorePinProperty() != null) 269 { 270 String propertyName = configuration.getKeyStorePinProperty(); 271 String pinStr = System.getProperty(propertyName); 272 273 if (pinStr == null) 274 { 275 unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_PIN_PROPERTY_NOT_SET.get(propertyName, cfgEntryDN)); 276 configAcceptable = false; 277 } 278 } 279 else if (configuration.getKeyStorePinEnvironmentVariable() != null) 280 { 281 String enVarName = configuration.getKeyStorePinEnvironmentVariable(); 282 String pinStr = System.getenv(enVarName); 283 284 if (pinStr == null) 285 { 286 unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_PIN_ENVAR_NOT_SET.get(enVarName, configEntryDN)); 287 configAcceptable = false; 288 } 289 } 290 else if (configuration.getKeyStorePinFile() != null) 291 { 292 String fileName = configuration.getKeyStorePinFile(); 293 File pinFile = getFileForPath(fileName); 294 295 if (!pinFile.exists()) 296 { 297 unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_PIN_NO_SUCH_FILE.get(fileName, configEntryDN)); 298 configAcceptable = false; 299 } 300 else 301 { 302 String pinStr = null; 303 BufferedReader br = null; 304 try { 305 br = new BufferedReader(new FileReader(pinFile)); 306 pinStr = br.readLine(); 307 } 308 catch (IOException ioe) 309 { 310 unacceptableReasons.add( 311 ERR_PKCS11_KEYMANAGER_PIN_FILE_CANNOT_READ.get( 312 fileName, cfgEntryDN, getExceptionMessage(ioe))); 313 configAcceptable = false; 314 } 315 finally 316 { 317 StaticUtils.close(br); 318 } 319 320 if (pinStr == null) 321 { 322 unacceptableReasons.add(ERR_PKCS11_KEYMANAGER_PIN_FILE_EMPTY.get(fileName, configEntryDN)); 323 configAcceptable = false; 324 } 325 } 326 } 327 else if (configuration.getKeyStorePin() != null) 328 { 329 String pinStr = configuration.getKeyStorePin(); 330 if (pinStr == null) 331 { 332 // We should have a pin from the configuration, but no. 333 unacceptableReasons.add( 334 ERR_PKCS11_KEYMANAGER_CANNOT_DETERMINE_PIN_FROM_ATTR.get(cfgEntryDN, null)); 335 configAcceptable = false; 336 } 337 } 338 339 return configAcceptable; 340 } 341 342 343 344 /** {@inheritDoc} */ 345 public ConfigChangeResult applyConfigurationChange( 346 PKCS11KeyManagerProviderCfg configuration) 347 { 348 final ConfigChangeResult ccr = new ConfigChangeResult(); 349 350 // Get the PIN needed to access the contents of the keystore file. 351 // 352 // We will offer several places to look for the PIN, and we will 353 // do so in the following order: 354 // 355 // - In a specified Java property 356 // - In a specified environment variable 357 // - In a specified file on the server filesystem. 358 // - As the value of a configuration attribute. 359 // 360 // In any case, the PIN must be in the clear. 361 char[] newPIN = null; 362 363 if (configuration.getKeyStorePinProperty() != null) 364 { 365 String propertyName = configuration.getKeyStorePinProperty(); 366 String pinStr = System.getProperty(propertyName); 367 368 if (pinStr == null) 369 { 370 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 371 ccr.addMessage(ERR_PKCS11_KEYMANAGER_PIN_PROPERTY_NOT_SET.get(propertyName, configEntryDN)); 372 } 373 else 374 { 375 newPIN = pinStr.toCharArray(); 376 } 377 } 378 else if (configuration.getKeyStorePinEnvironmentVariable() != null) 379 { 380 String enVarName = configuration.getKeyStorePinEnvironmentVariable(); 381 String pinStr = System.getenv(enVarName); 382 383 if (pinStr == null) 384 { 385 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 386 ccr.addMessage(ERR_PKCS11_KEYMANAGER_PIN_ENVAR_NOT_SET.get(enVarName, configEntryDN)); 387 } 388 else 389 { 390 newPIN = pinStr.toCharArray(); 391 } 392 } 393 else if (configuration.getKeyStorePinFile() != null) 394 { 395 String fileName = configuration.getKeyStorePinFile(); 396 File pinFile = getFileForPath(fileName); 397 398 if (!pinFile.exists()) 399 { 400 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 401 ccr.addMessage(ERR_PKCS11_KEYMANAGER_PIN_NO_SUCH_FILE.get(fileName, configEntryDN)); 402 } 403 else 404 { 405 String pinStr = null; 406 BufferedReader br = null; 407 try { 408 br = new BufferedReader(new FileReader(pinFile)); 409 pinStr = br.readLine(); 410 } 411 catch (IOException ioe) 412 { 413 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 414 ccr.addMessage(ERR_PKCS11_KEYMANAGER_PIN_FILE_CANNOT_READ.get( 415 fileName, configEntryDN, getExceptionMessage(ioe))); 416 } 417 finally 418 { 419 StaticUtils.close(br); 420 } 421 422 if (pinStr == null) 423 { 424 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 425 ccr.addMessage(ERR_PKCS11_KEYMANAGER_PIN_FILE_EMPTY.get(fileName, configEntryDN)); 426 } 427 else 428 { 429 newPIN = pinStr.toCharArray(); 430 } 431 } 432 } 433 else if (configuration.getKeyStorePin() != null) 434 { 435 newPIN = configuration.getKeyStorePin().toCharArray(); 436 } 437 438 if (ccr.getResultCode() == ResultCode.SUCCESS) 439 { 440 currentConfig = configuration; 441 keyStorePIN = newPIN; 442 } 443 444 return ccr; 445 } 446}