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 2007-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS
026 */
027package org.opends.server.backends;
028
029import static org.forgerock.util.Reject.*;
030import static org.opends.messages.BackendMessages.*;
031import static org.opends.server.config.ConfigConstants.*;
032import static org.opends.server.util.ServerConstants.*;
033import static org.opends.server.util.StaticUtils.*;
034
035import java.io.BufferedReader;
036import java.io.File;
037import java.io.FileInputStream;
038import java.io.FileOutputStream;
039import java.io.FileReader;
040import java.io.FileWriter;
041import java.io.IOException;
042import java.io.PrintWriter;
043import java.net.UnknownHostException;
044import java.security.Key;
045import java.security.KeyStore;
046import java.security.KeyStoreException;
047import java.security.cert.Certificate;
048import java.util.Collections;
049import java.util.Iterator;
050import java.util.LinkedHashMap;
051import java.util.List;
052import java.util.Random;
053import java.util.Set;
054import java.util.SortedSet;
055
056import javax.naming.ldap.Rdn;
057import javax.net.ssl.KeyManager;
058import javax.net.ssl.KeyManagerFactory;
059import javax.net.ssl.TrustManager;
060import javax.net.ssl.TrustManagerFactory;
061
062import org.forgerock.i18n.LocalizableMessage;
063import org.forgerock.i18n.slf4j.LocalizedLogger;
064import org.forgerock.opendj.config.server.ConfigChangeResult;
065import org.forgerock.opendj.config.server.ConfigException;
066import org.forgerock.opendj.ldap.ByteString;
067import org.forgerock.opendj.ldap.ConditionResult;
068import org.forgerock.opendj.ldap.ResultCode;
069import org.forgerock.opendj.ldap.SearchScope;
070import org.forgerock.util.Reject;
071import org.opends.server.admin.server.ConfigurationChangeListener;
072import org.opends.server.admin.std.server.TrustStoreBackendCfg;
073import org.opends.server.api.Backend;
074import org.opends.server.core.AddOperation;
075import org.opends.server.core.DeleteOperation;
076import org.opends.server.core.DirectoryServer;
077import org.opends.server.core.ModifyDNOperation;
078import org.opends.server.core.ModifyOperation;
079import org.opends.server.core.SearchOperation;
080import org.opends.server.core.ServerContext;
081import org.opends.server.types.*;
082import org.opends.server.util.CertificateManager;
083import org.opends.server.util.SetupUtils;
084
085/**
086 * This class defines a backend used to provide an LDAP view of public keys
087 * stored in a key store.
088 */
089public class TrustStoreBackend extends Backend<TrustStoreBackendCfg>
090       implements ConfigurationChangeListener<TrustStoreBackendCfg>
091{
092  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
093
094
095
096  /** The current configuration state. */
097  private TrustStoreBackendCfg configuration;
098
099  /** The DN for the base entry. */
100  private DN baseDN;
101
102  /** The set of base DNs for this backend. */
103  private DN[] baseDNs;
104
105  /** The base entry. */
106  private Entry baseEntry;
107
108  /** The PIN needed to access the trust store backing file. */
109  private char[] trustStorePIN;
110
111  /** The path to the trust store backing file. */
112  private String trustStoreFile;
113
114  /** The type of trust store backing file to use. */
115  private String trustStoreType;
116
117  /** The certificate manager for the trust store. */
118  private CertificateManager certificateManager;
119
120
121
122  /**
123   * Creates a new backend.  All backend
124   * implementations must implement a default constructor that use
125   * <CODE>super()</CODE> to invoke this constructor.
126   */
127  public TrustStoreBackend()
128  {
129    super();
130
131    // Perform all initialization in initializeBackend.
132  }
133
134  /** {@inheritDoc} */
135  @Override
136  public void configureBackend(TrustStoreBackendCfg config, ServerContext serverContext) throws ConfigException
137  {
138    Reject.ifNull(config);
139    configuration = config;
140  }
141
142  /** {@inheritDoc} */
143  @Override
144  public void openBackend() throws ConfigException, InitializationException
145  {
146    DN configEntryDN = configuration.dn();
147
148    // Create the set of base DNs that we will handle.  In this case, it's just
149    // the DN of the base trust store entry.
150    SortedSet<DN> baseDNSet = configuration.getBaseDN();
151    if (baseDNSet.size() != 1)
152    {
153      throw new InitializationException(ERR_TRUSTSTORE_REQUIRES_ONE_BASE_DN.get(configEntryDN));
154    }
155    baseDN = baseDNSet.first();
156    baseDNs = new DN[] {baseDN};
157
158    // Get the path to the trust store file.
159    trustStoreFile = configuration.getTrustStoreFile();
160
161
162    // Get the trust store type.  If none is specified, then use the default
163    // type.
164    trustStoreType = configuration.getTrustStoreType();
165    if (trustStoreType == null)
166    {
167      trustStoreType = KeyStore.getDefaultType();
168    }
169
170    try
171    {
172      KeyStore.getInstance(trustStoreType);
173    }
174    catch (KeyStoreException kse)
175    {
176      logger.traceException(kse);
177      throw new InitializationException(ERR_TRUSTSTORE_INVALID_TYPE.get(
178          trustStoreType, configEntryDN, getExceptionMessage(kse)));
179    }
180
181
182    // Get the PIN needed to access the contents of the trust store file.  We
183    // will offer several places to look for the PIN, and we will do so in the
184    // following order:
185    // - In a specified Java property
186    // - In a specified environment variable
187    // - In a specified file on the server filesystem.
188    // - As the value of a configuration attribute.
189    // In any case, the PIN must be in the clear.  If no PIN is provided, then
190    // it will be assumed that none is required to access the information in the
191    // trust store.
192    String pinProperty = configuration.getTrustStorePinProperty();
193    if (pinProperty == null)
194    {
195      String pinEnVar = configuration.getTrustStorePinEnvironmentVariable();
196      if (pinEnVar == null)
197      {
198        String pinFilePath = configuration.getTrustStorePinFile();
199        if (pinFilePath == null)
200        {
201          String pinStr = configuration.getTrustStorePin();
202          if (pinStr == null)
203          {
204            // This should be an Error. Otherwise, programs fails.
205            // Is there a Unit Test?
206            trustStorePIN = null;
207          }
208          else
209          {
210            trustStorePIN = pinStr.toCharArray();
211          }
212        }
213        else
214        {
215          File pinFile = getFileForPath(pinFilePath);
216          if (! pinFile.exists())
217          {
218            try
219            {
220              // Generate a PIN.
221              trustStorePIN = createKeystorePassword();
222
223              // Store the PIN in the pin file.
224              createPINFile(pinFile.getPath(), new String(trustStorePIN));
225            }
226            catch (Exception e)
227            {
228              throw new InitializationException(
229                  ERR_TRUSTSTORE_PIN_FILE_CANNOT_CREATE.get(pinFilePath, configEntryDN));
230            }
231          }
232          else
233          {
234            String pinStr;
235
236            BufferedReader br = null;
237            try
238            {
239              br = new BufferedReader(new FileReader(pinFile));
240              pinStr = br.readLine();
241            }
242            catch (IOException ioe)
243            {
244              LocalizableMessage message = ERR_TRUSTSTORE_PIN_FILE_CANNOT_READ.
245                  get(pinFilePath, configEntryDN, getExceptionMessage(ioe));
246              throw new InitializationException(message, ioe);
247            }
248            finally
249            {
250              close(br);
251            }
252
253            if (pinStr == null)
254            {
255              throw new InitializationException(
256                  ERR_TRUSTSTORE_PIN_FILE_EMPTY.get(pinFilePath, configEntryDN));
257            }
258            trustStorePIN = pinStr.toCharArray();
259          }
260        }
261      }
262      else
263      {
264        String pinStr = System.getenv(pinEnVar);
265        if (pinStr == null)
266        {
267          throw new InitializationException(
268              ERR_TRUSTSTORE_PIN_ENVAR_NOT_SET.get(pinProperty, configEntryDN));
269        }
270        trustStorePIN = pinStr.toCharArray();
271      }
272    }
273    else
274    {
275      String pinStr = System.getProperty(pinProperty);
276      if (pinStr == null)
277      {
278        throw new InitializationException(ERR_TRUSTSTORE_PIN_PROPERTY_NOT_SET.get(pinProperty, configEntryDN));
279      }
280      trustStorePIN = pinStr.toCharArray();
281    }
282
283    // Create a certificate manager.
284    certificateManager =
285         new CertificateManager(getFileForPath(trustStoreFile).getPath(),
286                                trustStoreType,
287                                new String(trustStorePIN));
288
289    // Generate a self-signed certificate, if there is none.
290    generateInstanceCertificateIfAbsent();
291
292    // Construct the trust store base entry.
293    LinkedHashMap<ObjectClass,String> objectClasses = new LinkedHashMap<>(2);
294    objectClasses.put(DirectoryServer.getTopObjectClass(), OC_TOP);
295
296    ObjectClass branchOC =
297         DirectoryServer.getObjectClass("ds-cfg-branch", true);
298    objectClasses.put(branchOC, "ds-cfg-branch");
299
300    LinkedHashMap<AttributeType,List<Attribute>> opAttrs = new LinkedHashMap<>(0);
301    LinkedHashMap<AttributeType,List<Attribute>> userAttrs = new LinkedHashMap<>(1);
302
303    RDN rdn = baseDN.rdn();
304    int numAVAs = rdn.getNumValues();
305    for (int i=0; i < numAVAs; i++)
306    {
307      AttributeType attrType = rdn.getAttributeType(i);
308      userAttrs.put(attrType, Attributes.createAsList(attrType, rdn.getAttributeValue(i)));
309    }
310
311    baseEntry = new Entry(baseDN, objectClasses, userAttrs, opAttrs);
312
313    // Register this as a change listener.
314    configuration.addTrustStoreChangeListener(this);
315
316
317    // Register the trust store base as a private suffix.
318    try
319    {
320      DirectoryServer.registerBaseDN(baseDN, this, true);
321    }
322    catch (Exception e)
323    {
324      logger.traceException(e);
325      throw new InitializationException(ERR_BACKEND_CANNOT_REGISTER_BASEDN.get(baseDN, e), e);
326    }
327  }
328
329  /** {@inheritDoc} */
330  @Override
331  public void closeBackend()
332  {
333    configuration.addTrustStoreChangeListener(this);
334
335    try
336    {
337      DirectoryServer.deregisterBaseDN(baseDN);
338    }
339    catch (Exception e)
340    {
341      logger.traceException(e);
342    }
343  }
344
345  /** {@inheritDoc} */
346  @Override
347  public DN[] getBaseDNs()
348  {
349    return baseDNs;
350  }
351
352  /** {@inheritDoc} */
353  @Override
354  public long getEntryCount()
355  {
356    int numEntries = 1;
357
358    try
359    {
360      String[] aliases = certificateManager.getCertificateAliases();
361      if (aliases != null)
362      {
363        numEntries += aliases.length;
364      }
365    }
366    catch (KeyStoreException e)
367    {
368      logger.traceException(e);
369    }
370
371    return numEntries;
372  }
373
374  /** {@inheritDoc} */
375  @Override
376  public boolean isIndexed(AttributeType attributeType, IndexType indexType)
377  {
378    // All searches in this backend will always be considered indexed.
379    return true;
380  }
381
382  /** {@inheritDoc} */
383  @Override
384  public Entry getEntry(DN entryDN) throws DirectoryException
385  {
386    // If the requested entry was null, then throw an exception.
387    if (entryDN == null)
388    {
389      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
390          ERR_BACKEND_GET_ENTRY_NULL.get(getBackendID()));
391    }
392
393
394    // If the requested entry was the backend base entry, then retrieve it.
395    if (entryDN.equals(baseDN))
396    {
397      return baseEntry.duplicate(true);
398    }
399
400
401    // See if the requested entry was one level below the backend base entry.
402    // If so, then it must point to a trust store entry.
403    DN parentDN = entryDN.getParentDNInSuffix();
404    if (parentDN != null && parentDN.equals(baseDN))
405    {
406      try
407      {
408        return getCertEntry(entryDN);
409      }
410      catch (DirectoryException e)
411      {
412        logger.traceException(e);
413      }
414    }
415    return null;
416  }
417
418
419
420  /**
421   * Generates an entry for a certificate based on the provided DN.  The
422   * DN must contain an RDN component that specifies the alias of the
423   * certificate, and that certificate alias must exist in the key store.
424   *
425   * @param  entryDN  The DN of the certificate to retrieve.
426   *
427   * @return  The requested certificate entry.
428   *
429   * @throws  DirectoryException  If the specified alias does not exist, or if
430   *                              the DN does not specify any alias.
431   */
432  private Entry getCertEntry(DN entryDN)
433         throws DirectoryException
434  {
435    // Make sure that the DN specifies a certificate alias.
436    AttributeType t = DirectoryServer.getAttributeTypeOrDefault(ATTR_CRYPTO_KEY_ID);
437    ByteString v = entryDN.rdn().getAttributeValue(t);
438    if (v == null)
439    {
440      LocalizableMessage message = ERR_TRUSTSTORE_DN_DOES_NOT_SPECIFY_CERTIFICATE.get(entryDN);
441      throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message, baseDN, null);
442    }
443
444    String certAlias = v.toString();
445    ByteString certValue;
446    try
447    {
448      Certificate cert = certificateManager.getCertificate(certAlias);
449      if (cert == null)
450      {
451        LocalizableMessage message = ERR_TRUSTSTORE_CERTIFICATE_NOT_FOUND.get(entryDN, certAlias);
452        throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
453      }
454      certValue = ByteString.wrap(cert.getEncoded());
455    }
456    catch (Exception e)
457    {
458      logger.traceException(e);
459
460      LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_RETRIEVE_CERT.get(
461          certAlias, trustStoreFile, e.getMessage());
462      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
463    }
464
465    // Construct the certificate entry to return.
466    LinkedHashMap<ObjectClass,String> ocMap = new LinkedHashMap<>(2);
467    ocMap.put(DirectoryServer.getTopObjectClass(), OC_TOP);
468
469    ObjectClass objectClass =
470         DirectoryServer.getObjectClass(OC_CRYPTO_INSTANCE_KEY, true);
471    ocMap.put(objectClass, OC_CRYPTO_INSTANCE_KEY);
472
473    LinkedHashMap<AttributeType,List<Attribute>> opAttrs = new LinkedHashMap<>(0);
474    LinkedHashMap<AttributeType,List<Attribute>> userAttrs = new LinkedHashMap<>(3);
475
476    userAttrs.put(t, Attributes.createAsList(t, v));
477
478
479    t = DirectoryServer.getAttributeTypeOrDefault(ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE);
480    AttributeBuilder builder = new AttributeBuilder(t);
481    builder.setOption("binary");
482    builder.add(certValue);
483    userAttrs.put(t, builder.toAttributeList());
484
485
486    Entry e = new Entry(entryDN, ocMap, userAttrs, opAttrs);
487    e.processVirtualAttributes();
488    return e;
489  }
490
491  /** {@inheritDoc} */
492  @Override
493  public void addEntry(Entry entry, AddOperation addOperation)
494         throws DirectoryException
495  {
496    DN entryDN = entry.getName();
497
498    if (entryDN.equals(baseDN))
499    {
500      LocalizableMessage message = ERR_TRUSTSTORE_INVALID_BASE.get(entryDN);
501      throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, message);
502    }
503
504    DN parentDN = entryDN.getParentDNInSuffix();
505    if (parentDN == null)
506    {
507      LocalizableMessage message = ERR_TRUSTSTORE_INVALID_BASE.get(entryDN);
508      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
509    }
510
511    if (parentDN.equals(baseDN))
512    {
513      addCertificate(entry);
514    }
515    else
516    {
517      LocalizableMessage message = ERR_TRUSTSTORE_INVALID_BASE.get(entryDN);
518      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
519    }
520  }
521
522  /** {@inheritDoc} */
523  @Override
524  public void deleteEntry(DN entryDN, DeleteOperation deleteOperation)
525         throws DirectoryException
526  {
527    if (entryDN.equals(baseDN))
528    {
529      LocalizableMessage message = ERR_TRUSTSTORE_INVALID_BASE.get(entryDN);
530      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
531    }
532
533    DN parentDN = entryDN.getParentDNInSuffix();
534    if (parentDN == null || !parentDN.equals(baseDN))
535    {
536      LocalizableMessage message = ERR_TRUSTSTORE_INVALID_BASE.get(entryDN);
537      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
538    }
539
540    deleteCertificate(entryDN);
541  }
542
543  /** {@inheritDoc} */
544  @Override
545  public void replaceEntry(Entry oldEntry, Entry newEntry,
546      ModifyOperation modifyOperation) throws DirectoryException
547  {
548    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
549        ERR_BACKEND_MODIFY_NOT_SUPPORTED.get(oldEntry.getName(), getBackendID()));
550  }
551
552  /** {@inheritDoc} */
553  @Override
554  public void renameEntry(DN currentDN, Entry entry,
555                          ModifyDNOperation modifyDNOperation)
556         throws DirectoryException
557  {
558    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
559        ERR_BACKEND_MODIFY_DN_NOT_SUPPORTED.get(currentDN, getBackendID()));
560  }
561
562  /** {@inheritDoc} */
563  @Override
564  public void search(SearchOperation searchOperation)
565         throws DirectoryException
566  {
567    // Get the base entry for the search, if possible.  If it doesn't exist,
568    // then this will throw an exception.
569    DN    baseDN    = searchOperation.getBaseDN();
570    Entry baseEntry = getEntry(baseDN);
571
572
573    // Look at the base DN and see if it's the trust store base DN, or a
574    // trust store entry DN.
575    SearchScope  scope  = searchOperation.getScope();
576    SearchFilter filter = searchOperation.getFilter();
577    if (this.baseDN.equals(baseDN))
578    {
579      if ((scope == SearchScope.BASE_OBJECT || scope == SearchScope.WHOLE_SUBTREE)
580          && filter.matchesEntry(baseEntry))
581      {
582        searchOperation.returnEntry(baseEntry, null);
583      }
584
585      String[] aliases = null;
586      try
587      {
588        aliases = certificateManager.getCertificateAliases();
589      }
590      catch (KeyStoreException e)
591      {
592        logger.traceException(e);
593      }
594
595      if (aliases == null)
596      {
597        aliases = new String[0];
598      }
599
600      if (scope != SearchScope.BASE_OBJECT && aliases.length != 0)
601      {
602        AttributeType certAliasType = DirectoryServer.getAttributeTypeOrDefault(ATTR_CRYPTO_KEY_ID);
603        for (String alias : aliases)
604        {
605          DN certDN = makeChildDN(this.baseDN, certAliasType, alias);
606
607          Entry certEntry;
608          try
609          {
610            certEntry = getCertEntry(certDN);
611          }
612          catch (Exception e)
613          {
614            logger.traceException(e);
615            continue;
616          }
617
618          if (filter.matchesEntry(certEntry))
619          {
620            searchOperation.returnEntry(certEntry, null);
621          }
622        }
623      }
624    }
625    else if (this.baseDN.equals(baseDN.getParentDNInSuffix()))
626    {
627      Entry certEntry = getCertEntry(baseDN);
628
629      if ((scope == SearchScope.BASE_OBJECT || scope == SearchScope.WHOLE_SUBTREE)
630          && filter.matchesEntry(certEntry))
631      {
632        searchOperation.returnEntry(certEntry, null);
633      }
634    }
635    else
636    {
637      LocalizableMessage message = ERR_TRUSTSTORE_INVALID_BASE.get(baseDN);
638      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
639    }
640  }
641
642  /** {@inheritDoc} */
643  @Override
644  public Set<String> getSupportedControls()
645  {
646    return Collections.emptySet();
647  }
648
649  /** {@inheritDoc} */
650  @Override
651  public Set<String> getSupportedFeatures()
652  {
653    return Collections.emptySet();
654  }
655
656  /** {@inheritDoc} */
657  @Override
658  public boolean supports(BackendOperation backendOperation)
659  {
660    return false;
661  }
662
663  /** {@inheritDoc} */
664  @Override
665  public void exportLDIF(LDIFExportConfig exportConfig)
666         throws DirectoryException
667  {
668    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
669        ERR_BACKEND_IMPORT_AND_EXPORT_NOT_SUPPORTED.get(getBackendID()));
670  }
671
672  /** {@inheritDoc} */
673  @Override
674  public LDIFImportResult importLDIF(LDIFImportConfig importConfig, ServerContext serverContext)
675      throws DirectoryException
676  {
677    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
678        ERR_BACKEND_IMPORT_AND_EXPORT_NOT_SUPPORTED.get(getBackendID()));
679  }
680
681  /** {@inheritDoc} */
682  @Override
683  public void createBackup(BackupConfig backupConfig)
684       throws DirectoryException
685  {
686    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
687        ERR_BACKEND_BACKUP_AND_RESTORE_NOT_SUPPORTED.get(getBackendID()));
688  }
689
690  /** {@inheritDoc} */
691  @Override
692  public void removeBackup(BackupDirectory backupDirectory,
693                           String backupID)
694         throws DirectoryException
695  {
696    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
697        ERR_BACKEND_BACKUP_AND_RESTORE_NOT_SUPPORTED.get(getBackendID()));
698  }
699
700  /** {@inheritDoc} */
701  @Override
702  public void restoreBackup(RestoreConfig restoreConfig)
703         throws DirectoryException
704  {
705    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
706        ERR_BACKEND_BACKUP_AND_RESTORE_NOT_SUPPORTED.get(getBackendID()));
707  }
708
709  /** {@inheritDoc} */
710  @Override
711  public ConditionResult hasSubordinates(DN entryDN)
712      throws DirectoryException
713  {
714    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
715        ERR_HAS_SUBORDINATES_NOT_SUPPORTED.get());
716  }
717
718  /** {@inheritDoc} */
719  @Override
720  public long getNumberOfEntriesInBaseDN(DN baseDN) throws DirectoryException
721  {
722    checkNotNull(baseDN, "baseDN must not be null");
723    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, ERR_NUM_SUBORDINATES_NOT_SUPPORTED.get());
724  }
725
726  /** {@inheritDoc} */
727  @Override
728  public long getNumberOfChildren(DN parentDN) throws DirectoryException
729  {
730    checkNotNull(parentDN, "parentDN must not be null");
731    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, ERR_NUM_SUBORDINATES_NOT_SUPPORTED.get());
732  }
733
734  /** {@inheritDoc} */
735  @Override
736  public boolean isConfigurationChangeAcceptable(
737       TrustStoreBackendCfg configuration, List<LocalizableMessage> unacceptableReasons)
738  {
739    boolean configAcceptable = true;
740    DN cfgEntryDN = configuration.dn();
741
742
743    // Get the path to the trust store file.
744    String newTrustStoreFile = configuration.getTrustStoreFile();
745    try
746    {
747      File f = getFileForPath(newTrustStoreFile);
748      if (!f.exists() || !f.isFile())
749      {
750        unacceptableReasons.add(ERR_TRUSTSTORE_NO_SUCH_FILE.get(newTrustStoreFile, cfgEntryDN));
751        configAcceptable = false;
752      }
753    }
754    catch (Exception e)
755    {
756      logger.traceException(e);
757
758      unacceptableReasons.add(ERR_TRUSTSTORE_CANNOT_DETERMINE_FILE.get(cfgEntryDN, getExceptionMessage(e)));
759      configAcceptable = false;
760    }
761
762
763    // Check to see if the trust store type is acceptable.
764    String storeType = configuration.getTrustStoreType();
765    if (storeType != null)
766    {
767      try
768      {
769        KeyStore.getInstance(storeType);
770      }
771      catch (KeyStoreException kse)
772      {
773        logger.traceException(kse);
774
775        unacceptableReasons.add(ERR_TRUSTSTORE_INVALID_TYPE.get(
776            storeType, cfgEntryDN, getExceptionMessage(kse)));
777        configAcceptable = false;
778      }
779    }
780
781
782    // If there is a PIN property, then make sure the corresponding
783    // property is set.
784    String pinProp = configuration.getTrustStorePinProperty();
785    if (pinProp != null && System.getProperty(pinProp) == null)
786    {
787      unacceptableReasons.add(ERR_TRUSTSTORE_PIN_PROPERTY_NOT_SET.get(pinProp, cfgEntryDN));
788      configAcceptable = false;
789    }
790
791
792    // If there is a PIN environment variable, then make sure the corresponding
793    // environment variable is set.
794    String pinEnVar = configuration.getTrustStorePinEnvironmentVariable();
795    if (pinEnVar != null && System.getenv(pinEnVar) == null)
796    {
797      unacceptableReasons.add(ERR_TRUSTSTORE_PIN_ENVAR_NOT_SET.get(pinEnVar, cfgEntryDN));
798      configAcceptable = false;
799    }
800
801
802    // If there is a PIN file, then make sure the file is readable if it exists.
803    String pinFile = configuration.getTrustStorePinFile();
804    if (pinFile != null)
805    {
806      File f = new File(pinFile);
807      if (f.exists())
808      {
809        String pinStr = null;
810
811        BufferedReader br = null;
812        try
813        {
814          br = new BufferedReader(new FileReader(pinFile));
815          pinStr = br.readLine();
816        }
817        catch (IOException ioe)
818        {
819          unacceptableReasons.add(ERR_TRUSTSTORE_PIN_FILE_CANNOT_READ.get(
820              pinFile, cfgEntryDN, getExceptionMessage(ioe)));
821          configAcceptable = false;
822        }
823        finally
824        {
825          close(br);
826        }
827
828        if (pinStr == null)
829        {
830          unacceptableReasons.add(ERR_TRUSTSTORE_PIN_FILE_EMPTY.get(pinFile, cfgEntryDN));
831          configAcceptable = false;
832        }
833      }
834    }
835
836
837    return configAcceptable;
838  }
839
840  /** {@inheritDoc} */
841  @Override
842  public ConfigChangeResult applyConfigurationChange(TrustStoreBackendCfg cfg)
843  {
844    final ConfigChangeResult ccr = new ConfigChangeResult();
845    DN configEntryDN = cfg.dn();
846
847    // Get the path to the trust store file.
848    String newTrustStoreFile = cfg.getTrustStoreFile();
849    File f = getFileForPath(newTrustStoreFile);
850    if (!f.exists() || !f.isFile())
851    {
852      ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
853      ccr.addMessage(ERR_TRUSTSTORE_NO_SUCH_FILE.get(newTrustStoreFile, configEntryDN));
854    }
855
856
857    // Get the trust store type.  If none is specified, then use the default
858    // type.
859    String newTrustStoreType = cfg.getTrustStoreType();
860    if (newTrustStoreType == null)
861    {
862      newTrustStoreType = KeyStore.getDefaultType();
863    }
864
865    try
866    {
867      KeyStore.getInstance(newTrustStoreType);
868    }
869    catch (KeyStoreException kse)
870    {
871      logger.traceException(kse);
872
873      ccr.addMessage(ERR_TRUSTSTORE_INVALID_TYPE.get(newTrustStoreType, configEntryDN, getExceptionMessage(kse)));
874      ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
875    }
876
877
878    // Get the PIN needed to access the contents of the trust store file.  We
879    // will offer several places to look for the PIN, and we will do so in the
880    // following order:
881    // - In a specified Java property
882    // - In a specified environment variable
883    // - In a specified file on the server filesystem.
884    // - As the value of a configuration attribute.
885    // In any case, the PIN must be in the clear.  If no PIN is provided, then
886    // it will be assumed that none is required to access the information in the
887    // trust store.
888    char[] newPIN = null;
889    String newPINProperty = cfg.getTrustStorePinProperty();
890    if (newPINProperty == null)
891    {
892      String newPINEnVar = cfg.getTrustStorePinEnvironmentVariable();
893      if (newPINEnVar == null)
894      {
895        String newPINFile = cfg.getTrustStorePinFile();
896        if (newPINFile == null)
897        {
898          String pinStr = cfg.getTrustStorePin();
899          if (pinStr == null)
900          {
901            newPIN = null;
902          }
903          else
904          {
905            newPIN = pinStr.toCharArray();
906          }
907        }
908        else
909        {
910          File pinFile = getFileForPath(newPINFile);
911          if (! pinFile.exists())
912          {
913            try
914            {
915              // Generate a PIN.
916              newPIN = createKeystorePassword();
917
918              // Store the PIN in the pin file.
919              createPINFile(pinFile.getPath(), new String(newPIN));
920            }
921            catch (Exception e)
922            {
923              ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
924              ccr.addMessage(ERR_TRUSTSTORE_PIN_FILE_CANNOT_CREATE.get(newPINFile, configEntryDN));
925            }
926          }
927          else
928          {
929            String pinStr = null;
930
931            BufferedReader br = null;
932            try
933            {
934              br = new BufferedReader(new FileReader(pinFile));
935              pinStr = br.readLine();
936            }
937            catch (IOException ioe)
938            {
939              ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
940              ccr.addMessage(ERR_TRUSTSTORE_PIN_FILE_CANNOT_READ.get(
941                  newPINFile, configEntryDN, getExceptionMessage(ioe)));
942            }
943            finally
944            {
945              close(br);
946            }
947
948            if (pinStr == null)
949            {
950              ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
951              ccr.addMessage(ERR_TRUSTSTORE_PIN_FILE_EMPTY.get(newPINFile, configEntryDN));
952            }
953            else
954            {
955              newPIN = pinStr.toCharArray();
956            }
957          }
958        }
959      }
960      else
961      {
962        String pinStr = System.getenv(newPINEnVar);
963        if (pinStr == null)
964        {
965          ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
966          ccr.addMessage(ERR_TRUSTSTORE_PIN_ENVAR_NOT_SET.get(newPINEnVar, configEntryDN));
967        }
968        else
969        {
970          newPIN = pinStr.toCharArray();
971        }
972      }
973    }
974    else
975    {
976      String pinStr = System.getProperty(newPINProperty);
977      if (pinStr == null)
978      {
979        ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
980        ccr.addMessage(ERR_TRUSTSTORE_PIN_PROPERTY_NOT_SET.get(newPINProperty, configEntryDN));
981      }
982      else
983      {
984        newPIN = pinStr.toCharArray();
985      }
986    }
987
988
989    if (ccr.getResultCode() == ResultCode.SUCCESS)
990    {
991      trustStoreFile = newTrustStoreFile;
992      trustStoreType = newTrustStoreType;
993      trustStorePIN  = newPIN;
994      configuration  = cfg;
995      certificateManager =
996           new CertificateManager(getFileForPath(trustStoreFile).getPath(),
997                                  trustStoreType,
998                                  new String(trustStorePIN));
999    }
1000
1001    return ccr;
1002  }
1003
1004  /**
1005   * Create a new child DN from a given parent DN.  The child RDN is formed
1006   * from a given attribute type and string value.
1007   * @param parentDN The DN of the parent.
1008   * @param rdnAttrType The attribute type of the RDN.
1009   * @param rdnStringValue The string value of the RDN.
1010   * @return A new child DN.
1011   */
1012  public static DN makeChildDN(DN parentDN, AttributeType rdnAttrType,
1013                               String rdnStringValue)
1014  {
1015    ByteString attrValue = ByteString.valueOf(rdnStringValue);
1016    return parentDN.child(RDN.create(rdnAttrType, attrValue));
1017  }
1018
1019
1020  /**
1021   * Retrieves a set of <CODE>KeyManager</CODE> objects that may be used for
1022   * interactions requiring access to a key manager.
1023   *
1024   * @return  A set of <CODE>KeyManager</CODE> objects that may be used for
1025   *          interactions requiring access to a key manager.
1026   *
1027   * @throws DirectoryException  If a problem occurs while attempting to obtain
1028   *                             the set of key managers.
1029   */
1030  public KeyManager[] getKeyManagers()
1031         throws DirectoryException
1032  {
1033    KeyStore keyStore;
1034    FileInputStream inputStream = null;
1035    try
1036    {
1037      keyStore = KeyStore.getInstance(trustStoreType);
1038
1039      inputStream =
1040           new FileInputStream(getFileForPath(trustStoreFile));
1041      keyStore.load(inputStream, trustStorePIN);
1042    }
1043    catch (Exception e)
1044    {
1045      logger.traceException(e);
1046
1047      LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_LOAD.get(
1048          trustStoreFile, getExceptionMessage(e));
1049      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1050                                   message, e);
1051    }
1052    finally
1053    {
1054      close(inputStream);
1055    }
1056
1057
1058    try
1059    {
1060      String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
1061      KeyManagerFactory keyManagerFactory =
1062           KeyManagerFactory.getInstance(keyManagerAlgorithm);
1063      keyManagerFactory.init(keyStore, trustStorePIN);
1064      return keyManagerFactory.getKeyManagers();
1065    }
1066    catch (Exception e)
1067    {
1068      logger.traceException(e);
1069
1070      LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_CREATE_FACTORY.get(
1071          trustStoreFile, getExceptionMessage(e));
1072      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1073                                   message, e);
1074    }
1075  }
1076
1077
1078  /**
1079   * Retrieves a set of {@code TrustManager} objects that may be used
1080   * for interactions requiring access to a trust manager.
1081   *
1082   * @return  A set of {@code TrustManager} objects that may be used
1083   *          for interactions requiring access to a trust manager.
1084   *
1085   * @throws  DirectoryException  If a problem occurs while attempting
1086   *                              to obtain the set of trust managers.
1087   */
1088  public TrustManager[] getTrustManagers()
1089         throws DirectoryException
1090  {
1091    KeyStore trustStore;
1092    FileInputStream inputStream = null;
1093    try
1094    {
1095      trustStore = KeyStore.getInstance(trustStoreType);
1096
1097      inputStream =
1098           new FileInputStream(getFileForPath(trustStoreFile));
1099      trustStore.load(inputStream, trustStorePIN);
1100    }
1101    catch (Exception e)
1102    {
1103      logger.traceException(e);
1104
1105      LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_LOAD.get(
1106          trustStoreFile, getExceptionMessage(e));
1107      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1108                                   message, e);
1109    }
1110    finally
1111    {
1112      close(inputStream);
1113    }
1114
1115
1116    try
1117    {
1118      String trustManagerAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
1119      TrustManagerFactory trustManagerFactory =
1120           TrustManagerFactory.getInstance(trustManagerAlgorithm);
1121      trustManagerFactory.init(trustStore);
1122      return trustManagerFactory.getTrustManagers();
1123    }
1124    catch (Exception e)
1125    {
1126      logger.traceException(e);
1127
1128      LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_CREATE_FACTORY.get(
1129          trustStoreFile, getExceptionMessage(e));
1130      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1131                                   message, e);
1132    }
1133  }
1134
1135
1136  /**
1137   * Returns the key associated with the given alias, using the trust
1138   * store pin to recover it.
1139   *
1140   * @param   alias The alias name.
1141   *
1142   * @return  The requested key, or null if the given alias does not exist
1143   *          or does not identify a key-related entry.
1144   *
1145   * @throws  DirectoryException  If an error occurs while retrieving the key.
1146   */
1147  public Key getKey(String alias)
1148         throws DirectoryException
1149  {
1150    KeyStore trustStore;
1151    FileInputStream inputStream = null;
1152    try
1153    {
1154      trustStore = KeyStore.getInstance(trustStoreType);
1155
1156      inputStream =
1157           new FileInputStream(getFileForPath(trustStoreFile));
1158      trustStore.load(inputStream, trustStorePIN);
1159    }
1160    catch (Exception e)
1161    {
1162      logger.traceException(e);
1163
1164      LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_LOAD.get(
1165          trustStoreFile, getExceptionMessage(e));
1166      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1167                                   message, e);
1168    }
1169    finally
1170    {
1171      close(inputStream);
1172    }
1173
1174    try
1175    {
1176      return trustStore.getKey(alias, trustStorePIN);
1177    }
1178    catch (Exception e)
1179    {
1180      logger.traceException(e);
1181
1182      LocalizableMessage message = ERR_TRUSTSTORE_ERROR_READING_KEY.get(
1183           alias, trustStoreFile, getExceptionMessage(e));
1184      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
1185                                   message, e);
1186    }
1187  }
1188
1189
1190  private void addCertificate(Entry entry)
1191       throws DirectoryException
1192  {
1193    DN entryDN = entry.getName();
1194
1195    // Make sure that the DN specifies a certificate alias.
1196    AttributeType t = DirectoryServer.getAttributeTypeOrDefault(ATTR_CRYPTO_KEY_ID);
1197    ByteString v = entryDN.rdn().getAttributeValue(t);
1198    if (v == null)
1199    {
1200      LocalizableMessage message = ERR_TRUSTSTORE_DN_DOES_NOT_SPECIFY_CERTIFICATE.get(entryDN);
1201      throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message, baseDN, null);
1202    }
1203
1204    String certAlias = v.toString();
1205    try
1206    {
1207      if (certificateManager.aliasInUse(certAlias))
1208      {
1209        LocalizableMessage message = ERR_TRUSTSTORE_ALIAS_IN_USE.get(entryDN);
1210        throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, message);
1211      }
1212
1213      ObjectClass ocSelfSignedCertRequest =
1214           DirectoryServer.getObjectClass(OC_SELF_SIGNED_CERT_REQUEST, true);
1215      if (entry.hasObjectClass(ocSelfSignedCertRequest))
1216      {
1217        try
1218        {
1219          certificateManager.generateSelfSignedCertificate(
1220             certAlias,
1221             getADSCertificateSubjectDN(),
1222             getADSCertificateValidity());
1223        }
1224        catch (Exception e)
1225        {
1226          LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_GENERATE_CERT.get(
1227              certAlias, trustStoreFile, getExceptionMessage(e));
1228          throw new DirectoryException(
1229               DirectoryServer.getServerErrorResultCode(), message, e);
1230        }
1231      }
1232      else
1233      {
1234        List<Attribute> certAttrs = entry.getAttribute(
1235             ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE);
1236        if (certAttrs == null)
1237        {
1238          LocalizableMessage message =
1239               ERR_TRUSTSTORE_ENTRY_MISSING_CERT_ATTR.get(entryDN, ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE);
1240          throw new DirectoryException(
1241               DirectoryServer.getServerErrorResultCode(), message);
1242        }
1243        if (certAttrs.size() != 1)
1244        {
1245          LocalizableMessage message =
1246               ERR_TRUSTSTORE_ENTRY_HAS_MULTIPLE_CERT_ATTRS.get(entryDN, ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE);
1247          throw new DirectoryException(
1248               DirectoryServer.getServerErrorResultCode(), message);
1249        }
1250
1251        Attribute certAttr = certAttrs.get(0);
1252        Iterator<ByteString> i = certAttr.iterator();
1253
1254        if (!i.hasNext())
1255        {
1256          LocalizableMessage message =
1257               ERR_TRUSTSTORE_ENTRY_MISSING_CERT_VALUE.get(entryDN, ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE);
1258          throw new DirectoryException(
1259               DirectoryServer.getServerErrorResultCode(), message);
1260        }
1261
1262        ByteString certBytes = i.next();
1263
1264        if (i.hasNext())
1265        {
1266          LocalizableMessage message =
1267               ERR_TRUSTSTORE_ENTRY_HAS_MULTIPLE_CERT_VALUES.get(entryDN, ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE);
1268          throw new DirectoryException(
1269               DirectoryServer.getServerErrorResultCode(), message);
1270        }
1271
1272        try
1273        {
1274          File tempDir = getFileForPath("config");
1275          File tempFile = File.createTempFile(configuration.getBackendId(),
1276                                              certAlias, tempDir);
1277          try
1278          {
1279            FileOutputStream outputStream =
1280                 new FileOutputStream(tempFile.getPath(), false);
1281            try
1282            {
1283              certBytes.copyTo(outputStream);
1284            }
1285            finally
1286            {
1287              outputStream.close();
1288            }
1289
1290            certificateManager.addCertificate(certAlias, tempFile);
1291          }
1292          finally
1293          {
1294            tempFile.delete();
1295          }
1296        }
1297        catch (IOException e)
1298        {
1299          LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_WRITE_CERT.get(
1300              certAlias, getExceptionMessage(e));
1301          throw new DirectoryException(
1302               DirectoryServer.getServerErrorResultCode(), message, e);
1303        }
1304      }
1305    }
1306    catch (Exception e)
1307    {
1308      LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_ADD_CERT.get(
1309           certAlias, trustStoreFile, getExceptionMessage(e));
1310      throw new DirectoryException(
1311           DirectoryServer.getServerErrorResultCode(), message, e);
1312    }
1313
1314  }
1315
1316
1317  private void deleteCertificate(DN entryDN)
1318       throws DirectoryException
1319  {
1320    // Make sure that the DN specifies a certificate alias.
1321    AttributeType t = DirectoryServer.getAttributeTypeOrDefault(ATTR_CRYPTO_KEY_ID);
1322    ByteString v = entryDN.rdn().getAttributeValue(t);
1323    if (v == null)
1324    {
1325      LocalizableMessage message = ERR_TRUSTSTORE_DN_DOES_NOT_SPECIFY_CERTIFICATE.get(entryDN);
1326      throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message, baseDN, null);
1327    }
1328
1329    String certAlias = v.toString();
1330    try
1331    {
1332      if (!certificateManager.aliasInUse(certAlias))
1333      {
1334        LocalizableMessage message = ERR_TRUSTSTORE_INVALID_BASE.get(entryDN);
1335        throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
1336      }
1337
1338      certificateManager.removeCertificate(certAlias);
1339    }
1340    catch (Exception e)
1341    {
1342      LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_DELETE_CERT.get(
1343           certAlias, trustStoreFile, getExceptionMessage(e));
1344      throw new DirectoryException(
1345           DirectoryServer.getServerErrorResultCode(), message, e);
1346    }
1347  }
1348
1349
1350  /**
1351   * Returns the validity period to be used to generate the ADS certificate.
1352   * @return The validity period to be used to generate the ADS certificate.
1353   */
1354  private static int getADSCertificateValidity()
1355  {
1356    return 20 * 365;
1357  }
1358
1359  /**
1360   * Returns the Subject DN to be used to generate the ADS certificate.
1361   * @return The Subject DN to be used to generate the ADS certificate.
1362   * @throws java.net.UnknownHostException If the server host name could not be
1363   *                                       determined.
1364   */
1365  private static String getADSCertificateSubjectDN()
1366       throws UnknownHostException
1367  {
1368    String hostName =
1369      SetupUtils.getHostNameForCertificate(DirectoryServer.getServerRoot());
1370    return "cn=" + Rdn.escapeValue(hostName) + ",O=OpenDJ Certificate";
1371  }
1372
1373  /**
1374   * Create a randomly generated password for a certificate keystore.
1375   * @return A randomly generated password for a certificate keystore.
1376   */
1377  private static char[] createKeystorePassword() {
1378    int pwdLength = 50;
1379    char[] pwd = new char[pwdLength];
1380    Random random = new Random();
1381    for (int pos=0; pos < pwdLength; pos++) {
1382        int type = getRandomInt(random,3);
1383        char nextChar = getRandomChar(random,type);
1384        pwd[pos] = nextChar;
1385    }
1386    return pwd;
1387  }
1388
1389  private static char getRandomChar(Random random, int type)
1390  {
1391    char generatedChar;
1392    int next = random.nextInt();
1393    int d;
1394
1395    switch (type)
1396    {
1397    case 0:
1398      // Will return a digit
1399      d = next % 10;
1400      if (d < 0)
1401      {
1402        d = d * -1;
1403      }
1404      generatedChar = (char) (d+48);
1405      break;
1406    case 1:
1407      // Will return a lower case letter
1408      d = next % 26;
1409      if (d < 0)
1410      {
1411        d = d * -1;
1412      }
1413      generatedChar =  (char) (d + 97);
1414      break;
1415    default:
1416      // Will return a capital letter
1417      d = next % 26;
1418      if (d < 0)
1419      {
1420        d = d * -1;
1421      }
1422      generatedChar = (char) (d + 65) ;
1423    }
1424
1425    return generatedChar;
1426  }
1427
1428  private static int getRandomInt(Random random,int modulo)
1429  {
1430    return random.nextInt() & modulo;
1431  }
1432
1433  /**
1434   * Creates a PIN file on the specified path.
1435   * @param path the path where the PIN file will be created.
1436   * @param pin The PIN to store in the file.
1437   * @throws IOException if something goes wrong.
1438   */
1439  public static void createPINFile(String path, String pin)
1440       throws IOException
1441  {
1442    FileWriter file = new FileWriter(path);
1443    PrintWriter out = new PrintWriter(file);
1444
1445    out.println(pin);
1446
1447    out.flush();
1448    out.close();
1449
1450    try {
1451      if (!FilePermission.setPermissions(new File(path),
1452          new FilePermission(0600)))
1453      {
1454        // Log a warning that the permissions were not set.
1455        logger.warn(WARN_TRUSTSTORE_SET_PERMISSIONS_FAILED, path);
1456      }
1457    } catch(DirectoryException e) {
1458      // Log a warning that the permissions were not set.
1459      logger.warn(WARN_TRUSTSTORE_SET_PERMISSIONS_FAILED, path);
1460    }
1461  }
1462
1463  /**
1464   * Generates a self-signed certificate with well-known alias if there is none.
1465   * @throws InitializationException If an error occurs while interacting with
1466   *                                 the key store.
1467   */
1468  private void generateInstanceCertificateIfAbsent()
1469       throws InitializationException
1470  {
1471    String certAlias = ADS_CERTIFICATE_ALIAS;
1472
1473    try
1474    {
1475      if (certificateManager.aliasInUse(certAlias))
1476      {
1477        return;
1478      }
1479    }
1480    catch (Exception e)
1481    {
1482      LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_ADD_CERT.get(
1483           certAlias, trustStoreFile, getExceptionMessage(e));
1484      throw new InitializationException(message, e);
1485    }
1486
1487    try
1488    {
1489      certificateManager.generateSelfSignedCertificate(
1490           certAlias,
1491           getADSCertificateSubjectDN(),
1492           getADSCertificateValidity());
1493    }
1494    catch (Exception e)
1495    {
1496      LocalizableMessage message = ERR_TRUSTSTORE_CANNOT_GENERATE_CERT.get(
1497           certAlias, trustStoreFile, getExceptionMessage(e));
1498      throw new InitializationException(message, e);
1499    }
1500
1501  }
1502}
1503