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