/// <summary>
        /// Creates the certificate via a proxy instead of calling the CryptoAPI directly.
        /// </summary>
        /// <param name="executablePath">The executable path.</param>
        /// <param name="storePath">The store path.</param>
        /// <param name="password">The password used to protect the certificate.</param>
        /// <param name="applicationUri">The application URI.</param>
        /// <param name="applicationName">Name of the application.</param>
        /// <param name="subjectName">Name of the subject.</param>
        /// <param name="domainNames">The domain names.</param>
        /// <param name="keySize">Size of the key.</param>
        /// <param name="startTime">The start time.</param>
        /// <param name="lifetimeInMonths">The lifetime in months.</param>
        /// <param name="hashSizeInBits">The hash size in bits.</param>
        /// <param name="isCA">if set to <c>true</c> if creating a certificate authority.</param>
        /// <param name="usePEMFormat">if set to <c>true</c> the private ket is store in the PEM format.</param>
        /// <param name="issuerKeyFilePath">The path to the PFX file containing the CA private key.</param>
        /// <param name="issuerKeyFilePassword">The  password for the PFX file containing the CA private key.</param>
        /// <returns></returns>
        public static async Task<X509Certificate2Collection> CreateCertificateViaProxy(
            string executablePath,
            string storePath,
            string password,
            string applicationUri,
            string applicationName,
            string subjectName,
            IList<String> domainNames,
            ushort keySize,
            DateTime startTime,
            ushort lifetimeInMonths,
            ushort hashSizeInBits,
            bool isCA,
            bool usePEMFormat,
            string issuerKeyFilePath,
            string issuerKeyFilePassword)
        {
            // check if the proxy exists.
            FileInfo filePath = new FileInfo(executablePath);

            if (!filePath.Exists)
            {
                throw ServiceResultException.Create(StatusCodes.BadConfigurationError, "Cannnot find the Opc.Ua.CertificateGenerator utility: {0}", executablePath);
            }
            
            // expand any strings.
            storePath = Utils.GetAbsoluteDirectoryPath(storePath, true, false, true);

            if (storePath == null)
            {
                throw ServiceResultException.Create(StatusCodes.BadInvalidArgument, "Certificate store does not exist: {0}", storePath);
            }

            // set default values.
            SetSuitableDefaults(
                ref applicationUri,
                ref applicationName,
                ref subjectName,
                ref domainNames,
                ref keySize,
                ref lifetimeInMonths);

            // reconstruct name using slash as delimeter.
            subjectName = ChangeSubjectNameDelimiter(subjectName, '/');

            string tempFile = Path.GetTempFileName();

            try
            {
                StreamWriter writer = new StreamWriter(new FileStream(tempFile, FileMode.OpenOrCreate));

                writer.WriteLine("-cmd issue", storePath);

                if (!String.IsNullOrEmpty(storePath))
                {
                    writer.WriteLine("-storePath {0}", storePath);
                }

                if (!String.IsNullOrEmpty(applicationName))
                {
                    writer.WriteLine("-applicationName {0} ", applicationName);
                }

                if (!String.IsNullOrEmpty(subjectName))
                {
                    writer.WriteLine("-subjectName {0}", subjectName);
                }

                if (!String.IsNullOrEmpty(password))
                {
                    writer.WriteLine("-password {0}", password);
                }

                if (!isCA)
                {
                    if (!String.IsNullOrEmpty(applicationUri))
                    {
                        writer.WriteLine("-applicationUri {0}", applicationUri);
                    }

                    if (domainNames != null && domainNames.Count > 0)
                    {
                        StringBuilder buffer = new StringBuilder();

                        for (int ii = 0; ii < domainNames.Count; ii++)
                        {
                            if (buffer.Length > 0)
                            {
                                buffer.Append(",");
                            }

                            buffer.Append(domainNames[ii]);
                        }

                        writer.WriteLine("-domainNames {0}", buffer.ToString());
                    }
                }

                writer.WriteLine("-keySize {0}", keySize);

                if (startTime > DateTime.MinValue)
                {
                    writer.WriteLine("-startTime {0}", startTime.Ticks - new DateTime(1601, 1, 1).Ticks);
                }

                writer.WriteLine("-lifetimeInMonths {0}", lifetimeInMonths);
                writer.WriteLine("-hashSize {0}", hashSizeInBits);

                if (isCA)
                {
                    writer.WriteLine("-ca true");
                }

                if (usePEMFormat)
                {
                    writer.WriteLine("-pem true");
                }

                if (!String.IsNullOrEmpty(issuerKeyFilePath))
                {
                    writer.WriteLine("-issuerKeyFilePath {0}", issuerKeyFilePath);
                }

                if (!String.IsNullOrEmpty(issuerKeyFilePassword))
                {
                    writer.WriteLine("-issuerKeyPassword {0}", issuerKeyFilePassword);
                }

                writer.WriteLine("");
                writer.Flush();
                writer.Dispose();

                string result = null;
                string thumbprint = null;

                StreamReader reader = new StreamReader(new FileStream(tempFile, FileMode.Open));

                try
                {
                    while ((result = reader.ReadLine()) != null)
                    {
                        if (String.IsNullOrEmpty(result))
                        {
                            continue;
                        }

                        if (result.StartsWith("-cmd"))
                        {
                            throw new ServiceResultException("Input file was not processed properly.");
                        }

                        if (result.StartsWith("-thumbprint"))
                        {
                            thumbprint = result.Substring("-thumbprint".Length).Trim();
                            break;
                        }

                        if (result.StartsWith("-error"))
                        {
                            throw new ServiceResultException(result);
                        }
                    }
                }
                finally
                {
                    reader.Dispose();
                }

                // load the new certificate from the store.
                ICertificateStore store = new Opc.Ua.DirectoryCertificateStore();
                await store.Open(storePath);
                X509Certificate2Collection certificates = await store.FindByThumbprint(thumbprint);
                store.Close();

                return certificates;
            }
            catch (Exception e)
            {
                throw new ServiceResultException("Could not create a certificate via a proxy: " + e.Message, e);
            }
            finally
            {
                if (tempFile != null)
                {
                    try { File.Delete(tempFile); } catch {}
                }
            }
        }
        /// <summary>
        /// Creates a self signed application instance certificate.
        /// </summary>
        /// <param name="storeType">Type of certificate store (Windows or Directory) <see cref="CertificateStoreType"/>.</param>
        /// <param name="storePath">The store path (syntax depends on storeType).</param>
        /// <param name="password">The password to use to protect the certificate.</param>
        /// <param name="applicationUri">The application uri (created if not specified).</param>
        /// <param name="applicationName">Name of the application (optional if subjectName is specified).</param>
        /// <param name="subjectName">The subject used to create the certificate (optional if applicationName is specified).</param>
        /// <param name="domainNames">The domain names that can be used to access the server machine (defaults to local computer name if not specified).</param>
        /// <param name="keySize">Size of the key (1024, 2048 or 4096).</param>
        /// <param name="startTime">The start time.</param>
        /// <param name="lifetimeInMonths">The lifetime of the key in months.</param>
        /// <param name="hashSizeInBits">The hash size in bits.</param>
        /// <param name="isCA">if set to <c>true</c> the a CA certificate is created.</param>
        /// <param name="usePEMFormat">if set to <c>true</c> the private ket is store in the PEM format.</param>
        /// <param name="issuerKeyFilePath">The path to the PFX file containing the CA private key.</param>
        /// <param name="issuerKeyFilePassword">The  password for the PFX file containing the CA private key.</param>
        /// <returns>The certificate with a private key.</returns>
        public static async Task<X509Certificate2> CreateCertificate(
            string storeType,
            string storePath,
            string password,
            string applicationUri,
            string applicationName,
            string subjectName,
            IList<String> domainNames,
            ushort keySize,
            DateTime startTime,
            ushort lifetimeInMonths,
            ushort hashSizeInBits,
            bool isCA,
            bool usePEMFormat,
            string issuerKeyFilePath,
            string issuerKeyFilePassword)
        {
            // use the proxy if create a certificate in a directory store.
            if (storeType == CertificateStoreType.Directory)
            {
                string executablePath = GetCertificateGeneratorPath();

                if (String.IsNullOrEmpty(executablePath))
                {
                    throw ServiceResultException.Create(StatusCodes.BadConfigurationError, "The CertificateGenerator utility is not installed.");
                }

                X509Certificate2Collection certificates = await CreateCertificateViaProxy(
                    executablePath,
                    storePath,
                    password,
                    applicationUri,
                    applicationName,
                    subjectName,
                    domainNames,
                    keySize,
                    startTime,
                    lifetimeInMonths,
                    hashSizeInBits,
                    isCA,
                    usePEMFormat,
                    issuerKeyFilePath,
                    issuerKeyFilePassword);
                if (certificates.Count > 0)
                {
                    return certificates[0];
                }
            }

            if (isCA)
            {
                throw new NotSupportedException("Cannot create a CA certificate in a Windows store at this time.");
            }

            if (!String.IsNullOrEmpty(password))
            {
                throw new NotSupportedException("Cannot add password when creating a certificate in a Windows store at this time.");
            }

            if (!String.IsNullOrEmpty(issuerKeyFilePath))
            {
                throw new NotSupportedException("Cannot use a CA certificate to create a certificate in a Windows store at this time.");
            }

            // set default values.
            SetSuitableDefaults(
                ref applicationUri,
                ref applicationName,
                ref subjectName,
                ref domainNames,
                ref keySize,
                ref lifetimeInMonths);

            // create the certificate.
            X509Certificate2 certificate = await CreateCertificate(
                applicationUri,
                applicationName,
                subjectName.ToString(),
                domainNames,
                keySize,
                lifetimeInMonths);
            
            // add it to the store.
            if (!String.IsNullOrEmpty(storePath))
            {
                ICertificateStore store = null;

                if (storeType == CertificateStoreType.Windows)
                {
                    store = new Opc.Ua.WindowsCertificateStore();
                }
                else
                {
                    store = new Opc.Ua.DirectoryCertificateStore();
                }

                await store.Open(storePath);
                await store.Add(certificate);
            }

            return certificate;
        }