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