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