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